Как я могу заставить структуру сущностей вставлять столбцы идентификаторов?

Я хочу написать некоторый код C# для инициализации моей базы данных с некоторыми начальными данными. Ясно, что для этого потребуется способность устанавливать значения различных столбцов Identity при вставке. Я использую код-первый подход. По умолчанию, DbContext обрабатывает соединение с базой данных, и поэтому вы не можете SET IDENTITY_INSERT [dbo].[MyTable] ON, Итак, я до сих пор использовал DbContext конструктор, который позволяет мне указать соединение с БД, которое будет использоваться. Затем я установил IDENTITY_INSERT в ON в этом соединении с БД, а затем попробуйте вставить мои записи, используя сущность Framework. Вот пример того, что я получил до сих пор:

public class MyUserSeeder : IEntitySeeder {
    public void InitializeEntities(AssessmentSystemContext context, SqlConnection connection) {
        context.MyUsers.Add(new MyUser { MyUserId = 106, ConceptPersonId = 520476, Salutation = "Mrs", Firstname = "Novelette", Surname = "Aldred", Email = null, LoginId = "520476", Password="28c923d21b68fdf129b46de949b9f7e0d03f6ced8e9404066f4f3a75e115147489c9f68195c2128e320ca9018cd711df", IsEnabled = true, SpecialRequirements = null });
        try {
            connection.Open();
            SqlCommand cmd = new SqlCommand("SET IDENTITY_INSERT [dbo].[MyUser] ON", connection);
            int retVal = cmd.ExecuteNonQuery();
            context.SaveChanges();
        }
        finally {
            connection.Close();
        }
    }
}

Так близко и еще так далеко - потому что, хотя cmd.ExecuteNonQuery() работает нормально, когда я бегу context.SaveChanges()Мне сообщили, что "для столбца идентификаторов в таблице" MyUser "должно быть указано явное значение, когда для IDENTITY_INSERT задано значение ON или когда пользователь репликации вставляет в столбец идентификаторов NOT FOR REPLICATION".

Предположительно, поскольку MyUserId (который является столбцом Identity в таблице MyUser) является первичным ключом, структура сущностей не пытается установить его при вызове context.SaveChanges()хотя я дал MyUser сущность значение для MyUserId имущество.

Есть ли способ заставить инфраструктуру сущностей попытаться вставить даже значения первичного ключа для сущности? Или, возможно, способ временно пометить MyUserId как не являющийся значением первичного ключа, поэтому EF пытается вставить его?

14 ответов

Решение

После тщательного рассмотрения я решил, что отказ от структуры сущностей вставлять столбцы идентификаторов - это функция, а не ошибка.:-) Если бы я вставлял все записи в моей базе данных, включая их значения идентификаторов, мне также пришлось бы создавать сущность для каждой таблицы ссылок, которую структура сущностей создала для меня автоматически! Это просто не правильный подход.

Итак, я делаю настройку классов заполнения, которые просто используют код C# и создают объекты EF, а затем используют DbContext сохранить вновь созданные данные. Требуется немного больше времени, чтобы взять дамп SQL и превратить его в код C#, но данных не слишком много (и не должно быть) только для "заполнения" данных - это должен быть небольшой объем данных, который является репрезентативным о типе данных, которые были бы в живой БД, которые могут быть быстро помещены в новую БД для целей отладки / разработки. Это означает, что если я хочу связать сущности вместе, мне нужно выполнить запросы на то, что уже было вставлено, или мой код не будет знать их сгенерированное значение идентичности, например. Такие вещи появятся в начальном коде после того, как я настрою и сделаю context.SaveChanges за MyRoles:

var roleBasic = context.MyRoles.Where(rl => rl.Name == "Basic").First();
var roleAdmin = context.MyRoles.Where(rl => rl.Name == "Admin").First();
var roleContentAuthor = context.MyRoles.Where(rl => rl.Name == "ContentAuthor").First();

MyUser thisUser = context.MyUsers.Add(new MyUser {
    Salutation = "Mrs", Firstname = "Novelette", Surname = "Aldred", Email = null, LoginUsername = "naldred", Password="c1c966821b68fdf129c46de949b9f7e0d03f6cad8ea404066f4f3a75e11514748ac9f68695c2128e520ca0275cd711df", IsEnabled = true, SpecialRequirements = null
});
thisUser.Roles.Add(roleBasic);

