2014-02-07 41 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。