Multiple generic repositories in unitofwork?

后端 未结 3 1321
南笙
南笙 2021-01-07 05:38

Lets say I have 2 tables. ProductCategory and Product. I have 1 generic repository that can handle both tables:

public class Generi         


        
3条回答
  •  离开以前
    2021-01-07 05:57

    Demonstrating a solution with only one class would be

    public class Session : ISession
    {
        private readonly DbContext _dbContext;
        public Session(DbContext dbContext)
        {
            _dbContext = dbContext;
        }
    
        public TEntity Single(Expression> expression) where TEntity : class
        {
            return _dbContext.Set().SingleOrDefault(expression);
        }
    
        public IQueryable Query() where TEntity : class
        {
            return _dbContext.Set().AsQueryable();
        }
    
        public void Commit()
        {
            try { _dbContext.SaveChanges(); }
            catch (DbEntityValidationException ex)
            {
                var m = ex.ToFriendlyMessage();
                throw new DbEntityValidationException(m);
            }
        }
    
        public void Dispose()
        {
            _dbContext.Dispose();
        }
    
        public void Add(IEnumerable items) where TEntity : class
        {
            items.ToList().ForEach(Add);
        }
    
        public void Add(TEntity item) where TEntity : class
        {
            _dbContext.Set().Add(item);
        }
    
        public void Remove(TEntity item) where TEntity : class
        {
            _dbContext.Set().Remove(item);
        }
    
        public void Remove(Expression> expression) where TEntity : class
        {
            var items = Query().Where(expression);
            Remove(items);
        }
    
        public void Remove(IEnumerable items) where TEntity : class
        {
            items.ToList().ForEach(Remove);
        }
    }
    

    and then your usage can be

    public class User
    {
        public int? Id { get; set; }
        public string Name { get; set; }
        public DateTime Dob { get; set; }
    }
    public class Usage
    {
        private readonly ISession _session;
        public Usage(ISession session) { _session = session; }
    
        public void Create(User user)
        {
            _session.Add(user);
            _session.Commit();
        }
        public void Update(User user)
        {
            var existing = _session.Single(x => x.Id == user.Id);
    
            // this gets cumbursome for an entity with many properties. 
            // I would use some thing like valueinjecter (nuget package)
            // to inject the existing customer values into the one retreived from the Db.
            existing.Name = user.Name;
            existing.Dob = user.Dob;
    
            _session.Commit();
        }
    }
    

    I have deliberately not included a Repository class. To have a class encapsulate both queries and commands for every entity is an over kill and a needless abstraction. Its almost a design flaw at a fundamental level. Queries and commands are fundamentally different concerns. Queries in the most simplest manner can be created as extensions methods on the ISession interface. Commands can be done using a few classes like such..

    public interface ICommand
    {
        void ApplyTo(TSource source);
    }
    public interface ICommandHandler
    {
        void Handle(ICommand command);
    }
    public class LinqCommandHandler : ICommandHandler
    {
        private readonly ISession _session;
    
        public LinqCommandHandler(ISession session)
        {
            _session = session;
        }
        public void Handle(ICommand command)
        {
            command.ApplyTo(_session);
            _session.Commit();
        }
    }
    public class UpdateDobForUserName : ICommand
    {
        public string UserName { get; set; }
        public DateTime Dob { get; set; }
        public void OnSend(IStore store)
        {
            var existing = store.Query().SingleOrDefault(x => x.Name == UserName);
            existing.Dob = Dob;
        }
    }
    
    public class Usage
    {
        private readonly ICommandHandler _commandHandler;
    
        public Usage(ICommandHandler commandHandler)
        {
            _commandHandler = commandHandler;
        }
    
        public void Update()
        {
            var command = new UpdateDobForUserName {UserName = "mary", Dob = new DateTime(1960, 10, 2)};
            _commandHandler.Handle(command);
        }
    }
    

    The IStore above is the same as the Session class, except that it doesn't implement the IDisposable interface and doesn't have a Commit() method. The ISession then obviously inherits from an IStore and also implements IDisposable and has one method Commit(). This ensures an ICommand can never open or dispose connections and cannot commit. Its responsibility is to define a command and define how its applied. Who applies it and what happens and what not on command application is a different responsibility which is with the ICommandHandler.

提交回复
热议问题