如何将entity framework中的数据访问映射到业务逻辑对象

我在ASP.NET C#MVC应用程序中使用Entity Framework。

我在数据访问层中有EF生成的对象:

namespace Project1.DataAccess { using System; using System.Collections.Generic; public partial class User { public User() { this.Files = new HashSet(); this.Folders = new HashSet(); } //... } } 

现在,我想创建业务逻辑对象,然后使用数据访问对象映射它们:

 namespace Project1.Logic { public class User { public int Id { get; set; } } } 

我在数据库中有很少的表。 我需要使用Automapper吗? 如果不是,我该如何实现映射?

我一直使用EF6从MSSQL表生成我的数据访问层,然后创建一组对象来表示我想要如何与我的代码(或显示它)进行交互,这些对象是POCO。 通过实现Repository模式来处理“映射”。 下面是一个通用界面,可以帮助我确保所有的repo类都遵循相同的形状。

  public interface IDataRepository { IQueryable Get(); T Get(int id); T Add(T obj); T Update(T obj); void Delete(T obj); } 

然后,我创建这样的repo类。 (使用您的UserBusiness和UserDAL类)

 public class NewRepo : IDataRepository { YourContext db = new YourContext(); public IQueryable Get() { return (from u in db.UserDAL select new UserBusiness() { Id = u.Id, Name = u.Name }); } public UserBusiness Get(int id) { return (from u in db.UserDAL where u.Id == id select new UserBusiness() { Id = u.Id, Name = u.Name }).FirstOrDefault(); } public Order Add(UserBusiness obj) { UserDAL u= new UserDAL(); u.Name = obj.Name; db.UserDAL.Add(u); db.SaveChanges(); //Assuming the database is generating your Id's for you obj.Id = u.Id; return obj; } public Order Update(UserBusiness obj) { UserDAL u= new UserDAL(); u.Id = obj.Id; u.Name = obj.Name; db.Entry(u).State = EntityState.Modified; db.SaveChanges(); return obj; } public void Delete(UserBusiness obj) { UserDAL u = db.UserDAL .Where(o=>o.Id == obj.Id) .FirstOrDefault(); if (u!=Null) { db.Entry(u).State = EntityState.Deleted; db.SaveChanges(); } } } 

在您的应用程序中,您现在使用repo类的方法而不是DBContext。

最后,我经常最后添加另一层“服务类”,它与我的repos交互,管理Business类的内部数据……或者你可以通过向它们添加repo方法使你的Business类更“智能”。 我倾向于保持POCO的愚蠢并构建服务类来获取,设置和编辑属性。

是的,有一堆左右映射将一个类“转换”为另一个类,但它是内部业务逻辑类的清晰分离,以供日后使用。 POCO转换的直接表一开始看起来很傻,但只要等到DBA想要规范化几个字段,或者你决定向这些简单对象添加一个集合。 能够管理您的业务对象而不打破其余的应用程序是无价的。


编辑:下面是存储库的通用版本,这使得创建新存储库变得更加容易。

这是所有业务逻辑层类的基类:

 public class BaseEntity { public int Id { get; set; } } 

这是所有数据访问层类的基类:

 public class BaseEntityDAL { [Key] [Column("Id")] public int Id { get; set; } } 

这是存储库的通用基类(注意,我们在这里使用AutoMapper):

 public abstract class BaseRepository : IRepository where TDAL : BaseEntityDAL, new() where TBLL : BaseEntity, new() { protected readonly MyDbContext context; protected readonly DbSet dbSet; protected virtual TDAL Map(TBLL obj) { Mapper.CreateMap(); return Mapper.Map(obj); } protected virtual TBLL Map(TDAL obj) { Mapper.CreateMap(); return Mapper.Map(obj); } protected abstract IQueryable GetIQueryable(); public BaseRepository(MyDbContext context, DbSet dbSet) { if (context == null) throw new ArgumentNullException(nameof(context)); if (dbSet == null) throw new ArgumentNullException(nameof(dbSet)); this.context = context; this.dbSet = dbSet; } public TBLL Get(int id) { var entity = dbSet .Where(i => i.Id == id) .FirstOrDefault(); var result = Map(entity); return result; } public IQueryable Get() { return GetIQueryable(); } public TBLL Add(TBLL obj) { var entity = Map(obj); dbSet.Add(entity); context.SaveChanges(); obj.Id = entity.Id; return obj; } public TBLL Update(TBLL obj) { var entity = Map(obj); context.Entry(entity).State = EntityState.Modified; context.SaveChanges(); return obj; } public void Delete(TBLL obj) { TDAL entity = dbSet .Where(e => e.Id == obj.Id) .FirstOrDefault(); if (entity != null) { context.Entry(entity).State = EntityState.Deleted; context.SaveChanges(); } } } 

最后,当我们完成上述所有操作时,这是存储库的示例实现,非常干净:

 public class ContractRepository : BaseRepository { protected override IQueryable GetIQueryable() { return dbSet .Select(entity => new Contract() { // We cannot use AutoMapper here, because Entity Framework // won't be able to process the expression. Hence manual // mapping. Id = entity.Id, CompanyId = entity.CompanyId, ProjectId = entity.ProjectId, IndexNumber = entity.IndexNumber, ContractNumber = entity.ContractNumber, ConclusionDate = entity.ConclusionDate, Notes = entity.Notes }); } public ContractRepository(MyDbContext context) : base(context, context.Contracts) { } } 

如果您的项目相对较小,我建议您根本不使用DTO – 相反,您可以使用Entity Framework Code First并跨多个层重用您的业务实体(只需确保将Code First实体放置到某个公共库中)。

否则,您可以创建自己的转换方法或使用AutoMapper之类的库。

如果要在业务模型设计中使用普通的旧Clr对象并将它们映射到数据库表,则可以使用Code First Approach for Entity Framework。 在Code中,首先不会为您生成任何内容。 但是,您将负责将Business Objects映射到数据库表和字段。 您可以通过两种方式进行基础测试:

  • Fluent API使用这种方法,您将在datacontext对象中定义Object和关系实体之间的关系:这是一个来自msdn的例子http://msdn.microsoft.com/en-us/data/jj591620.aspx
  • 现在使用这种方法,您将使用Data Annotations将您的类映射到数据库实体,Data Annotations将成为Business Objects本身的一部分:例如: http : //www.entityframeworktutorial.net/code-first/dataannotation-in-代码first.aspx

这两种方法将为您生成相同的映射,但我更喜欢Fluent Api方法,因为它提供了更强的映射API,并使您的BO独立于将集中在您的datacontext中的任何映射逻辑。

但是..一旦你生成了类,这些将被绑定并映射给你,这是数据库的第一个方法。 因此,您可以扩展这些类,因为它们是部分的。 您可以在此博客中找到有关EF上不同工作流程的详细信息,这些工作流程将帮助您根据需要使用正确的工作流程: http : //blog.smartbear.com/development/choosing-the-right-entity-framework-workflow /

如果您的DAL和BLL User对象完全相同 ,您可以使用这样的函数进行映射:

 public void SetProperties(object source, object target) { var type = target.GetType(); foreach (var prop in source.GetType().GetProperties()) { var propGetter = prop.GetGetMethod(); var propSetter = type.GetProperty(prop.Name).GetSetMethod(); var valueToSet = propGetter.Invoke(source, null); propSetter.Invoke(target, new[] { valueToSet }); } } 

但是,为什么你需要在DAL和BLL中User不同但又完全相同的User对象? 如果您需要更改User对象的属性会发生什么? 您必须在每个实例中,在每个层中,在整个应用程序中进行更改(紧密耦合),并且首先要破坏DAL和BLL的目的。

这是一个使用generics和接口非常有用的实例。 所以,给定对象User示例:

 public class User { public int Id { get; set; } public string Name { get; set; } } 

…您可以为从数据访问接口(DAL)inheritance的对象创建通用的“存储库”类或其他一些数据访问方法:

 public class DALMethods : IDALMethods where T : class { private UserContext _db; private DbSet _set; public DALMethods(UserContext db) { _db = db; _set = _db.Set(); } public void Create(T entity) { _set.Add(entity); _db.SaveChanges(); } //... Expressly dispose context method needed. } 

..然后你的BLL只会关注User业务逻辑:

 public class UserBLL : IBLLMethods { private DALMethods _repository; private UserContext _db; public UserBLL() { _db = new UserContext(); _repository = new DALMethods(_db); } public bool CreateUserIfNameIsBob(User user) { // Create bob if bob if (user.Name == "Bob") { _repository.Create(user); return true; } // Not bob return false; } } 

上面的例子是有目的的通用,但我认为它们说明了这一点。 如果您的User对象发生更改,则不会阻止BLL和DAL图层工作。 您可以使用IDALMethods类的接口来强制实施约束,或者使用IoC容器进一步解耦代码。

HTH