EF,代码第一 - 如何设置在插入自定义的Guid标识值 [英] EF, Code First - How to set a custom Guid identity value on insert

查看:2263
本文介绍了EF,代码第一 - 如何设置在插入自定义的Guid标识值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我真的在具有数据库中插入新的实体打交道时面临着以下问题的的Guid 作为主键 - EF 5与代码第一个办法



我知道有很多类似的题目,因为我是粗纱此问题小时,但我couldn`t找到这个问题的话题。



作为一个例子,我的POCO类是:

 公共类EntityRole:IAdminModel 
{
〔键]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)
公众的Guid标识{搞定;组; }

[必填]
[最大长度(50)]
公共字符串名称{;组; }

[必填]
[显示(NAME =角色代码)]
[最大长度(20)]
公共字符串RoleCode {搞定;组; }

[显示(名称=实体分配)]
[InverseProperty(角色)]
公开名单<实体GT;实体{搞定;组; }
}

RoleCode和名称只是文本数据,这是可编辑的管理面板,所以不`吨考虑这些字段名。



在添加新的实体,我不`吨指定主键值。到目前为止好,都在这里工作正常。由于需要的价值,并具有自动生成值的主键字段,它应该不会始终指定ID,但如果我设置一个值,它应该保持(如果启用标识插入)。



但在某些情况下,我想指定一个主键的值,例如我的分贝值的初始种子。我需要它以后处理 - 让刚刚说我需要一个特定的Guid在那里。所以,如果我有我的配置类:

  //初始数据种子
保护覆盖无效种子(WebPortalDbContext上下文)
{
context.MenuItems.AddOrUpdate(
M = GT; m.Id,
新EntityRole {ID =新的GUID(268bf332-910e-4ea1-92f8-1ac0611f4c62) ,名称=一些名,RoleCode =SN},
);
}

如果我做一个定期添加GUID项设置那张甚至没有工作:

 使用(VAR上下文=新MyDBContext())
{
context.MenuItems.Add(
新实体(){ID =新的GUID(<有的GUID>中),名称=fooname/ *一些其它的值* /}
);

context.SaveChanges();
}



我已经在SQL Server跟踪是:



  EXEC sp_executesql的N'declare @generated_keys表([ID]唯一标识符)
插入[DBO]。[EntityRoles]([名],[ RoleCode])
输出插入。[ID]进入@generated_keys
值(@ 0,@ 1)
选择吨。[ID]从@generated_keys为g加入
[DBO 。] [EntityRoles]作为克叔[ID] = T [ID]
,其中@@ ROWCOUNT过夜。; 0',N'@ 0为nvarchar(50),@ 1为nvarchar(20)',@ 0 = N'Chief船长',@ 1 = N'CO1'

下面显而易见的是新的Guid值只是不从EF SQL发生器到SQL Server发送,所以这个问题是在EF



所以我删除了 DatabaseGeneratedOption.Identity 属性,那么它是确定的,但我失去了自动生成的ID密钥,它不适合我,因为它的工作,是非常罕见的情况。



从我唯一的解决办法现在:



我最终是要覆盖的SaveChanges()的的DbContext的方法和修改必须添加州的所有实体(我从的这里):

  ///<总结> /总结>节省changetracker<实体时,自定义处理; 
公共覆盖INT的SaveChanges()
{
//建议明确设定新的GUID适当的实体 - http://msdn.microsoft.com/en-us/library/dd283139。 ASPX
的foreach(在ChangeTracker.Entries VAR条目(),其中(E =方式> e.State == EntityState.Added))
{
变种T = entry.Entity.GetType( );
如果(t.GetProperty(ID)== NULL)
继续;

VAR信息= t.GetProperty(ID)GetCustomAttributes(typeof运算(DatabaseGeneratedAttribute),真).Cast< DatabaseGeneratedAttribute>();
如果(!info.Any()|| info.Single()。DatabaseGeneratedOption!= DatabaseGeneratedOption.Identity)
{
如果(t.GetProperty(ID)。属性类型== typeof运算(GUID)及和放大器;(GUID)t.GetProperty(ID)的GetValue(entry.Entity,NULL)== Guid.Empty)
t.GetProperty(ID)的SetValue(入门.Entity,Guid.NewGuid(),NULL);
}
}
返回base.SaveChanges();
}

在这个组合中,所有的 DatabaseGeneratedOption 应除去。所有型号都有一个名为id主键,下面的最佳实践主题命名规则之一。



但是,这并不看上去非常优雅workaronud,因为我thnk EF5应该能够处理这样的情况。它适用于智力的身份,如果标识插入上。



所以不会有人有一个想法如何实现对问题呢?


更好的解决方案< DIV CLASS =h2_lin>解决方案

最简单的方法可以让你做到这一点,因为它是一个GUID是在你的类中的一个构造像

 公共EntityRole()
{
n = Guid.NewGuid();
}

和删除数据库生成的选项,或将其更改为DatabaseGeneratedOption.None。


I`m facing the following problem when dealing with inserting new entities in the DB that has Guid as primary keys - EF 5 with Code first approach.