Это также повышает вероятность того, что я обновлю свои начальные данные при изменении схемы, потому что, скорее всего, я нарушу исходный код при его изменении (если я удаляю поле или сущность, существующий исходный код, использующий это поле) / сущность не сможет скомпилироваться). С SQL-скриптом для выполнения заполнения это было бы не так, и при этом SQL-скрипт не был бы независимым от базы данных.

Поэтому я думаю, что если вы пытаетесь установить поля идентичности сущностей для данных заполнения БД, вы определенно выбрали неверный подход.

Если бы я на самом деле перетаскивал загрузку данных, скажем, с SQL Server, на PostgreSQL (полностью живая БД, а не просто некоторые начальные данные), я мог бы сделать это через EF, но я бы хотел, чтобы два контекста были открыты одновременно, и написать некоторый код, чтобы получить все различные объекты из исходного контекста и поместить их в целевой контекст, а затем сохранить изменения.

Как правило, единственное время, когда уместно вставлять значения идентификаторов, - это когда вы копируете из одной БД в другую БД в пределах одной и той же СУБД (SQL Server -> SQL Server, PostgreSQL -> PostgreSQL и т. Д.), А затем выполняете это в SQL-скрипте, а не в EF-коде вначале (SQL-скрипт не будет независимым от DB, но это не обязательно; вы не переходите между разными СУБД).

МетодEF 6, используя статью MSDN:

    using (var dataContext = new DataModelContainer())
    using (var transaction = dataContext.Database.BeginTransaction())
    {
      var user = new User()
      {
        ID = id,
        Name = "John"
      };

      dataContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] ON");

      dataContext.User.Add(user);
      dataContext.SaveChanges();

      dataContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] OFF");

      transaction.Commit();
    }

Обновление: во избежание ошибки "Для столбца идентификаторов в таблице" TableName "должно быть указано явное значение, когда для IDENTITY_INSERT задано значение ON или когда пользователь репликации вставляет в столбец идентификаторов NOT FOR REPLICATION", необходимо изменить значение свойства StoreGeneratedPattern столбец идентичности от Идентичности до Нет в конструкторе моделей.

Обратите внимание, что изменение параметра StoreGeneratedPattern на None приведет к невозможности вставки объекта без указанного идентификатора (обычным способом) с ошибкой "Невозможно вставить явное значение для столбца идентификаторов в таблице" TableName ", когда для IDENTITY_INSERT задано значение OFF".

Вам не нужно заниматься смешными делами со связью, вы можете отключить посредника и просто использовать ObjectContext.ExecuteStoreCommand,

Затем вы можете достичь того, что вы хотите, сделав это:

context.ExecuteStoreCommand("SET IDENTITY_INSERT [dbo].[MyUser] ON");

Я не знаю ни одного встроенного способа сказать EF, чтобы установить идентификационную вставку.

Это не идеально, но это будет более гибким и менее "хакерским", чем ваш нынешний подход.

Обновить:

Я только что понял, что есть вторая часть вашей проблемы. Теперь, когда вы сказали SQL, что хотите выполнять вставку идентификатора, EF даже не пытается вставить значения для указанного идентификатора (с чего бы это? Мы этого не говорили).

У меня нет опыта работы с первым подходом к коду, но из некоторых быстрых поисков кажется, что вам нужно сказать EF, что ваш столбец не должен генерироваться из магазина. Вам нужно будет сделать что-то вроде этого.

Property(obj => obj.MyUserId)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)
    .HasColumnName("MyUserId");

Надеюсь, это поможет вам в правильном направлении:-)

Немного опоздал на вечеринку, но в случае, если кто-то сначала столкнется с этой проблемой в EF5 с БД: я не мог заставить работать ни одно из решений, но нашел другой обходной путь:

Перед запуском .SaveChanges() Команда сбрасываю счетчик идентификаторов таблицы:

Entities.Database.ExecuteSqlCommand(String.Format("DBCC CHECKIDENT ([TableNameHere], RESEED, {0})", newObject.Id-1););
Entities.YourTable.Add(newObject);
Entities.SaveChanges();

Это означает, что .SaveChanges() необходимо применять после каждого добавления - но по крайней мере это работает!

Вот решение проблемы. Я попробовал это на EF6, и это сработало для меня. Ниже приведен псевдокод, который должен работать.

