如何在n层架构中将Entity Framework模型类映射到业务层类 - ASP.NET-MVC

5

我正在使用MVC框架(ASP.NET MVC5,Entity Framework 6)中的e-tier架构进行开发。我的应用程序分为三个子项目,它们是业务层、数据访问层、存储库(包括存储库和工作单元)和ASP.NET MVC Web应用程序。我很难理解业务数据和实体框架模型之间的映射。例如,如果我在实体框架中有一个模型类“用户”

DAL - 用户模型

[Table("User")]
public class User
{
    public User() { }

    [Key]
    public int UserID { get; set; }

    [StringLength(250)]
    [Required]
    public string FirstName { get; set; }

    [StringLength(250)]
    [Required]
    public string LastName { get; set; }

    [Required]
    public int Age { get; set; }

    [StringLength(250)]
    [Required]
    public string EmailAddress { get; set; }

    public ICollection<UserInGroup> UserInGroup { get; set; }
}

在业务层面,我有一个用户类。

BLL - 用户类

public class User
{
    private string userID;
    private string firstName;
    private string lastName; 
    private int age;
    private string emailAddress;


    public string UserID
    {
        get { return userID; }
        set { userID = value; }
    }

    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }

    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }

    public string EmailAddress
    {
        get { return emailAddress; }
        set { emailAddress = value; }
    }

    public void GetAllUser()
    {
        List<App.DAL.Model.User> _user = new List<DAL.Model.User>();

        using (var _uow = new UserManagement_UnitOfWork())
        {

            _user = (from u in _uow.User_Repository.GetAll()
                     select u).ToList();

        }           
    }
}

我如何将它们映射在一起?其次,对于GetAllUser()方法,我仍然需要使用DAL中的User模型类才能从数据库中获取所有用户,我的当前理解是:每个层应该相互独立,并且我有一个抽象层即存储库,位于数据访问层和业务层之间。我对这两个概念如何协同工作略感困惑。我是否正确理解或者遗漏了某些内容?也需要关于User业务层的实际答案。


1
如果您没有要添加的内容,为什么不直接使用原始类呢?此外,GetAllUser() 应该是静态的或者放在其他地方,因为一个用户对象返回所有用户是没有意义的。 - Sami Kuhmonen
当然,这些层不是独立的 - 这个想法曾经是 Layer-n 只知道 Layer-(n-1)(或所有下面的层) - 无论如何,架构取决于您的用例的大小 - 当您只是在做 TODO-list 时将其抽象化为一切都是过度的。话虽如此:您的模型可能应该为 DAL 和 BLL 所知 - 这样,您的 Repositories 就可以使用 BLL 实际使用的模型(这是我实现它的方式)。 - Random Dev
3个回答

8

您的问题更多与设计/架构有关,没有“一刀切”的解决方案。我最多只能分享一些建议,并在一个相当典型的ASP.NET MVC + Entity Framework堆栈中通常会做什么:

1. 确保您的BLL.User类遵守单一职责原则

您的BLL.User类不应涉及如何通过Entity Framework / Unit of Work从数据库中检索DAL.User。您应该有另一个负责此操作的类/层:

public interface IUserRepository
{
    IEnumerable<User> GetAllUsers();
}

接下来需要实现另一个类来实现 IUserRepository 接口:

public class UserRepository : IUserRepository
{
    private readonly UserManagement_UnitOfWork _unitOfWork;

    public UserRepository(UserManagement_UnitOfWork unitOfWork)
    {
         _unitOfWork = unitOfWork;
    }

    public IEnumerable<User> GetAllUsers()
    {
        return from u in _unitOfWork.User_Repository.GetAll()
               select u;
    }
}

这样做可以消除你的BLL.UserUserManagment_UnitOfWork类的依赖,方便测试/模拟(例如可以编写单元测试以模拟内存中的IUserRepository)。

然后从控制器中,每当需要检索BLL.Users时,只需将IUserRepository的实例注入到控制器的构造函数中即可:

public class UserController
{
    private readonly IUserRepository _userRepository;

    public UserController(IUserRepository userRepository)
    {
         _userRepository = userRepository;
    }

    public ActionResult Index()
    {
        // Simple example using IEnumerable<BLL.User> as the view model
        return View(_userRepository.GetAllUsers().ToList());
    }
}

2. 如何将 DAL.User 映射到 BLL.User

其实和第一点很相似,你可以简单地拥有另一组接口/类:

public interface IUserMapper
{
     BLL.User MapUser(DAL.User);
}

public class UserMapper : IUserMapper
{
    public BLL.User MapUser(DAL.User user)
    {
         return new BLL.User
         {
             FirstName = user.FirstName,
             LastName = user.LastName,
             Age = user.Age
             // etc...
         };
    }
}

如果你认为编写映射代码很繁琐,考虑使用AutoMapper,这样你的代码就变成了Mapper.Map<DAL.User, BLL.User>(user)

加分项

  1. 你可以跳过BLL.User中的那些private字段,并将它们转换为自动属性
  2. 你可以添加从ValidationAttribute派生的属性,以帮助在ASP.NET MVC应用程序中进行验证
public class User
{
    public string UserId { get; set; }

    [Required]
    public string FirstName { get; set; }

    public string LastName { get; set; }

    [Range(0, int.MaxValue)]
    public int Age { get; set; }

    [EmailAddress]
    public string EmailAddress { get; set; }
}

5
您可以通过使用AutoMapper来轻松实现这一点。 通过Nuget安装它:
PM> Install-Package AutoMapper

然后你所要做的就是配置AutoMapper,让它为你映射类似的对象。
Mapper.CreateMap<DAL.User, BLL.User>();

您可以像这样在任何地方转换对象:
BLL.User bll_User = Mapper.Map<DAL.User, BLL.User>(dal_user);

4
首先,没有必要同时使用Repository Layer和Data Access Layer。这两个层的目的相同——为使用持久化存储提供一个抽象层。尽管它们有不同的概念
1. Repository模式基本上为您提供了一种使用数据库的方式,就像它是内存集合一样。大多数情况下,它会公开基本的CRUD(创建、读取、更新和删除)方法,还有一些其他扩展,例如GetAll方法等。 这是一个非常简化的描述,但有足够的资源介绍这个模式 例如 2. 数据访问层也以更主观的方式添加了一个抽象层。基本上,它包含用于访问数据库的所有API。例如,对于您的User类,它可以具有诸如GetUserByNameGetUserByIdRemoveUserGetAllActiveUsers等方法。
也就是说,工作单元不一定要在存储库/数据访问层中实现。由于单个工作单元可以包含多个实体的修改或对外部服务的请求,因此在我的看法中,业务逻辑层更适合UoW,因为在大多数情况下,您可以将单个业务操作(BL中的方法)视为UoW。
现在,当这一点变得更加清晰时(希望如此),您的存储库/数据访问层应仅返回业务实体,而不是由数据库生成的类(如果它们不同),并且所有映射逻辑都应封装在存储库/数据访问层中。例如,GetUserById方法应返回BLL- User类,而不是在这种情况下由EF生成的User类,后者可以是存储库/数据访问层的内部类。
为了处理这些类之间的映射,您可以使用不同的库,例如ValueInjecterAutoMapper,它们非常好用,可以使您的开发更加轻松。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接