I know there are a lot similar topics as I was roving for hours for this issue, but I couldn`t find topic with this problem.

As an example, my POCO class is:

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

    [Required]
    [MaxLength(50)]
    public string Name { get; set; }

    [Required]
    [Display(Name = "Role code")]
    [MaxLength(20)]
    public string RoleCode { get; set; }

    [Display(Name = "Entities Assigned")]
    [InverseProperty("Role")]
    public List<Entity> Entities { get; set; }
}

RoleCode and Name are just text data, that is editable in the admin panel, so don`t consider those field names.

When adding new entity I don`t specify primary key values. So far so good, all works fine here. As a primary key field that requires value and has auto-generation of values, it is supposed not to always specify Id, but if I set a value, it should be kept (if identity insert is enabled).

But in some cases I want to specify a primary key value, for example for initial seeding of my DB with values. I need it for later processing - lets just say I need that specific Guid to be there. So if I have in my Configuration class:

// Initial data seed
protected override void Seed(WebPortalDbContext context)
{
    context.MenuItems.AddOrUpdate(
        m => m.Id,
        new EntityRole {Id = new Guid("268bf332-910e-4ea1-92f8-1ac0611f4c62"), Name = "Some name", RoleCode = "SN"},
    );
}

The Guid key setting goes not work even if I do a regular add:

using (var context = new MyDBContext())
{
    context.MenuItems.Add(
        new Entity() {Id = new Guid("<some guid>"), Name = "fooname" /*some other values*/}
    );

    context.SaveChanges();
}

What I have in the SQL Server trace is:

exec sp_executesql N'declare @generated_keys table([Id] uniqueidentifier)
insert [dbo].[EntityRoles]([Name], [RoleCode])
output inserted.[Id] into @generated_keys
values (@0, @1)
select t.[Id]
from @generated_keys as g join [dbo].[EntityRoles] as t on g.[Id] = t.[Id]
where @@ROWCOUNT > 0',N'@0 nvarchar(50),@1 nvarchar(20)',@0=N'Chief Captain',@1=N'CO1'

Here it is obvious that the new Guid value is just not sent from the EF SQL generator to the SQL Server, so the problem is in EF.

So I removed the DatabaseGeneratedOption.Identity attribute, then it is ok, but I lose the auto generation of the Id key, which does not work for me as it is very rare case.

The only solution from me for now:

I ended up is to overwrite the SaveChanges() method of the DBContext and modify all entities that have state to be added (I took the idea from here):

/// <summary> Custom processing when saving entities in changetracker </summary>
public override int SaveChanges()
{
    // recommended to explicitly set New Guid for appropriate entities -- http://msdn.microsoft.com/en-us/library/dd283139.aspx
    foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added))
    {
        var t = entry.Entity.GetType();
        if (t.GetProperty("Id") == null)
            continue;

        var info = t.GetProperty("Id").GetCustomAttributes(typeof (DatabaseGeneratedAttribute), true).Cast<DatabaseGeneratedAttribute>();
        if (!info.Any() || info.Single().DatabaseGeneratedOption != DatabaseGeneratedOption.Identity)
        {
            if (t.GetProperty("Id").PropertyType == typeof(Guid) && (Guid)t.GetProperty("Id").GetValue(entry.Entity, null) == Guid.Empty)
                t.GetProperty("Id").SetValue(entry.Entity, Guid.NewGuid(), null);
        }
    }
    return base.SaveChanges();
}

In combination with this, all DatabaseGeneratedOption should be removed. All models has primary keys named "Id", following one of the best practice topics for naming conventions.

But this does not looks very elegant workaronud, because I thnk EF5 should be able to handle such cases. It works for Int identities if identity insert is on.

So does someone has an idea how to achieve better solution on the problem?

解决方案

The simple way for you to do this since it is a GUID is to have one constructor in your class like

public EntityRole()
{
   Id = Guid.NewGuid();
}

and remove The database generated option or change it to DatabaseGeneratedOption.None.

这篇关于EF,代码第一 - 如何设置在插入自定义的Guid标识值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