Подтвердить что ты не робот

Entity Framework 6 GUID как первичный ключ: не может вставить значение NULL в столбец "Id", таблицу "FileStore"; столбец не допускает

У меня есть объект с первичным ключом "Id", который является Guid:

public class FileStore
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
}

И какая-то конфигурация:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<FileStore>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    base.OnModelCreating(modelBuilder);
}

Когда я пытаюсь вставить запись, я получаю следующую ошибку:

Невозможно вставить значение NULL в столбец "Id", таблицу "FileStore"; столбец не допускает нулей. INSERT терпит неудачу.\R\nПриложение завершено.

Я не хочу генерировать Guid вручную. Я просто хочу вставить запись и получить Id, сгенерированный SQL Server. Если я установил столбец .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity), Id не является столбцом Identity в SQL Server.

Как я могу настроить Entity Framework для автоматического создания Guid в SQL Server?

4b9b3361

Ответ 1

В дополнение к добавлению этих атрибутов в столбец идентификатора

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }

в вашей миграции вы должны изменить свой CreateTable, чтобы добавить свойство defaultValueSQL в свой столбец i.e.:

Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),

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

Ответ 2

попробуйте следующее:

public class FileStore
 {
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public Guid Id { get; set; }
   public string Name { get; set; }
   public string Path { get; set; }
 }

Вы можете проверить это сообщение fooobar.com/questions/96256/....

Ответ 3

Вы можете установить значение по умолчанию вашего идентификатора в своем db для newsequentialid() или newid(). Тогда должна работать конфигурация идентичности EF.

Ответ 4

Это случилось со мной раньше.

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

Исправление:

Нам нужно только перейти в базу данных, выбрать столбец идентификатора и добавить newsequentialid() вручную в Default Value or Binding.

Не нужно обновлять dbo.__ Таблица MigrationHistory.

Надеюсь, что это поможет.


Решение добавления New Guid() обычно не является предпочтительным, поскольку в теории существует вероятность того, что вы можете получить дубликат случайно.


И вы не должны беспокоиться о прямом редактировании в базе данных. Все Entity Framework делают автоматизацию нашей работы с базой данных.

Переводы

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)

в

[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),

Если бы какой-то наш EF пропустил одну вещь и не добавил в значение по умолчанию для нас, просто добавьте ее вручную.

Ответ 5

Это работает для меня (без Azure), SQL 2008 R2 на dev-сервере или localdb\mssqllocaldb на локальной рабочей станции. Примечание: объект добавляет столбцы Create, CreateBy, Modified, ModifiedBy и Version.

public class Carrier : Entity
{
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

затем создайте класс конфигурации сопоставления

public class CarrierMap : EntityTypeConfiguration<Carrier>
{
    public CarrierMap()
    {
        HasKey(p => p.Id);

        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(p => p.Code)
            .HasMaxLength(4)
            .IsRequired()
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsClustered = true, IsUnique = true }));

        Property(p => p.Name).HasMaxLength(255).IsRequired();
        Property(p => p.Created).HasPrecision(7).IsRequired();
        Property(p => p.Modified)
            .HasColumnAnnotation("IX_Modified", new IndexAnnotation(new IndexAttribute()))
            .HasPrecision(7)
            .IsRequired();
        Property(p => p.CreatedBy).HasMaxLength(50).IsRequired();
        Property(p => p.ModifiedBy).HasMaxLength(50).IsRequired();
        Property(p => p.Version).IsRowVersion();
    }
}

Это создает метод Up в начальном DbMigration при выполнении add-migration, подобный этому

        CreateTable(
            "scoFreightRate.Carrier",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Code = c.String(nullable: false, maxLength: 4),
                    Name = c.String(nullable: false, maxLength: 255),
                    Created = c.DateTimeOffset(nullable: false, precision: 7),
                    CreatedBy = c.String(nullable: false, maxLength: 50),
                    Modified = c.DateTimeOffset(nullable: false, precision: 7,
                        annotations: new Dictionary<string, AnnotationValues>
                        {
                            { 
                                "IX_Modified",
                                new AnnotationValues(oldValue: null, newValue: "IndexAnnotation: { }")
                            },
                        }),
                    ModifiedBy = c.String(nullable: false, maxLength: 50),
                    Version = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
                })
            .PrimaryKey(t => t.Id)
            .Index(t => t.Code, unique: true, clustered: true);

Примечание: столбцы Id не получают значения по умолчанию, не волнуйтесь

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

