2016-05-13 284 views
4

我是EF新手,但我已經編程21年了。我喜歡把事情做得乾乾淨淨,一般而言,但是我剛剛做的事情似乎有些錯誤,但我不能把它放在手指上。EF應該封裝在基類中嗎?

EF上的每個示例我已經看到,開發人員爲每個POCO類創建4個單獨的CRUD方法。於是我開始沒有做到這一點,這就是我想出了:

型號:

using System.Data.Entity; 
using System.Reflection; 

namespace biz 
{ 
    public abstract class EFObject<T> where T : EFObject<T> 
    { 
    public int Id { get; set; } 

    internal static readonly string DbSetProperyName = typeof(T).Name + "s"; 

    public static EFCollection<T> Collection 
    { 
     get 
     { 
     using (var db = new Model1()) 
     { 
      PropertyInfo p = db.GetType().GetProperty(DbSetProperyName); 
      DbSet<T> collection = (DbSet<T>)p.GetValue(db); 
      return new EFCollection<T>(collection); 
     } 
     } 
    } 

    public void Insert() 
    { 
     using (var db = new Model1()) 
     { 
     PropertyInfo p = db.GetType().GetProperty(DbSetProperyName); 
     DbSet<T> collection = (DbSet<T>)p.GetValue(db); 
     collection.Add((T)this); 
     db.SaveChanges(); 
     } 
    } 

    public void Save() 
    { 
     if (Id == 0) 
     Insert(); 
     else 
     Update(); 
    } 

    public void Update() 
    { 
     using (var db = new Model1()) 
     { 
     PropertyInfo p = db.GetType().GetProperty(DbSetProperyName); 
     DbSet<T> collection = (DbSet<T>)p.GetValue(db); 
     T dbItem = collection.Find(Id); 
     foreach (PropertyInfo pi in typeof(T).GetProperties()) 
     { 
      pi.SetValue(dbItem, pi.GetValue(this)); 
     } 
     db.SaveChanges(); 
     } 
    } 
    } 
} 

泛型集合類:​​

所有業務層
public class Model1 : DbContext 
    { 
    public Model1() 
     : base("name=Model1") 
    { 
    } 

    public virtual DbSet<Member> Members { get; set; } 
    } 

基類:

using System.Collections.Generic; 

namespace biz 
{ 
    public class EFCollection<T> : List<T> where T : EFObject<T> 
    { 
    public EFCollection() 
    { 
    } 

    public EFCollection(IEnumerable<T> collection) 
    { 
     AddRange(collection); 
    } 

    public void Save() 
    { 
     foreach (T item in this) 
     item.Save(); 
    } 
    } 
} 

實施例的中間層類:

namespace biz 
{ 
    public class Member : EFObject<Member> 
    { 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 

    public Client[] Clients; 
    public Good[] Goods; 
    public decimal Percentage; 
    } 
} 

與用法:

var member = new biz.Member() { FirstName = "Brad", LastName = "Pitt", Percentage = 1 };// 
    member.Save(); 
    member = biz.Member.Collection.Find(o=>o.Id == member.Id); 
    member.FirstName = "Cherry"; 
    member.Save(); 

的使用代碼的工作,但我不知道是這種方法要運行我到什麼樣的問題?

有一件事讓我誤解了我所做的事情,也許是因爲我現在知道EF足夠好了。在我的更新場景中,我1)使用一個會話從集合中獲取對象,2)斷開連接,3)更新對象的屬性,3)開始新的會話,3)通過主鍵從數據庫(它不再是同一個對象!),4)通過反射更新它,然後5)保存更改。所以有兩個對象不涉及一個反射。我想我必須「放開」連接,以便在獲取它時保留原始對象,但我不知道如何解決此問題。

+0

沒有一次性提交一個對象圖的,交易中,n + 1次的查詢,僅舉幾例。這種方法與實體持久性無知的常見EF工作流程是垂直的。它讓人聯想到活躍的記錄,這是一個完全不同於數據庫+工作單元的數據訪問模式。 –

+0

你也通過繼承將實體直接綁定到實體框架。 – Amy

+0

@Amy,是的,這是真的。這是一個小項目。或者,我可以用戰略/橋樑模式解決這個問題。對CRUD功能的訪問仍然是通過基類。你還注意到了什麼嗎? – toddmo

回答

0

當你讓你的核心基類掛在EF(或任何持久性事物)上時,有一些限制。業務層應該是持久性不可知的。所以,EF甚至不應該成爲業務或數據項目的參考!

這是我最終做的。我從我的基類DatabaseObject中獲得了與CRUD方法相同的好處,並且我使用DI交換了我的持久層。我的EF「加載項」DLL會查看業務層和數據層。它通過後期構建命令部署在垃圾箱中。

EF implements IPersistenceProvider interface

PersistenceProvider.cs

using Atlas.Data.Kernel; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 
using System.Reflection; 
using System; 
using Atlas.Core.Kernel.Extensions; 
using System.ComponentModel.DataAnnotations.Schema; 

namespace Atlas.Data.EntityFramework.Kernel 
{ 
    public class PersistenceProvider<T> : IPersistenceProvider<T> where T : DatabaseObject<T> 
    { 
    public static readonly PersistenceProvider<T> Current = new PersistenceProvider<T>(); 
    public static readonly string DbSetProperyName = typeof(T).Pluralize(); 
    public static readonly PropertyInfo DbSetProperyInfo = typeof(DatabaseContext).GetProperty(DbSetProperyName); 

