我是EF的新手,但我已经编程了21年。我喜欢干燥和通用的东西,但我刚刚做的事情似乎有些不对,但是我不能指责它。
EF上看到的每个例子都有开发人员为每个POCO类创建4个单独的CRUD方法。所以我打算不必这样做,这就是我提出的:
型号:
public class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
public virtual DbSet<Member> Members { get; set; }
}
所有业务层的基类:
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();
}
}
}
}
Generic Collection类:
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)通过主键从中找到匹配的对象db(它不再是同一个对象!),4)通过反射更新它,然后5)保存更改。所以有两个对象涉及的不是一个和反射。我想我必须“放开”连接以保留原始对象一旦得到它,我不知道如何解决这个问题。
答案 0 :(得分:1)
这是一种常见的反模式(出于多种原因)。
EF已经实现了UoW和存储库模式,因此,实际上,您是在抽象上创建抽象。
请参阅以下文章,了解为何不好:
答案 1 :(得分:0)
当您使核心基类与EF(或任何持久性事物)结合时,存在一些限制。业务层应该是持久性不可知的。因此,EF甚至不应该是业务或数据项目的参考!
这就是我最终做的事情。我从基类DatabaseObject
中获得了与CRUD方法相同的好处,并且我可以用DI替换我的持久层。我的EF“插件”dll可以看到业务和数据层。它通过构建后命令部署在bin中。
<强> 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
}
}