CREATE TABLE [scoFreightRate].[Carrier] (
    [Id]         UNIQUEIDENTIFIER   DEFAULT (newsequentialid()) NOT NULL,
    [Code]       NVARCHAR (4)       NOT NULL,
    [Name]       NVARCHAR (255)     NOT NULL,
    [Created]    DATETIMEOFFSET (7) NOT NULL,
    [CreatedBy]  NVARCHAR (50)      NOT NULL,
    [Modified]   DATETIMEOFFSET (7) NOT NULL,
    [ModifiedBy] NVARCHAR (50)      NOT NULL,
    [Version]    ROWVERSION         NOT NULL,
    CONSTRAINT [PK_scoFreightRate.Carrier] PRIMARY KEY NONCLUSTERED ([Id] ASC)
);


GO
CREATE UNIQUE CLUSTERED INDEX [IX_Code]
    ON [scoFreightRate].[Carrier]([Code] ASC);

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

public class OurMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AddPrimaryKeyOperation addPrimaryKeyOperation)
    {
        if (addPrimaryKeyOperation == null) throw new ArgumentNullException("addPrimaryKeyOperation");
        if (!addPrimaryKeyOperation.Table.Contains("__MigrationHistory"))
            addPrimaryKeyOperation.IsClustered = false;
        base.Generate(addPrimaryKeyOperation);
    }

    protected override void Generate(CreateTableOperation createTableOperation)
    {
        if (createTableOperation == null) throw new ArgumentNullException("createTableOperation");
        if (!createTableOperation.Name.Contains("__MigrationHistory"))
            createTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(createTableOperation);
    }

    protected override void Generate(MoveTableOperation moveTableOperation)
    {
        if (moveTableOperation == null) throw new ArgumentNullException("moveTableOperation");
        if (!moveTableOperation.CreateTableOperation.Name.Contains("__MigrationHistory")) moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(moveTableOperation);
    }
}

Ответ 6

В соответствии с this, DatabaseGeneratedOption.Identity не определяется определенной миграцией, если она добавила после, таблица был создан, и в этом случае я сталкиваюсь. Поэтому я удалил базу данных и эту конкретную миграцию и добавил новую миграцию, наконец, обновил базу данных, а затем все работает так, как ожидалось. Я использую EF 6.1, SQL2014 и VS2013.

Ответ 7

Entity Framework - используйте Guid в качестве основного ключа

Использование Guid в качестве основного файла ваших таблиц при использовании Entity Framework требует немного больше усилий, чем при использовании целого числа. Процесс установки прост, после того, как вы прочитали/показали, как это сделать.

Процесс немного отличается для подходов Code First и Database First. В этом сообщении обсуждаются обе методики.

введите здесь описание изображения

Первый код

Использование Guid в качестве первичного ключа при первом подходе к коду является простым. При создании объекта добавьте атрибут DatabaseGenerated в свой первичный ключ, как показано ниже:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }

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

codefirst-defaultvalue

Также обратите внимание, что очень важно, чтобы значение по умолчанию для столбца было установлено на (newsequentialid()). Это генерирует новый последовательный (непрерывный) Guid для каждой строки. Если бы вы были настолько склонны, вы могли бы изменить это на newid()), что приведет к совершенно случайному Guid для каждой новой строки. Это будет очищаться каждый раз, когда ваша база данных будет удалена и повторно создана, поэтому это лучше работает при использовании подхода Database First.

База данных сначала

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

Убедитесь, что вы отредактировали столбец первичного ключа и добавили функцию newsequentialid()) или (newid()) в качестве значения по умолчанию, прежде чем что-либо делать.

введите здесь описание изображения

Затем откройте диаграмму EDMX, выберите соответствующее свойство и откройте окно свойств. Убедитесь, что для StoreGeneratedPattern установлено значение identity.

databasefirst-model

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

using (ApplicationDbContext context = new ApplicationDbContext())
{
    var person = new Person
                     {
                         FirstName = "Random",
                         LastName = "Person";
                     };

    context.People.Add(person);
    context.SaveChanges();
    Console.WriteLine(person.Id);
}

Важное примечание: поле "Guid" ДОЛЖНО быть первичным ключом, или это не работает. Entity Framework предоставит вам довольно загадочное сообщение об ошибке!

Резюме

Guid (глобально уникальные идентификаторы) можно легко использовать в качестве основных ключей в Entity Framework. Для этого требуется немного дополнительных усилий, в зависимости от того, какой подход вы принимаете. При использовании первого подхода кода добавьте атрибут DatabaseGenerated в поле ключа. При использовании подхода Database First явным образом устанавливаю StoredGeneratedPattern в Identity на вашей модели.

[1]: https://i.stack.imgur.com/IxGdd.png
[2]: https://i.stack.imgur.com/Qssea.png

Ответ 8

Если вы используете Code-First и уже имеете базу данных:

public override void Up()
{
    AlterColumn("dbo.MyTable","Id", c =>  c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"));
}

Ответ 9

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

Создайте GUID на уровне С#, чтобы отношения продолжали работать.

Ответ 10

И что-то вроде этого?

public class Carrier : Entity
{
    public Carrier()
    {
         this.Id = Guid.NewGuid();
    }  
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}