Использование LINQ для вставки данных в таблицу, которая использует последовательность в качестве генератора первичного ключа - PullRequest
0 голосов
/ 31 января 2019

У меня есть таблица, которая генерирует свой первичный ключ из последовательности (которая просто считает от 0):

CREATE TABLE [dbo].[testTable](
    [id] [int] NOT NULL,
    [a] [int] NOT NULL,
 CONSTRAINT [PK_testTable] PRIMARY KEY CLUSTERED ([id] ASC))

ALTER TABLE [dbo].[tblTestTable] ADD  CONSTRAINT [DF_tblTestTable_id]  DEFAULT (NEXT VALUE FOR [seq_PK_tblTestTable]) FOR [id]

Я использовал O / R Designer в Visual Studio для создания файлов сопоставления дляТаблица;поле id определяется как:

[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id", DbType="Int NOT NULL", IsPrimaryKey=true)]
public int id {…}

, и теперь я пытаюсь вставить данные через LINQ.

var testTableRecord = new testTable()
{
    a = 1,
};
db.Connection.Open();
db.testTables.InsertOnSubmit(testTableRecord);
db.SubmitChanges();
Console.WriteLine($"id: {testTableRecord.id}");

Проблема, с которой я сталкиваюсь, заключается в том, что LINQ кажетсяНевозможно обработать генерацию идентификатора через последовательность, поскольку при вставке неявно устанавливается id в значение.

  • Когда я устанавливаю id в значение CanBeNull, вставка завершается неудачно, поскольку она пытается вставитьNULL в ненулевое поле.
  • Когда я устанавливаю id в IsDbGenerated, вставка работает, но ожидает поле IDENTITY и пытается загрузить сгенерированный идентификатор с SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]',N'@p0 int',@p0=1, и затемустанавливает id в объекте на ноль, потому что SCOPE_IDENTITY() возвращает null
  • Я думал о том, чтобы просто использовать IsDbGenerated, уничтожить объект LINQ и запросить базу данных для id, но у меня нет ничего уникального для поиска.

К сожалению, изменение механизма создания идентификатора на IDENTITY не вариант.

Нужно ли явно запрашиватьDB для следующего значения последовательности и установить идентификатор вручную?

Каков наилучший способ обработки этих вставок через LINQ?

PS: мне нужен идентификатор, потому что мне нужно вставить больше данных, использующих идентификатор как FK.

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Ваша единственная надежда - довольно глубокий рефакторинг и использование хранимой процедуры для вставки записей .Хранимая процедура может быть сопоставлена ​​с методом Insert класса в конструкторе контекста данных.

Используя определение таблицы, хранимой является ничто иное, как это:

CREATE PROCEDURE InsertTestTable
(
    @id int OUTPUT,
    @a AS int
)
AS
BEGIN
    INSERT dbo.testTable (a) VALUES (@a);

    SET @id = (SELECT CONVERT(int, current_value) 
        FROM sys.sequences WHERE name = 'seq_PK_tblTestTable')
END

Вы можете импортировать этохранимой процедуры в контекст путем перетаскивания ее из Sql Object Explorer на поверхность конструктора, которая будет выглядеть следующим образом:

dbml designer

Следующий шагщелкните класс testTable и нажмите кнопку с многоточием для метода Вставка (который был включен путем добавления хранимой процедуры в контекст):

Set Insert method

И настройте его следующим образом:

Set Insert method dialog

Вот и все.Теперь LINQ-to-SQL сгенерирует вызов хранимой процедуры для вставки записи, например:

declare @p3 int
set @p3=8

declare @p5 int
set @p5=0
exec sp_executesql N'EXEC @RETURN_VALUE = [dbo].[InsertTestTable] @id = @p0 OUTPUT, 
    @a = @p1',N'@p0 int output,@p1 int,@RETURN_VALUE int output',
    @p0=@p3 output,@p1=123,@RETURN_VALUE=@p5 output
select @p3, @p5

Конечно, вам, возможно, придется задуматься, как долго вы собираетесь держаться за LINQ-to-SQL.Entity Framework Core имеет поддержку последовательности из коробки .И многое другое.

0 голосов
/ 31 января 2019

Глядя на решения с точки зрения исходного SQL:

1.

INSERT INTO [dbo].[testTable] VALUES (NEXT VALUE FOR [dbo].[seq_PK_tblTestTable], 1)

Насколько я могу сказать, просто невозможно сделать в LINQ to SQL

2.

INSERT INTO [dbo].[testTable] (a) VALUES (1)

Этого можно достичь в LINQ to SQL, исключив свойство id из сущности testTable.

Если вам нужно извлечь идентификаторы из таблицы, вы можете создать отдельные сущности для вставки и запроса:

public class testTableInsert {
    [ColumnAttribute(...)]
    public int a
}

public class testTableResult {
    [ColumnAttribute(...)]
    public int id

    [ColumnAttribute(...)]
    public int a
}

3.

DECLARE @nextId INT;
SELECT @nextId = NEXT VALUE FOR [dbo].[seq_PK_tblTestTable];  
INSERT INTO [dbo].[testTable] VALUES (@nextId, 1)

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

Вот пример кода, демонстрирующийкак расширить сгенерированный DataContext, используя частичные методы.

public partial class MyDataContext : System.Data.Linq.DataContext
{
    partial void InsertTestTable(TestTable instance)
    {

        using (var cmd = Connection.CreateCommand()) 
        {
            cmd.CommandText = "SELECT NEXT VALUE FOR [dbo].[seq_PK_TestTable] as NextId";
            cmd.Transaction = Transaction;
            int nextId = (int) cmd.ExecuteScalar();
            instance.id = nextId;

            ExecuteDynamicInsert(instance);
        }         
    }
}

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

TestTable entity = new TestTable { a = 2 };
dataContext.TestTables.InsertOnSubmit(entity);
dataContext.SubmitChanges();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...