Использование файловых таблиц с Entity Framework, а также каскадные функции - PullRequest
0 голосов
/ 27 марта 2020

SQL Сервер FileTable был впервые представлен в 2012 году, однако он не поддерживается Entity Framework (либо. NET Core, либо полный. NET Framework). Использование SQL Сервер FileTable или FileStream позволяет ускорить загрузку и выгрузку файлов.

Я хочу использовать FileTable с моим. NET Базовым приложением, где я должен создать отношения этого * 1007. * с другой простой таблицей.

Для этого мне нужно иметь тип hierarchyid в C#, а также включить поддержку файлового потока на SQL уровне экземпляра сервера. Похоже, что Entity Framework не предоставляет поддержку для создания базы данных с поддержкой FileTable при создании базы данных во время обновления миграции.

1 Ответ

0 голосов
/ 27 марта 2020

Ранее я задавал вопрос и не нашел ответа, когда хотел использовать SQL Таблицы файлов с моим. Net Базовым приложением, использующим код вначале. Хорошо, теперь, когда я решил проблему, я хотел бы поделиться этим. Я не буду вдаваться в подробности, однако это подробно описано на веб-сайте Microsoft.

Прежде всего, вам необходимо включить поддержку File Stream на SQL уровне экземпляра сервера. Щелкните правой кнопкой мыши службу MSSQLSERVER в SQL Диспетчер конфигурации сервера (мой SQL Имя экземпляра сервера было MSSQLSERVER) и выберите свойства. На вкладке FileStream включите первые два флажка («Включить файловый поток для доступа transact- SQL и Включить файловый поток для доступа к файлу ввода-вывода»). Затем перезапустите службу.

В SQL Server Management Studio (SSMS) щелкните правой кнопкой мыши верхний узел (в основном по имени вашего компьютера) и выберите Свойства. Go на вкладку «Дополнительно» и измените уровень доступа FILESTREAM по своему желанию (будь то транзакционный - SQL или полный доступ).

После этого вы должны создать базу данных следующим запросом:

CREATE DATABASE xyz
ON PRIMARY 
(NAME = FS,
    FILENAME = 'D:\Database\xyzDB.mdf'),
FILEGROUP FileStreamFS CONTAINS FILESTREAM(NAME = FStream, 
    FILENAME = 'D:\Database\xyzFs')
LOG ON 
(NAME = FILESDBLog,   
    FILENAME = 'D:\Database\xyzDBLog.ldf')
WITH FILESTREAM (NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = N'xyz')
GO

Здесь D:\Database - это мой каталог, в котором я храню файлы базы данных, включая файлы, которые будут храниться в SQL File Table. xyz - имя суффикса базы данных DB.mdf, Fs & DBLog.ldf и префикс N после этого. Я включил NON_TRANSACTED_ACCESS; но вы можете отключить, если вы не хотите такой доступ. Обратите внимание, что каталог должен существовать до запуска этого запроса.

Теперь, когда вы создали базу данных, вы можете go вперед и запустить миграцию из вашего. Net Приложения с тем же именем базы данных в вашем Соединении Строка.

Вам также понадобятся SQL функции для поддержки ваших операций, я создал это, используя MigrationBuilder класс:

migrationBuilder.Sql("CREATE FUNCTION dbo.HierarchyIdToString (@Id hierarchyid) RETURNS varchar(max) with schemabinding AS BEGIN RETURN CONVERT(varchar(max),CONVERT(varbinary(max),@Id,1),1); END");

И

migrationBuilder.SQL("CREATE FUNCTION StringToHierarchyId (@Id varchar(max)) "+
"RETURNS hierarchyid WITH SCHEMABINDING AS "+
"BEGIN "+
"RETURN CONVERT(hierarchyid,CONVERT(VARBINARY(MAX),@Id,1),1) "+
"END");

Позже я буду использовать эту функцию и объясню ее роль в дальнейшем.

Теперь создайте таблицу файлов, в которой будут храниться ваши файлы. Конечно, вы можете создать столько файловых таблиц, сколько вам нужно для файлов разных типов.

migrationBuilder.Sql("CREATE TABLE DocumentStore AS FILETABLE WITH (FileTable_Directory = 'DocumentStore', FileTable_Collate_Filename = database_default);");

Моя таблица файлов называется DocumentStore.

Вам также потребуется хранимая процедура для получите путь к таблице файлов Root (где хранятся файлы), чтобы получить доступ к этим файлам на уровне файловой системы, на случай, если вы хотите получить доступ к файлам нетранзакционными способами. Ниже приведен код:

migrationBuilder.Sql("CREATE PROCEDURE GetFileTableRootPath @TableName VARCHAR(100), @Path VARCHAR(1000) OUTPUT AS BEGIN SET @Path = (SELECT FILETABLEROOTPATH(@TableName)) END");

Обратите внимание, что FILETABLEROOTPATH('TableName') - это встроенная функция, которую я использовал в хранимой процедуре. Я знал, как вызывать хранимые процедуры в. Net, поэтому я просто обернул функцию в хранимую процедуру.

Я создал другую хранимую процедуру, чтобы получить Path_Locator любого файла, хранящегося в таблице файлов. Path_Locator - это первичный ключ таблицы файлов, который позже потребуется ввести в качестве ссылки на этот файл в другой таблице. Код:

migrationBuilder.Sql("CREATE PROCEDURE GetFileTableRootPath @TableName VARCHAR(100), @Path VARCHAR(1000) OUTPUT AS BEGIN SET @Path = (SELECT FILETABLEROOTPATH(@TableName)) END");

Я также создал таблицу с использованием простого. Net Класс модели с именем Documents.cs, включая обычные атрибуты (повторяются, хотя они также доступны в таблице файлов), включая атрибут для справочный файл в таблице файлов. Поскольку File Table имеет PK с именем Path_Locator с типом HIERARCHYID, я создал поле varchar(max) в таблице Documents и сохраню Path_Locator в этом столбце после преобразования в SQL VARCHAR из SQL HIERARCHYID тип данных. Эта таблица является частью классов моего домена и будет иметь отношения, как обычно у таблицы.

Теперь, когда я создал вспомогательные таблицы, мне также нужно реализовать функциональность CASCADE DELETE, которую я могу сделать, используя SQL Триггеры. Первый триггер для таблицы файлов выглядит следующим образом:

migrationBuilder.Sql(
                "CREATE TRIGGER dbo.CascadeDelete ON DocumentStore "+
                "AFTER DELETE NOT FOR REPLICATION " +
                "AS "+
                "BEGIN "+
                "SET NOCOUNT ON "+
                "DECLARE @Id varchar(max); "+
                "DECLARE @Table Table (MyHierarchy hierarchyid); "+
                "INSERT INTO @Table SELECT deleted.path_locator from deleted; "+
                "WHILE ((SELECT COUNT(*) FROM @Table) > 0) "+
                "BEGIN "+
                "select @Id = dbo.HierarchyIdToString((SELECT TOP 1 * from @Table)); "+
                "DELETE FROM Documents WHERE HierarchyInString = @Id; "+
                "DELETE FROM @Table where MyHierarchy = dbo.StringToHierarchyId(@Id); "+
                "END END"
            );

И второй триггер будет работать с таблицей, используя миграции с именем Documents, чтобы отразить удаление файлов в таблице файлов (синхронизация базы данных):

migrationBuilder.Sql(
                "CREATE TRIGGER dbo.CascadeDeleteDocuments ON Documents AFTER DELETE NOT FOR REPLICATION AS BEGIN SET NOCOUNT ON DECLARE @Id hierarchyid; DECLARE @Table Table (MyHierarchyInString varchar(max)); INSERT INTO @Table SELECT deleted.HierarchyInString from deleted; WHILE ((SELECT COUNT(*) FROM @Table) > 0) BEGIN select @Id = dbo.StringToHierarchyId((SELECT TOP 1 * from @Table)); DELETE FROM DocumentStore WHERE path_locator = @Id; DELETE FROM @Table where MyHierarchyInString = dbo.HierarchyIdToString(@Id); END END");

Чтобы эти триггеры работали, мне нужно было преобразовать HIERARCHYID с VARCHAR(MAX) и наоборот, для чего я использовал SQL Скалярные функции для преобразования туда и обратно.

Теперь, чтобы вставить файлы, я сохраняю в windows файловую систему в месте, извлеченном из хранимой процедуры GETPATHLOCATOR, и файл автоматически сохраняется в моей таблице файлов. Одновременно я также добавляю запись в свою другую таблицу, созданную из C# класса модели, т.е. Documents, поддерживающего обе таблицы.

В будущем я попытаюсь создать базу данных с поддержкой File Stream, включить поддержку File Stream на SQL Уровень экземпляра сервера с использованием некоторого кода из приложения, чтобы избежать этого из процесса кода.

Спасибо.

...