C# EF中的GUID-COMB策略

C# EF中的GUID-COMB策略,c#,entity-framework,guid,entity-framework-migrations,newsequentialid,C#,Entity Framework,Guid,Entity Framework Migrations,Newsequentialid,在新的Entity Framework 4.1中,是否有任何方法可以使用CodeFirst设计为对象实现Guid梳标识策略?我原以为设置StoreGeneratedPattern会起作用,但它仍然为我提供了正常的guid 我猜您正在使用SQL server作为数据库。这是不同MS工具之间不一致的一个很好的例子。SQL server团队不建议使用newid()作为UNIQUEIDENTIFIER列的默认值,如果将Guid属性指定为在数据库中自动生成,ADO.NET团队将使用它。他们应该改用news

在新的Entity Framework 4.1中,是否有任何方法可以使用CodeFirst设计为对象实现Guid梳标识策略?我原以为设置
StoreGeneratedPattern
会起作用,但它仍然为我提供了正常的guid

我猜您正在使用SQL server作为数据库。这是不同MS工具之间不一致的一个很好的例子。SQL server团队不建议使用
newid()
作为
UNIQUEIDENTIFIER
列的默认值,如果将
Guid
属性指定为在数据库中自动生成,ADO.NET团队将使用它。他们应该改用
newsequentialid()

若您想要由数据库生成连续的guid,那个么必须修改生成的表,而且它非常复杂,因为您必须找到自动生成的默认约束,删除它并创建新的约束。这一切都可以在自定义数据库初始值设定项中完成。这是我的示例代码:

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);
    }
}
类程序
{
静态void Main(字符串[]参数)
{
SetInitializer(新的CustomInitializer());
使用(var context=new context())
{
Add(newtestentity(){Name=“A”});
Add(newtestentity(){Name=“B”});
SaveChanges();
}
}
}
公共类CustomInitializer:DropCreateDatabaseAlways
{
受保护的覆盖无效种子(上下文)
{
种子(上下文);
context.Database.ExecuteSqlCommand(@)
声明@Name VARCHAR(100)
从sys.objects中选择@Name=O.Name作为O
内部连接sys.tables作为O.parent\u object\u id=T.object\u id上的T
其中O.type_desc类似于“DEFAULT_CONSTRAINT”
和O.名称,如“DF_utestenties_uid_uu%”
和T.Name='TestEntities'
声明@Sql NVARCHAR(2000)='ALTER TABLE TestEntities DROP Constraint'+@Name
EXEC sp_executesql@Sql
更改表格测试
为Id“)添加约束IdDef DEF DEFAULT NEWSSEquentialid();
}
}
公共类测试
{
公共Guid Id{get;set;}
公共字符串名称{get;set;}
}
公共类上下文:DbContext
{
公共数据库集测试实体{get;set;}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
基于模型创建(modelBuilder);
modelBuilder.Entity()
.Property(e=>e.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}

为什么要担心数据库中Guid列的默认值?为什么不像其他值一样在客户机上生成Guid呢。这需要在客户端代码中有一个方法,该方法将生成梳状GUI:

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的一个优点是,您可以在客户机上生成Guid,而无需往返数据库

最简单的答案

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()
,在我的情况下,它由FluentMigrations管理。

我想您已经检查了DB服务器端默认值?是的,我已经检查了。我没有发现它能解决我的问题。+1如果定义类似梳子的guid非常简单,那么您可以将此代码与以下代码结合起来:并且您得到guid.COMB initializer。我很难在这两个答案之间进行选择,因为两者不可避免地会导致相同的结果。它们都很有用。非常感谢您提供的这些信息,它工作得非常好。使用顺序生成的GUID将减少SQL server中索引的复杂性。在SQL server上使用newsequencialid()时要小心。如果服务器重新启动或nic更改,则您将获得一个新序列,该序列可能低于初始序列。这似乎是不必要的。我真的希望4.1 EF比这更强大。非常感谢你为我澄清这一点,它工作得非常好。我强烈建议不要在种子方法中做这样的事情。相反,它属于单独的迁移。每次触发迁移时都将调用种子。并不是说这不是一个正确的解决方案,只是说它在错误的地方触发了。@CasperLeonNielsen:我在EF获得迁移API之前很久就写了这个例子。您可以使用迁移添加新答案以使其最新。为了帮助其他人,它需要一个“FluentMigrator migrations”的示例@CasperLeonNielsen。这不是真的,它要求表架构具有默认设置。如何设置
newsequentialid()
的默认值实际上超出了这个问题的范围,成为了一个通用的sql架构问题。@CasperLeonNielsen这里链接的内容是关于设置sql中数据行的值,我说的是实际的表架构