2016-05-13 130 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回答:“您不仅在一个功能较少的丰富界面中包装了一个众所周知的,经过测试的存储库(实体框架),还会人为地限制消费者的功能,效益。” –