Прежде всего вам нужно создать перегрузку dbcontext по умолчанию. Если вы проверите базовый класс, вы найдете тот, который передает существующий dbConnection. Проверьте следующий код

public MyDbContext(DbConnection existingConnection, bool contextOwnsConnection)
        : base(existingConnection, contextOwnsConnection = true)
    {
        //optional
        this.Configuration.ProxyCreationEnabled = true;
        this.Configuration.LazyLoadingEnabled = true;
        this.Database.CommandTimeout = 360;
    }

А в разделе "Создание модели" удалите сгенерированную опцию "db",

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyTable>()
            .Property(a => a.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        base.OnModelCreating(modelBuilder);
    }

Теперь в коде вам нужно явно передать объект подключения,

using (var connection = new System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionStringName"].ConnectionString))
        {
            connection.Open();
            using (var context = new MyDbContext(connection, true))
            {
                context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[MyTable] ON");
                context.MyTable.AddRange(objectList);
                context.SaveChanges();
                context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[MyTable] OFF");
            }

            connection.Close();
        }

Эта идея работает надежно, только если целевая таблица пуста или записи вставляются с идентификаторами, превышающими все уже существующие идентификаторы в таблице!

Прошло 3 года, и я столкнулся с аналогичной проблемой переноса производственных данных в тестовую систему. Пользователи хотели иметь возможность копировать производственные данные в тестовую систему в любое время, поэтому вместо настройки задания переноса в SQL Server я искал способ выполнить перенос в приложении с использованием существующих классов EF. Таким образом, я мог предоставить пользователям пункт меню, чтобы начать передачу, когда они захотят.

Приложение использует базу данных MS SQL Server 2008 и EF 6. Поскольку две базы данных, как правило, имеют одинаковую структуру, я подумал, что мог бы легко переносить данные из одного экземпляра DbContext в другой, читая записи каждой сущности, используя AsNoTracking() и просто Add() (или же AddRange()) записи в соответствующее свойство в целевом экземпляре DbContext.

Вот DbContext с одной сущностью для иллюстрации:

public class MyDataContext: DbContext
{
    public virtual DbSet<Person> People { get; set; }
}

Чтобы скопировать данные людей, я сделал следующее:

private void CopyPeople()
{
    var records = _sourceContext.People.AsNoTracking().ToArray();
    _targetContext.People.AddRange(records);
    _targetContext.SaveChanges();
}

Пока таблицы были скопированы в правильном порядке (чтобы избежать проблем с ограничениями внешнего ключа), это работало очень хорошо. К сожалению, таблицы, использующие столбцы идентификаторов, усложняют задачу, поскольку EF игнорирует значения идентификаторов и просто позволяет SQL Server вставлять следующее значение идентификаторов. Для таблиц со столбцами идентификаторов я в итоге сделал следующее:

  1. Прочитать все записи данного лица
  2. Упорядочить записи по идентификатору в порядке возрастания
  3. установите для идентификатора таблицы значение первого идентификатора
  4. отслеживая следующее значение идентичности, добавляйте записи одну за другой. Если идентификатор не совпадает с ожидаемым следующим значением идентификатора, установите для идентификатора следующего требуемого значения

Пока таблица пуста (или все новые записи имеют идентификаторы выше, чем текущий его самый длинный идентификатор), а идентификаторы расположены в порядке возрастания, EF и MS SQL будут вставлять необходимые идентификаторы, и ни одна из систем не будет жаловаться.

Вот немного кода для иллюстрации:

private void InsertRecords(Person[] people)
{
    // setup expected id - presumption: empty table therefore 1
    int expectedId = 1;

    // now add all people in order of ascending id
    foreach(var person in people.OrderBy(p => p.PersonId))
    {
        // if the current person doesn't have the expected next id
        // we need to reseed the identity column of the table
        if (person.PersonId != expectedId)
        {
            // we need to save changes before changing the seed value
            _targetContext.SaveChanges();

            // change identity seed: set to one less than id
            //(SQL Server increments current value and inserts that)
            _targetContext.Database.ExecuteSqlCommand(
                String.Format("DBCC CHECKIDENT([Person], RESEED, {0}", person.PersonId - 1)
            );

            // update the expected id to the new value
            expectedId = person.PersonId;
        }

        // now add the person
        _targetContext.People.Add(person);

        // bump up the expectedId to the next value
        // Assumption: increment interval is 1
        expectedId++;
    }

    // now save any pending changes
    _targetContext.SaveChanges();
}

Используя отражение, я смог написать Load и Save метод, который работал для всех объектов в DbContext.

Это что-то вроде хака, но это позволяет мне использовать стандартные методы EF для чтения и записи сущностей и преодолевает проблему того, как установить столбцы идентификаторов в конкретные значения при определенных обстоятельствах.

Я надеюсь, что это поможет кому-то другому, столкнувшемуся с подобной проблемой.

ef core 6 или более ранняя версия

      await _dbCtx.Database.ExecuteSqlRawAsync($"SET IDENTITY_INSERT [table_name] ON");

_dbCtx.Set<TableName>().Add(new TableName { Id = 1, Name = "qwe"});

await _dbCtx.SaveChangesAsync()

await _dbCtx.Database.ExecuteSqlRawAsync($"SET IDENTITY_INSERT [table_name] OFF");

эф ядро ​​7 +

вам нужно обернуть его в транзакцию. (потому что в Ef 7 добавлено «SET IMPLICIT_TRANSACTIONS OFF» для улучшения производительности, что будет мешать предыдущей команде)

      using var transaction = _dbCtx.Database.BeginTransaction();

await _dbCtx.Database.ExecuteSqlRawAsync($"SET IDENTITY_INSERT [table_name] ON");

_dbCtx.Set<TableName>().Add(new TableName { Id = 1, Name = "qwe"});

await _dbCtx.SaveChangesAsync()

await _dbCtx.Database.ExecuteSqlRawAsync($"SET IDENTITY_INSERT [table_name] OFF");

await transaction.CommitAsync();

Я проделал эту работу, создав унаследованный контекст:

Мой обычный контекст с миграциями EF:

public class MyContext : DbContext
{
    public MyContext() : base("name=MyConnexionString")
    {...}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // best way to know the table names from classes... 
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        ...
    }
}

