简体   繁体   中英

Implement webapi with Repository pattern, unit of work and autofac dependency injection

started working on web api with Repository pattern, unit of work and Autofac dependency injection.

Architecture will be like,

  1. Web API as service.

  2. Business layer (Class library) - Business logic like convert entity into DTO.

  3. Data Access Layer (Class Library) - Repository and UOW(unit of work).

  4. Data (Class Library)- Implementing Entity frame work.

I created the generic repository and extended with specific repository, each repository will be having UOW. UOW is injected in repository by constructor.

Question: From BAL, Repository will be injected by constructor and assigned into local field. Should i create UOW and pass it as input parameter or autofac will take care of this.

Repository,

public class Repository<TEntity>:IRepository<TEntity> where TEntity:class
    {
        protected readonly DbContext Context;
        public Repository(IUnitOfWork unitOfWork)
        {
            Context = unitOfWork.TMSContext;
        }

        public TEntity Get(int id)
        {            
            return Context.Set<TEntity>().Find(id);
        }

        public IEnumerable<TEntity> GetAll()
        {            
            return Context.Set<TEntity>().ToList();
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return Context.Set<TEntity>().Where(predicate);
        }

        public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
        {
            return Context.Set<TEntity>().SingleOrDefault(predicate);
        }

        public void Add(TEntity entity)
        {
            Context.Set<TEntity>().Add(entity);
        }

        public void AddRange(IEnumerable<TEntity> entities)
        {
            Context.Set<TEntity>().AddRange(entities);
        }

        public void Remove(TEntity entity)
        {
            Context.Set<TEntity>().Remove(entity);
        }

        public void RemoveRange(IEnumerable<TEntity> entities)
        {
            Context.Set<TEntity>().RemoveRange(entities);
        }
    }
}

Unit of Work,

public class UnitOfWork:IUnitOfWork
    {
        private DbContext _dbContext;
        private DbContextTransaction _objTran;

        protected IDbFactory DbFactory
        {
            get;
            private set;
        }

        DbContext IUnitOfWork.TMSContext
        {
            get { return _dbContext ?? (_dbContext = DbFactory.Init()); }
        }

        public UnitOfWork(IDbFactory dbFactory)
        {
            DbFactory = dbFactory;
        }
        public int SaveCahnges()
        {
            return _dbContext.SaveChanges();
        }
        public void Dispose()
        {
            Dispose(true);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _dbContext.Dispose();
            }
        }

        public void BeginTransaction()
        {
            _objTran = _dbContext.Database.BeginTransaction();
        }

        public void Commit()
        {
            _objTran?.Commit();
        }

        public void Rollback()
        {
            _objTran?.Rollback();
            _objTran?.Dispose();
        }
    }

UserRepository Inherits Repository class,

public class UserRepository : Repository<User>, IUserRepository

{

IUnitOfWork _unitOfWork;

    public UserRepository(IUnitOfWork unitOfWork):base(unitOfWork)

    {

            _unitOfWork = unitOfWork;
        }

        public bool ValidateUser(string userName, string password)
        {            
            var res = SingleOrDefault(x => x.UserName == userName && x.UserPwd == password);
            return res != null;
        }

    }

In BAL implementation,

public class UserManager : IUserManager
    {
        private IUserRepository _userRepository;
    private IAddressRepository _addressRepository;
    private IUnitOfWork _unitOfWork;
        public UserManager(IUnitOfWork unitOfWork, IUserRepository userRepository, IAddressRepository addressRepository)
        {
            _unitOfWork = unitOfWork;
        _userRepository = userRepository;
        _addressRepository = addressRepository;
        }
        public bool ValidateUser(string userName, string password)
        {
            return _userRepository.ValidateUser(userName, password);
        }
public void SaveUser(UserDTO userDTO, AddressDTO addressDTO)
        {
            try
            {
                _unitOfWork.BeginTransaction();
                _userRepository.Add(ConvertToUser(userDTO));
                _unitOfWork.SaveCahnges();
                _addressRepository.Add(ConvertToAddress(addressDTO));
                _unitOfWork.SaveCahnges();

                _unitOfWork.Commit();
            }
            catch (Exception)
            {
                _unitOfWork.Rollback();
            }
        }
    }

WebAPI Controller,

public class UserController : ApiController
    {
        IUserManager _userManager;
        public UserController(IUserManager userManager)
        {
            _userManager = userManager;
        }
        [HttpGet]
        public bool ValidateUser(string userName, string password)
        {
            return _userManager.ValidateUser(userName, password);
        }
    }

AutoFac,

public class Bootstrapper
    {
        public static void Run()
        {
            //Configure AutoFac  
            AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
        }
    }

private static IContainer RegisterServices(ContainerBuilder builder)
        {
            //Register your Web API controllers.  
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            builder.RegisterType<TMSDataEntities>()
                   .As<DbContext>()
                   .InstancePerRequest();

            builder.RegisterType<DbFactory>()
                   .As<IDbFactory>()
                   .InstancePerRequest();

            //Set the dependency resolver to be Autofac.  
            Container = builder.Build();

            return Container;
        }

from above example, UserRepository will be injected from constructor in UserManager BAL,

  1. Should I pass UOW as another parameter or parameter "UserRepository" will have it by default.

  2. How it will handle suppose if inject two reository.

  3. BAL SaveUser method is trying to Add values of two different entity using UOW, is this implementation correct or any modification is required?

  1. BAL SaveUser method is trying to Add values of two different entity using UOW, is this implementation correct or any modification is required?

You can use UnitOfWork in the generic repository instead of the DbContext. In this case, you will have always access to UOW from each repository class.

Backing to the question, UOW does not refer only to one entity but to whole dbContext and SaveChanges() method save changes on all entities in your DbContext. In the SaveUser() method you don't need to call the SaveChanges() method from UOW twice times. You will have to remember to call SaveChanges() before commit transactions.

Your generic repository class should look like:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected IUnitOfWork UnitOfWork { get; }

    public Repository(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    // ...

 }

and your UserManager class:

public class UserManager : IUserManager
{
    private IUserRepository _userRepository;
    private IAddressRepository _addressRepository;

    public UserManager(IUserRepository userRepository, 
       IAddressRepositoryaddressRepository)
    {
        _userRepository = userRepository;
        _addressRepository = addressRepository;
    }

    public bool ValidateUser(string userName, string password)
    {
        return _userRepository.ValidateUser(userName, password);
    }

    public void SaveUser(UserDTO userDTO, AddressDTO addressDTO)
    {
        try
        {
            // Is doesn't matter if you use _userRepository or _addressRepository,
            // because this classes use the same UnitOfWork.
            _userRepository.UnitOfWork.BeginTransaction();

            _userRepository.Add(ConvertToUser(userDTO));
            _addressRepository.Add(ConvertToAddress(addressDTO));

            _userRepository.UnitOfWork.SaveCahnges();
            _userRepository.UnitOfWork.Commit();
        }
        catch (Exception)
        {
            // Todo - log exception 
            _userRepository.UnitOfWork.Rollback();
        }
    }
}
  1. Should I pass UOF as another parameter or parameter "UserRepository" will have it by default.

Autofac will take care of creating all required instances with the correct dependency graph. You don't have to do anything special.

  1. How it will handle suppose if inject two repositories.

Autofac will dispose all instances it creates based on the specified lifetime scope scope.

When you use InstancePerRequest Autofac will create the required instance for each new request and automatically disposed all created instance when the request end.

See Lifetime scope from the autofac documentation to better understand lifetime scope.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM