How to map Entity Framework model classes with Business Layer class in n-tier architecture - ASP.NET-MVC

11,488

Solution 1

Your question is more about design/architecture which does not have a 'one-size-fits-all' solution. I can at most share some suggetions and what will I usually do in a fairly typical ASP.NET MVC + Entity Framework stack:

1. Make sure your BLL.User class obeys the Single Responsibility Principle

Your BLL.User class should not concern itself with how to retrieve DAL.User from the database through Entity Framework/Unit of Work. You should simply have another class/layer that will be responsible for that:

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

Then another class to implement 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;
    }
}

Doing so removes the dependency from your BLL.User to the UserManagment_UnitOfWork class and facilitates testing/mocking (i.e. unit tests can be written to mock an in-memory IUserRepository)

Then from your controller, whenever there is a need to retrieve BLL.Users, you simply inject an instance of IUserRepository to the controller's constructor:

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. How to map DAL.User to BLL.User

It's actually quite similar to point number 1, you can simply have another interface/class pair:

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...
         };
    }
}

Or, if you think writing mapping code is tedious, consider using AutoMapper so that your code becomes Mapper.Map<DAL.User, BLL.User>(user)

Bonus points

  1. You can skip those private fields in BLL.User and convert them to auto-properties
  2. You can add attributes that are derived from ValidationAttribute to aid validation in your ASP.NET MVC application
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; }
}

Solution 2

You can simply do this by using AutoMapper Install it via Nuget:

PM> Install-Package AutoMapper

and then all you have to do is to config AutoMapper to map similar objects for you.

Mapper.CreateMap<DAL.User, BLL.User>();

You can convert objects anywhere like this :

BLL.User bll_User = Mapper.Map<DAL.User, BLL.User>(dal_user);

Solution 3

Firs of all there is no need for both Repository Layer and Data Access Layer. Those 2 layers have the same purpose - to add a layer of abstraction for working with your persistent storage. Although they have different concepts

  1. Repository pattern basically provides you with a way to work with your db as if it was an in memory collection. Most of the time it exposes basic CRUD (create read update and delete) methods with some other extensions such as GetAll method and etc. This is a very simplified description but there are enough resources about this pattern for example.
  2. Data Access Layer also adds an abstraction but in much more subjective way. Basically it Contains all the API for accessing you DB. For example for your User class it could have methods such as GetUserByName, GetUserById, RemoveUser, GetAllActiveUsers and etc.

Also Unit of Work doesn't have to be implemented in Repository/DAL. Since a single unit of work can contain modification of multiple entities or requests to external services. In my opinion BLL would be a more suitable place for UoW since in most of the cases you can look at a single business action (a method in BL) as a UoW

Now when this is a little clearer (hopefully). Your Repository/DAL should return only Business entities and not a data base generated classes (in case that they are different) and all the mapping logic should be encapsulated inside the Repository/DAL Layers. For example GetUserById method should return a BLL - User Class and not a User Class generated by EF in this case which can be internal class to the Repository/DAL layer.

For handling mappings between those classes you can use different libraries such as ValueInjecter or AutoMapper which are really great and can make your development much easier.

Share:
11,488
K.Z
Author by

K.Z

Updated on June 04, 2022

Comments

  • K.Z
    K.Z almost 2 years

    I am working on e-tier architecture within MVC framework (ASP.NET MVC5, Entity Framework 6). My application is divided into three sub-projects which are Business-Layer, Data-Access-Layer, Repository (This include repository and Unit of Work) and ASP.NET MVC web-app. I am struggling to understand mapping between business data and entity framework model. for example if I have model class User in entity framework as

    DAL - User Model

    [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; }
    }
    

    and in business layer I have user class

    BLL - User Class

    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();
    
            }           
        }
    }
    

    How I map together and secondly; referring to method GetAllUser(), I still need to use User model class from DAL in order to get all the users from database, where is my current understanding is; each layer should be independent to each other and I have abstraction layer i.e. repository between data access layer and business layer. I am slightly confused of both concept working together. Am I on right track or missing something. also need practical answer on User business layer.