Мой альтернативный контекст использовался для переопределения личности. Не регистрируйте этот контекст для миграций EF (я использую его для передачи данных из другой базы данных):

public class MyContextForTransfers : MyContext
{
    public MyContextForTransfers() : base()
    {
        // Basically tells the context to take the database as it is...
        Database.SetInitializer<MyContextForTransfers >(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
         // Tells the Context to include Isd in inserts
         modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();
         base.OnModelCreating(modelBuilder);
    }
}

Как вставить (управление ошибками сильно упрощено...):

public void Insert<D>(iEnumerable<D> items)
{
    using (var destinationDb = new MyContextForTransfers())
    {
        using (var transaction = destinationDb.Database.BeginTransaction())
        {
            try
            {
                destinationDb.Database.ExecuteSqlCommand($"SET IDENTITY_INSERT [dbo].[{typeof(D).Name}] ON");
                destinationDb.Set<D>().AddRange(items);
                destinationDb.SaveChanges();
                destinationDb.Database.ExecuteSqlCommand($"SET IDENTITY_INSERT [dbo].[{typeof(D).Name}] OFF");
                transaction.Commit();
             }
             catch
             {
                transaction.Rollback();
             }
         }
    }
}

Проверка миграции перед любой транзакцией может быть хорошей идеей с "обычным" контекстом и конфигурацией:

Есть ли способ заставить каркас сущности попытаться вставить даже значения первичного ключа для сущности?

Да, но не так чисто, как хотелось бы видеть.

Предполагая, что вы используете автоматически сгенерированный ключ идентификации, EF полностью проигнорирует вашу попытку сохранить значение ключа. Похоже, что это "По замыслу" по многим веским причинам, подробно описанным выше, но все еще бывают моменты, когда вы хотите полностью контролировать свои начальные данные (или начальную нагрузку). Я предлагаю EF учесть этот вид посева в следующей версии. Но пока они не сделают, просто напишите небольшой код, который работает в рамках и автоматизирует беспорядочные детали.

Eventho VendorID игнорируется EF, вы можете использовать его с базовыми циклами и счетами, чтобы определить, сколько записей-заполнителей добавить между вашими живыми записями. Заполнителям присваивается следующий доступный идентификационный номер, когда они добавляются. Как только ваши записи будут иметь запрошенные идентификаторы, вам просто нужно удалить ненужную информацию.

public class NewsprintInitializer: DropCreateDatabaseIfModelChanges<NewsprintContext>
{
    protected override void Seed(NewsprintContext context)
    {
        var vendorSeed = new List<Vendor>
        {
            new Vendor { VendorID = 1, Name = "#1 Papier Masson / James McClaren" },
            new Vendor { VendorID = 5, Name = "#5 Abitibi-Price" },
            new Vendor { VendorID = 6, Name = "#6 Kruger Inc." },
            new Vendor { VendorID = 8, Name = "#8 Tembec" }
        };

        //  Add desired records AND Junk records for gaps in the IDs, because .VendorID is ignored on .Add
        int idx = 1;
        foreach (Vendor currentVendor in vendorSeed)
        {
            while (idx < currentVendor.VendorID)
            {
                context.Vendors.Add(new Vendor { Name = "**Junk**" });
                context.SaveChanges();
                idx++;
            }
            context.Vendors.Add(currentVendor);
            context.SaveChanges();
            idx++;
        }
        //  Cleanup (Query/Find and Remove/delete) the Junk records
        foreach (Vendor del in context.Vendors.Where(v => v.Name == "**Junk**"))
        {
            context.Vendors.Remove(del);
        }
        context.SaveChanges();

        // setup for other classes

    }
}

Это работало, как и ожидалось, за исключением того, что мне приходилось часто делать "SaveChanges", чтобы идентификаторы оставались в порядке.

После экспериментов с несколькими вариантами, найденными на этом сайте, у меня сработал следующий код (EF 6). Обратите внимание, что сначала он пытается выполнить нормальное обновление, если элемент уже существует. Если это не так, то пытается выполнить обычную вставку, если ошибка происходит из-за IDENTITY_INSERT, затем пытается обойти эту проблему. Также обратите внимание, что db.SaveChanges потерпит неудачу, следовательно, оператор db.Database.Connection.Open() и необязательный шаг проверки. Помните, что это не обновляет контекст, но в моем случае это не обязательно. Надеюсь это поможет!

public static bool UpdateLeadTime(int ltId, int ltDays)
{
    try
    {
        using (var db = new LeadTimeContext())
        {
            var result = db.LeadTimes.SingleOrDefault(l => l.LeadTimeId == ltId);

            if (result != null)
            {
                result.LeadTimeDays = ltDays;
                db.SaveChanges();
                logger.Info("Updated ltId: {0} with ltDays: {1}.", ltId, ltDays);
            }
            else
            {
                LeadTime leadtime = new LeadTime();
                leadtime.LeadTimeId = ltId;
                leadtime.LeadTimeDays = ltDays;

                try
                {
                    db.LeadTimes.Add(leadtime);
                    db.SaveChanges();
                    logger.Info("Inserted ltId: {0} with ltDays: {1}.", ltId, ltDays);
                }
                catch (Exception ex)
                {
                    logger.Warn("Error captured in UpdateLeadTime({0},{1}) was caught: {2}.", ltId, ltDays, ex.Message);
                    logger.Warn("Inner exception message: {0}", ex.InnerException.InnerException.Message);
                    if (ex.InnerException.InnerException.Message.Contains("IDENTITY_INSERT"))
                    {
                        logger.Warn("Attempting workaround...");
                        try
                        {
                            db.Database.Connection.Open();  // required to update database without db.SaveChanges()
                            db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[LeadTime] ON");
                            db.Database.ExecuteSqlCommand(
                                String.Format("INSERT INTO[dbo].[LeadTime]([LeadTimeId],[LeadTimeDays]) VALUES({0},{1})", ltId, ltDays)
                                );
                            db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[LeadTime] OFF");
                            logger.Info("Inserted ltId: {0} with ltDays: {1}.", ltId, ltDays);
                            // No need to save changes, the database has been updated.
                            //db.SaveChanges(); <-- causes error

                        }
                        catch (Exception ex1)
                        {
                            logger.Warn("Error captured in UpdateLeadTime({0},{1}) was caught: {2}.", ltId, ltDays, ex1.Message);
                            logger.Warn("Inner exception message: {0}", ex1.InnerException.InnerException.Message);
                        }
                        finally
                        {
                            db.Database.Connection.Close();
                            //Verification
                            if (ReadLeadTime(ltId) == ltDays)
                            {
                                logger.Info("Insertion verified. Workaround succeeded.");
                            }
                            else
                            {
                                logger.Info("Error!: Insert not verified. Workaround failed.");
                            }
                        }
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        logger.Warn("Error in UpdateLeadTime({0},{1}) was caught: {2}.", ltId.ToString(), ltDays.ToString(), ex.Message);
        logger.Warn("Inner exception message: {0}", ex.InnerException.InnerException.Message);
        Console.WriteLine(ex.Message);
        return false;
    }
    return true;
}

Я просто администратор баз данных, но всякий раз, когда появляется что-то подобное, я считаю это запахом кода. То есть, почему у вас есть что-то, что зависит от определенных строк, имеющих определенные значения идентичности? То есть, в приведенном выше примере, почему миссис Новелетт нужна идентификационная величина 106? Вместо того, чтобы полагаться на то, что это всегда так, вы можете получить ее ценность личности и использовать ее везде, где бы вы жестко закодировали 106. Немного более громоздко, но гораздо более гибко (на мой взгляд).

Это сработало (в данный момент я использую EFCore 3.x, безусловно, подходит для более поздних версий). Вызовите это с вводомtrue(т.е. установитьIDENTITY_INSERTкON) где-то перед вашим первым вызовомSaveAsync. Позвоните еще раз сfalseкогда это будет сделано (может быть, не требуется). Ключевой момент в том, чтоOpenConnectionдолжен был быть настроен, чтобы сделать эту работу. При сохранении несколько раз в одном контексте, по моему опыту, все равно нужно вызывать это только один раз, в начале, но даже если это вызывается/устанавливается в true несколько раз, это не повредит.

      public void Set_IDENTITY_INSERT(bool turnOn, DbContext context, string tableName)
{
    var db = context.Database;

    string command = $"SET IDENTITY_INSERT {tableName} {(turnOn ? "ON" : "OFF")}";

    if(turnOn)
        db.OpenConnection();

    db.ExecuteSqlCommand(command);

    if(!turnOn)
        db.CloseConnection();
}

Мое решение для EF6 Code First. Зависит от таблицы, не требующей заполнения других столбцов.

        db.Database.ExecuteSqlCommand($"SET IDENTITY_INSERT [**table**] ON; insert into [**table**] ([ID]) values (@id)", new SqlParameter("id", entity.ID));

  db.Entry(entity).State = EntityState.Modified;

  await db.SaveChangesAsync();

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

Это решение не идеально, но решаетошибка для Code First.

Обновление 16.08.22

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

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

К счастью (наверное), я уже знал, что в таблице нет обязательных столбцов, кроме первичного ключа. Как уже было сказано, так бывает не всегда, но в этот раз так было.

Если бы у меня были дополнительные столбцы, о которых нужно было бы беспокоиться, я бы не стал добавлять их в сценарий SQL. Несмотря на то, что вопрос о таблице редко меняется, это все еще явно запах кода.

Но со всеми вещами это компромисс (см. каждый второй ответ), и если вы читаете это и задаетесь вопросом, как поступить с дополнительными столбцами.

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

Мой второй выбор, как я упоминал выше, будет состоять в том, чтобы добавить дополнительные столбцы в сценарий SQL с разумными значениями по умолчанию. Затем, возможно, придумайте ручной процесс, обеспечивающий синхронизацию SQL.

Я не мог найти способ вставить записи в таблицу. По сути, я создал сценарий SQL с чем-то вроде этого...

            sb.Append("SET IDENTITY_INSERT [dbo].[tblCustomer] ON;");

            foreach(...)
            {
                var insert = string.Format("INSERT INTO [dbo].[tblCustomer]
                     ([ID],[GivenName],[FamilyName],[NINumber],[CustomerIdent],
                      [InputterID],[CompanyId],[Discriminator]) 
                      VALUES({0}, '{1}', '{2}', '{3}', '{4}', 2, 2, 'tblCustomer'); ", 
                          customerId, firstName, surname, nINumber, Guid.NewGuid());

            sb.Append(insert);
                ...
            }

            sb.Append("SET IDENTITY_INSERT [dbo].[tblCustomer] OFF;");
            using (var sqlConnection = new SqlConnection(connectionString))
            {
                var svrConnection = new ServerConnection(sqlConnection);
                var server = new Server(svrConnection);
                server.ConnectionContext.ExecuteNonQuery(sb.ToString());
        }

Я использую EF 6.

Другие вопросы по тегам