Стратегия GUID COMB в EF

Есть ли способ реализовать стратегию идентификации Guid COMB для объектов в новой Entity Framework 4.1 с использованием дизайна CodeFirst? Я думал, что установка StoreGeneratedPattern сработает, но она по-прежнему дает мне обычные GUID.


person Ciel    schedule 23.05.2011    source источник
comment
Я полагаю, вы уже проверили значение по умолчанию на стороне сервера БД?   -  person Craig Stuntz    schedule 23.05.2011
comment
Да, у меня есть. Я не нашел его для решения моей проблемы.   -  person Ciel    schedule 24.05.2011


Ответы (3)


Я предполагаю, что вы используете SQL-сервер в качестве базы данных. Это хороший пример несогласованности между различными инструментами MS. Команда SQL Server не рекомендует использовать newid() в качестве значения по умолчанию для столбцов UNIQUEIDENTIFIER, а группа ADO.NET использует его, если вы укажете свойство Guid как автоматически сгенерированное в базе данных. Вместо этого они должны использовать newsequentialid()!

Если вы хотите, чтобы последовательные Guids генерировались базой данных, вы должны изменить сгенерированную таблицу, и это действительно сложно, потому что вы должны найти автоматически сгенерированное ограничение по умолчанию, удалить его и создать новое ограничение. Все это можно сделать в пользовательском инициализаторе базы данных. Вот мой пример кода:

class Program
{

    static void Main(string[] args)
    {
        Database.SetInitializer(new CustomInitializer());
        using (var context = new Context())
        {
            context.TestEntities.Add(new TestEntity() { Name = "A" });
            context.TestEntities.Add(new TestEntity() { Name = "B" });
            context.SaveChanges();
        }
    }
}

public class CustomInitializer : DropCreateDatabaseAlways<Context>
{
    protected override void Seed(Context context)
    {
        base.Seed(context);

        context.Database.ExecuteSqlCommand(@"
            DECLARE @Name VARCHAR(100)

            SELECT @Name = O.Name FROM sys.objects AS O
            INNER JOIN sys.tables AS T ON O.parent_object_id = T.object_id
            WHERE O.type_desc LIKE 'DEFAULT_CONSTRAINT' 
              AND O.Name LIKE 'DF__TestEntities__Id__%'
              AND T.Name = 'TestEntities'

            DECLARE @Sql NVARCHAR(2000) = 'ALTER TABLE TestEntities DROP Constraint ' + @Name

            EXEC sp_executesql @Sql

            ALTER TABLE TestEntities
            ADD CONSTRAINT IdDef DEFAULT NEWSEQUENTIALID() FOR Id");
    }
}

public class TestEntity
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

public class Context : DbContext
{
    public DbSet<TestEntity> TestEntities { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<TestEntity>()
            .Property(e => e.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}
person Ladislav Mrnka    schedule 23.05.2011
comment
Это кажется ненужным. Я действительно надеялся, что 4.1 EF будет более надежным, чем этот. Большое спасибо за разъяснение этого для меня, это сработало отлично. - person Ciel; 24.05.2011
comment
Я настоятельно рекомендую не делать подобные вещи в методе seed. Вместо этого он относится к отдельной миграции. Seed будет вызываться каждый раз при запуске миграции. Не говоря уже о том, что это неправильное решение, просто оно срабатывает не в том месте. - person Casper Leon Nielsen; 10.12.2012
comment
@CasperLeonNielsen: я написал этот пример задолго до того, как EF получил API миграции. Вы можете добавить новый ответ с миграцией, чтобы сделать его актуальным. - person Ladislav Mrnka; 11.12.2012

Зачем вообще беспокоиться о значениях по умолчанию для столбцов Guid в базе данных? Почему бы просто не сгенерировать Guid на клиенте, как и любое другое значение. Для этого требуется, чтобы в вашем клиентском коде был метод, который будет генерировать COMB-подобные направляющие:

public static Guid NewGuid()
{
    var guidBinary = new byte[16];
    Array.Copy( Guid.NewGuid().ToByteArray(), 0, guidBinary, 0, 8 );
    Array.Copy( BitConverter.GetBytes( DateTime.Now.Ticks ), 0, guidBinary, 8, 8 );
    return new Guid( guidBinary );
}

Одним из преимуществ Guid является именно то, что вы можете генерировать их на клиенте без обращения к базе данных.

person Thomas    schedule 23.05.2011
comment
+1 Если определить COMB, как Guids, так просто, вы можете объединить этот код с этим: stackoverflow.com/questions/5275306/, и вы получите руководство. инициализатор гребенки. - person Ladislav Mrnka; 23.05.2011
comment
Мне было трудно выбрать между двумя ответами, поскольку оба неизбежно вели к одному и тому же результату. Они оба полезны. Большое спасибо за эту информацию, она работает очень хорошо. - person Ciel; 24.05.2011
comment
Использование последовательно сгенерированных Guid приведет к тому, что индексация будет менее сложной на сервере SQL. - person Elan Hasson; 16.09.2011
comment
Будьте осторожны при использовании newsequencialid() на сервере Sql. Если сервер перезагрузится или изменится nic, вы получите новую последовательность, и она может быть ниже исходной последовательности. - person Mike; 19.01.2012

Самый простой ответ

public class User
{
    public User(Guid? id = null, DateTime? created = null)
    {
        if (id != null)
            Id = id;

        if (created != null)
            Created = created;
    }

    public User()
    {
    }

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public DateTime? Created { get; internal set; }

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

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

person Chris Marisic    schedule 17.08.2011
comment
Чтобы это помогло другим, нужен пример миграции FluentMigrator. - person Casper Leon Nielsen; 10.12.2012
comment
@CasperLeonNielsen это не совсем так, для схемы таблицы требуется набор по умолчанию. То, как вы устанавливаете значение по умолчанию для newsequentialid(), действительно выходит за рамки этого вопроса и становится общей проблемой схемы sql. - person Chris Marisic; 12.12.2012
comment
stackoverflow.com/questions/12257465/ - person Casper Leon Nielsen; 12.12.2012
comment
@CasperLeonNielsen то, что вы там связали, касается установки значений строк данных в sql, о чем я говорю, это фактическая схема таблицы dba.stackexchange.com/questions/7249/< /а> - person Chris Marisic; 12.12.2012