    // C 
    public void Insert(T item) 
    { 
     DatabaseOperation((databaseContext, collection) => 
     { 
     collection.Add(item); 
     }, 
     item.Inserting, 
     item.Inserted 
    ); 
    } 

    // R 
    public IEnumerable<T> Select(Func<T, bool> predicate = null) 
    { 
     using (var databaseContext = new DatabaseContext()) 
     { 
     DbSet<T> collection = (DbSet<T>)DbSetProperyInfo.GetValue(databaseContext); 
     return predicate != null ? collection.Where(predicate).ToList() : collection.ToList(); 
     } 
    } 

    // U 
    public void Update(T item) 
    { 
     DatabaseOperation((databaseContext, collection) => 
     { 
     collection.Attach(item); 
     MarkModified(databaseContext, item); 
     }, 
     item.Updating, 
     item.Updated 
    ); 
    } 

    // D 
    public void Delete(T item) 
    { 
     DatabaseOperation((databaseContext, collection) => 
     { 
     collection.Attach(item); 
     collection.Remove(item); 
     }, 
     item.Deleting, 
     item.Deleted 
    ); 
    } 

    private void MarkModified(DatabaseContext databaseContext, DatabaseObject<T> efObject) 
    { 
     databaseContext.Entry(efObject).State = efObject.Id != null ? EntityState.Modified : EntityState.Added; 
     foreach (var pi in efObject.GetType().GetProperties().Where(pi => !pi.GetCustomAttributes(typeof(NotMappedAttribute), false).Any() && pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericArguments()[0].IsClass)) 
     { 
     var col = (IEnumerable<T>)pi.GetValue(efObject); 
     if (col != null) 
      foreach (DatabaseObject<T> item in col) 
      MarkModified(databaseContext, item); 
     } 
    } 

    private DatabaseContext databaseContext = null; 
    private void DatabaseOperation(Action<DatabaseContext, DbSet<T>> action, Action executing, Action executed) 
    { 
     bool outerOperation = databaseContext == null; 
     try 
     { 
     if (outerOperation) 
      databaseContext = new DatabaseContext(); 
     executing(); 
     DbSet<T> collection = (DbSet<T>)DbSetProperyInfo.GetValue(databaseContext); 
     action(databaseContext, collection); 
     executed(); 
     databaseContext.SaveChanges(); 
     } 
     finally 
     { 
     if (outerOperation) 
     { 
      databaseContext.Dispose(); 
      databaseContext = null; 
     } 
     } 
    } 

    } 
} 

DatabaseObject.cs

using Microsoft.Practices.Unity; 
using Microsoft.Practices.Unity.Configuration; 
using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.ComponentModel.DataAnnotations.Schema; 
using System.Configuration; 
using System.Linq; 
using System.Web; 

namespace Atlas.Data.Kernel 
{ 
    public class DatabaseObject<T> where T : DatabaseObject<T> 
    { 

    #region Constructors 
    public DatabaseObject() 
    { 
     Id = Guid.NewGuid(); 
    } 
    #endregion 

    #region Fields 

    [Key] 
    [Column(Order = 0)] 
    public Guid Id { get; set; } 

    #endregion 

    // C 
    public virtual void Insert() 
    { 
     PersistenceProvider.Insert((T)this); 
    } 

    // R 
    public static T SingleOrDefault(Guid Id) 
    { 
     return SingleOrDefault(o => o.Id == Id); 
    } 

    public static T SingleOrDefault(Func<T, bool> predicate) 
    { 
     return PersistenceProvider.Select(predicate).SingleOrDefault(); 
    } 

    public static IEnumerable<T> Select(Func<T, bool> predicate = null) 
    { 
     return PersistenceProvider.Select(predicate); 
    } 

    // U 
    public virtual void Update() 
    { 
     PersistenceProvider.Update((T)this); 
    } 

    // D 
    public virtual void Delete() 
    { 
     PersistenceProvider.Delete((T)this); 
    } 


    #region Callbacks 
    public virtual void Deleting() { } 
    public virtual void Deleted() { } 
    public virtual void Inserting() { } 
    public virtual void Inserted() { } 
    public virtual void Updating() { } 
    public virtual void Updated() { } 
    #endregion 

    #region Static Properties 
    private static IPersistenceProvider<T> persistenceProvider; 
    [Dependency] 
    public static IPersistenceProvider<T> PersistenceProvider 
    { 
     get 
     { 
     if(persistenceProvider == null) 
     { 
      var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = HttpContext.Current.Server.MapPath("~/bin/Atlas.Data.Kernel.dll.config") }; 
      Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); 
      var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); 

      var container = new UnityContainer().LoadConfiguration(unitySection); 
      persistenceProvider = container.Resolve<IPersistenceProvider<T>>(); 
     } 
     return persistenceProvider; 
     } 
     set => persistenceProvider = value; 
    } 
    #endregion 
    } 
} 
1
+0

Daffy,OP不建議通用資源庫。 –

+0

@GertArnold我可能誤解了,但是從OP的代碼示例中可以看出,它看起來好像他正朝着這個方向前進。 –

+1

@toddmo我引用了所引用的SO回答:「您不僅在一個功能較少的豐富界面中包裝了一個衆所周知的,經過測試的存儲庫(實體框架),還會人爲地限制消費者的功能,效益。」 –