2014-02-07 47 views
5

我们使用的是EntityFramework,并且已经和Database First一起经历了漫长的旅程。我们有多个上下文和许多实体。尽管我们首先接受了代码项目的概念,但我们想要探索EF提供的其他可能性。EntityFramework首先使用数据库检测复杂类型

我们正在使用最新版本的EF作为当前,6.0.2

我们有相当与普通场数表,如审计领域“CreatedBy”,“CreatedDate”,“UpdatedBy”和“UpdatedDate “我们认为在这里使用ComplexType是完美的。

在使用这些字段的表中,当我们从数据库中生成代码时,它会以raw的形式提取字段。然后我们在模型浏览器中删除它,添加复杂类型,然后将复杂属性映射到数据库中的字段名称。

在这个实验中,我们运行了“从模型生成数据库”,而没有映射字段以查看结果列名是什么,以及是否有任何约定或自动魔术会双向允许该行为(将复杂类型转换为列并将列识别为复杂类型)。

这导致列名称的复杂类型后缀为'_'和字段名称。我们进行了测试,并且在重新生成模型时,它不会从模型中的数据库中拉回复杂类型。

有没有一种正确的方法让EF首先检测带有数据库的表中的复杂类型? 有没有我可以编写的代码工厂,构建器,策略或模板?

我们的主要动机是我们有相当多的表格支持,我们接受频繁的更改,我们希望阻止团队中的人员忽略此步骤并破坏代码库。

非常感谢您的时间StackOverflowians!

- 编辑 -

虽然这并不使用自动检测复杂类型的解决这个问题,这有解决开发者的打破它们运行的​​T4模板更新时间模型的问题。

http://www.ninjacrab.com/2015/02/07/update-entityframework-detecting-complex-type-with-database-first/

+0

您是否建议使用复杂类型来简化审计的使用?作为一个便笺,我很好奇你的审计专栏名称。是CreatedBy一个varchar?如果没有,并且它实际上是和Id指针,那么我会建议CreatedById。我喜欢CreatedBy的可读性,但是CreatedDate听起来有点老派,你可能会考虑CreatedOn(我不喜欢[匈牙利记法](http://en.wikipedia.org/wiki/Hungarian_notation))。 –

+0

是的,我们使用复杂的类型来缓解审计。完全开放给其他做法/想法。 我们的“By”字段是varchar而不是FK /指针ID,因为在我们的用例中,我们实际上允许在引用数据上进行硬删除。每次我们写入主表时,它实际上都会将记录写入辅助“审计”表中。虽然实际的表可能(也可能应该)实际上对主用户有一个FK,但审计表不会强制引用,以便我们可以维护一条路径。 最后,我同意“关于”,之前没有听到过这个建议,也没有人真正抱怨过:) – Min

+0

呃“已用”。当然不再像帖子所描述的那样了 – Min

回答

1

我所做的是在EF通用半库模式。如果匹配,我还使用接口来识别和自动使用方法。

可审计接口:

public interface IDbAuditable : IDbEntity 
{ 
    Guid CreatedById { get; set; } 
    DateTime CreatedOn { get; set; } 
    Guid ModifiedById { get; set; } 
    DateTime ModifiedOn { get; set; } 
    Guid? DeletedById { get; set; } 
    DateTime? DeletedOn { get; set; } 
} 

(我个人比较喜欢的想法,如果CreatedOn == ModifiedOn那么它从来没有被修改,一些人喜欢的DateTime?其实并不重要,他们为同样的目的)

由于这个例子是在一个小项目中,我简单地包装了EF上下文,并没有使用任何IoC/DI。这是所有实际代码的一小部分(某些方法丢失,某些接口丢失,但它应该很有意义)。

public sealed class MyDb : IDisposable 
{ 
    private MyContext _context; 

    public MyDb(string connectionString, Lazy<Guid?> currentUserIdFunc) 
    { 
     this._context = new MyContext(connectionString); 

     Database.SetInitializer<MyContext>(new DatabaseInitializer()); 
     this._context.Database.Initialize(true); 

     this._currentUserIdFunc = currentUserIdFunc; 
    } 

    public async Task<T> GetEntityAsync<T>(Func<IQueryable<T>, IQueryable<T>> entityQuery) where T : class, IDbEntity 
    { 
     var query = entityQuery(this._context.Set<T>()); 

     if (typeof(T) is IDbAuditable) 
     { 
      query = query.Cast<IDbAuditable>() 
       .Where(a => !a.DeletedById.HasValue) 
       .Cast<T>(); 
     } 

     return await query.FirstOrDefaultAsync(); 
    } 

    public async Task<int> UpdateAsync<T>(T entity) where T : class, IDbEntity 
    { 
     if (entity is IDbDoNotModify) 
     { 
      throw new DoNotModifyException("Entity cannot be Modified (IDoNotModify)."); 
     } 

     this._context.Set<T>().Attach(entity); 
     var entry = this._context.Entry<T>(entity); 
     entry.State = EntityState.Unchanged; 

     var entityType = entity.GetType(); 

     var metadata = entityType.GetCustomAttributes(typeof(MetadataTypeAttribute)).FirstOrDefault() as MetadataTypeAttribute; 
     if (metadata != null) 
     { 

      var type = metadata.MetadataClassType; 

      var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public) 
       .Select(p => new 
       { 
        Name = p.Name, 
        ScaffoldColumn = p.GetCustomAttributes(typeof(ScaffoldColumnAttribute), true).FirstOrDefault() as ScaffoldColumnAttribute, 
        Readonly = entityType.GetProperty(p.Name).GetCustomAttributes(typeof(ReadOnlyAttribute), true).FirstOrDefault() as ReadOnlyAttribute 
       }) 
       .Where(p => (p.ScaffoldColumn == null || p.ScaffoldColumn.Scaffold) 
        && (p.Readonly == null || !p.Readonly.IsReadOnly)) 
       .ToList(); 

      foreach (var property in properties) 
      { 
       entry.Property(property.Name).IsModified = true; 
      } 

     } 
     else 
     { 
      entry.State = EntityState.Modified; 
     } 

     var auditable = entity as IDbAuditable; 
     if (auditable != null) 
     { 
      this.Modified(auditable, this._currentUserIdFunc.Value); 
      entry.Property("ModifiedOn").IsModified = true; 
      entry.Property("ModifiedById").IsModified = true; 
     } 


     return await this._context.SaveChangesAsync(); 
    } 

    private void Modified(IDbAuditable instance, Guid? currentUserId) 
    { 
     instance.ModifiedById = currentUserId.Value; 
     instance.ModifiedOn = DateTime.Now; 
    } 
} 

这里是我会怎么使用它:

// returns the first car with a model of ford 
var car = MyDb.EntityAsync<Car>((query) = query 
    .Where(c => c.Model.Equals("ford") 
); 

// returns the first dog with an ownerId of id 
var car = MyDb.EntityAsync<Dog>((query) => query 
    .Where(d => d.OwnerId == Id) 
); 

UpdateAsync(car); 

这个示例应用程序是非常小的,所以我没有实施任何的UnitOfWork,但它可以很容易地修改这样的情况。另外,如果您有很多上下文,MyDb类可以变成MyDb<T>。我允许大量使用DataAnnotations。