我在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<File>();
this.Folders = new HashSet<Folder>();
}
//...
}
}
现在,我想创建业务逻辑对象,然后使用数据访问对象映射它们:
namespace Project1.Logic
{
public class User
{
public int Id { get; set; }
}
}
我在数据库中有非常少量的表。我需要使用Automapper
吗?如果不是,我该如何实现映射?
答案 0 :(得分:9)
我一直使用EF6从MSSQL表生成我的数据访问层,然后创建一组对象来表示我希望如何与我的代码(或显示它)进行POCO交互。 &#34;映射&#34;通过实现存储库模式来处理。下面是一个通用界面,可以帮助我确保所有的repo类都遵循相同的形状。
public interface IDataRepository<T>
{
IQueryable<T> Get();
T Get(int id);
T Add(T obj);
T Update(T obj);
void Delete(T obj);
}
然后,我创建这样的repo类。 (使用您的UserBusiness和UserDAL类)
public class NewRepo : IDataRepository<UserBusiness>
{
YourContext db = new YourContext();
public IQueryable<UserBusiness> 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。
最后,我经常最终添加另一层服务类&#39;与我的回购交互,管理商业类的内部数据...或者你可以使你的商务课更聪明&#39;通过向他们添加repo方法。我倾向于保持POCO的愚蠢并构建服务类来获取,设置和编辑属性。
是的,有一堆左右映射到&#34;转换&#34;一个类到另一个类,但它是后来内部业务逻辑类的清晰分离。 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<TDAL, TBLL> : IRepository<TBLL>
where TDAL : BaseEntityDAL, new()
where TBLL : BaseEntity, new()
{
protected readonly MyDbContext context;
protected readonly DbSet<TDAL> dbSet;
protected virtual TDAL Map(TBLL obj)
{
Mapper.CreateMap<TBLL, TDAL>();
return Mapper.Map<TDAL>(obj);
}
protected virtual TBLL Map(TDAL obj)
{
Mapper.CreateMap<TDAL, TBLL>();
return Mapper.Map<TBLL>(obj);
}
protected abstract IQueryable<TBLL> GetIQueryable();
public BaseRepository(MyDbContext context, DbSet<TDAL> 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<TBLL> 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<ContractDAL, Contract>
{
protected override IQueryable<Contract> 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)
{
}
}
答案 1 :(得分:1)
如果您的项目相对较小,我建议您根本不使用DTO - 相反,您可以使用Entity Framework Code First并在多个层中重用您的业务实体(只需确保将Code First实体放置到某个公共库中) )。
否则,您可以创建自己的转换方法或使用AutoMapper等库。
答案 2 :(得分:1)
如果要在Business Model Design中使用Plain Old Clr Objects并将它们映射到数据库表,则可以使用Code First Approach for Entity Framework。 在Code中,首先不会为您生成任何内容。但是,您将负责将Business Objects映射到数据库表和字段。您可以通过两种方式进行基础测试:
这两种方法将为您生成相同的映射,但我更喜欢Fluent Api方法,因为它提供了更强的映射API,并使您的BO独立于将集中在您的datacontext中的任何映射逻辑。
但是..一旦你生成了类,这些将被绑定并映射给你,这是数据库的第一个方法。因此,您可以扩展这些类,因为它们是部分的。 您可以在此博客中找到有关在EF上制作的不同工作流程的详细信息,这些工作流程将帮助您根据需要使用正确的工作流程:http://blog.smartbear.com/development/choosing-the-right-entity-framework-workflow/
答案 3 :(得分:0)
如果您的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
对象的属性,会发生什么?您必须在每个实例中,在每个层中,在整个应用程序中进行更改(紧密耦合),并且首先要破坏DAL和BLL的目的。
这是一个使用泛型和接口非常有用的实例。因此,给定对象User
示例:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
...你可以创建一个通用的&#34;存储库&#34;从数据访问接口继承的对象的类或其他数据访问方法(DAL):
public class DALMethods<T> : IDALMethods<T> where T : class
{
private UserContext _db;
private DbSet<T> _set;
public DALMethods(UserContext db)
{
_db = db;
_set = _db.Set<T>();
}
public void Create(T entity)
{
_set.Add(entity);
_db.SaveChanges();
}
//... Expressly dispose context method needed.
}
..然后您的 BLL 会关注User
业务逻辑:
public class UserBLL : IBLLMethods<User>
{
private DALMethods<User> _repository;
private UserContext _db;
public UserBLL()
{
_db = new UserContext();
_repository = new DALMethods<User>(_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<T>
之类的接口来强制实施约束,或者使用IoC容器进一步解耦代码。
HTH