2012-11-30 21 views
1

我使用EF和AutoMapper与Azure表存储模拟。EntityFramework DatabaseGenerated.Identity和AutoMapper == null ID键

我有两种类型,Fragment(可序列化DTO)和FragmentEntity(EF持久DO)。我使用AutoMapper从DTO映射到DO,以便在PUT调用上持久化,反之亦然。

问题是我对EF和AutoMapper都没有经验。我已经将FragmentEntity类(根据我可以找到的文档)配置为在数据库中生成片段的Id(请参见下文)。

实体ID的定义:

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

在运行HTTP客户端与所述新的内容(片段)一个REST PUT调用。这是通过WCF接收并序列化为片段,如下所示。

[OperationContract] 
[WebInvoke(UriTemplate = "/Fragments", Method = "PUT")] 
void PutFagment(Fragment fragment); 

在调试过程中,如果我跨越代码接收到的ID值为空(因为我已将ID属性定义为可选)。

public void PutFagment(Fragment fragment) 
{ 
     var fragmentEntity = Mapper.Map<Fragment, FragmentEntity>(fragment); 
     _fragmentContext.Add(fragmentEntity); 
} 

[DataMember] 
public virtual int? Id { get; set; } 

我不确定是否将其定义为可选是明智之举;我这样做是因为如果它不是可选的,那么默认值为零,然后automapper映射到目标FragmentEntity,随后所有的键在数据库中最终为零(明显错误)。

正如你所料,我也已经使FragmentEntity Id可选,以避免在应用程序代码中设置一个值,我的希望是如果它为null数据库代可能发生但是这似乎并没有发生。

<Fragment><Id i:nil="true"/><Name>Drei</Name><Type>3</Type></Fragment> 

我的问题:

  1. 我认为决定把ID可选在这两个类是不正确的,我认为有一些AutoMapper配置,这将适当地处理这种情况,并且既可以保持非可选的。
  2. 我不确定为什么DatabaseGenerated属性没有生效。
+0

1.同意,它可能不应该是可选的。您可以在AutoMapper配置中使用条件选项,以便在零映射时不映射它,但我认为这不是您真正的问题。 2.这是你真正的问题,但不幸的是,我没有EF能够提供帮助的经验。你可能在这里找到一些有趣的东西? http://stackoverflow.com/questions/5124911/using-database-generated-guid-and-datetime-with-ef4 – Mightymuke

+0

感谢您的链接,它给了我一个有趣的想法。也许是因为azure表存储不是关系数据库,它不提供数据库密钥生成。这也可以解释为什么当它在表中有两个项目(0或null)时它不会引发异常......也许我必须获得该代码的所有权。 – Syntax

+0

如果您试图保留重复的RowKey值,它实际上会抛出异常,但很明显,因为RowKey用于唯一性,所以用户定义的任何键都不会被认可,这意味着Azure表存储不支持[Key]和[DatabaseGeneratedOption]属性。 – Syntax

回答

0

因此,事实证明,开发人员负责使用天蓝色表格存储来生成其唯一的rowkey。这是为FragmentEntity完成的,其延伸TableServiceEntity。即使提供商不支持数据库密钥生成,开发人员也必须拥有这个。

这意味着:

  1. 您必须拥有ID生成代码。
  2. 您不需要在您的TableServiceEntity中定义Id属性,而只需在两个方向的AutoMapper配置中提供合理的映射。

该ID不应该由客户端指定(这是服务器的责任)。因此,从DTO到持久化DO的映射会覆盖默认情况下使用新生成的Guid设置的值(请参阅下面的代码)。

Mapper.Configuration.CreateMap<Fragment, FragmentEntity>() 
     .ForMember(dest => dest.RowKey, 
       expression => expression.ResolveUsing(source => Guid.NewGuid().ToString())) 
// NB: you are also required to provide a partition key for TableServiceEntities 
     .ForMember(dest => dest.PartitionKey, 
       opt => opt.ResolveUsing(source => PartitionKeyConstants.LocalPartitionKey)); 

然后从FragmentEntity片段在另一个方向上的映射必须被配置成将DTO的Id属性设置为FragmentEntity的RowKey值。

Mapper.Configuration.CreateMap<FragmentEntity, Fragment>() 
     .ForMember(dest => dest.Id, 
       expression => expression.ResolveUsing(source => source.RowKey)); 

我的映射代码注册在我的Global.asax.cs中以处理映射注册。我会在某个时候将其转移到IoC容器中。

由于此更改,我不再在我的FragmentEntity中定义一个ID,并且Fragment类中已定义的Id属性不再是可选的。

[DataMember] 
public virtual Guid Id { get; set; } 

注意:我使用了Guid,因为它更容易生成唯一的Guid值,而不是跟踪int键和增量。这也意味着我在检查下一个int键值时不会引入数据库锁定瓶颈。