【问题标题】:Generic Wrapper around Generic Repositories using Reflection使用反射的通用存储库的通用包装器
【发布时间】:2014-03-22 19:51:46
【问题描述】:

我正在尝试在 n 层架构中练习松散耦合和分离所有内容,但我坚持 - 我相信 - 基本的东西。我的第一个问题是参考。我经常在程序集之间移动类,因为 A 类需要 B 类,但无法到达它,所以让我们移动 B 类——然后我打破 C 类。

这是我能想到的最好的了。

第 1 步:架构

Project.Data

  • 实体 (POCO)

Project.DataAccess

  • 上下文
  • 迁移
  • 存储库
  • 工作单元
  • 视图模型

Project.Web

  • 控制器
  • 观看次数

第 2 步:使用 Project.DataAccess 作为 Presentation 和 Data 之间的粘合剂

我正在使用工作单元模式,但这需要访问 POCO,因此我无法在控制器中使用 UoW。因此,我认为创建名为ViewModelService 的包装器/服务是一个不错的计划。此服务实例化 UoW 并将 AutoMapped Viewmodels 返回到我的控制器。

然而……

我的 UoW/Repository 模式是通用的,所以我也在尝试使我的服务也通用。

IRepository

public interface IRepository<TObject>
{
    IQueryable<TObject> All();
    IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate);

    IQueryable<TObject> Filter<TKey>(Expression<Func<TObject, bool>> filter,
        out int total, int index = 0, int size = 50);

    bool Contains(Expression<Func<TObject, bool>> predicate);
    TObject Find(params object[] keys);
    TObject Find(Expression<Func<TObject, bool>> predicate);
    TObject Create(TObject t);
    int Delete(TObject t);
    int Delete(Expression<Func<TObject, bool>> predicate);
    int Update(TObject t);
    void Ignore(TObject t);

    int Count { get; }
}

通用 BaseRepository

public class BaseRepository<TObject> : IRepository<TObject>
    where TObject : class
{
    protected AppDbContext Context = null;

    public BaseRepository(AppDbContext context)
    {
        Context = context;
    }

    protected DbSet<TObject> DbSet
    {
        get { return Context.Set<TObject>(); }
    }

    public virtual int Count
    {
        get { return Queryable.Count<TObject>(DbSet); }
    }

   // ... (You get the picture)
}

工作单位

public class UnitOfWork : IDisposable
{
    private readonly AppDbContext _context = new AppDbContext();

    private BaseRepository<Order> _orderRepository;
    private BaseRepository<Product> _productRepository;
    private BaseRepository<ApplicationUser> _userRepository;
    private bool _disposed;

    public BaseRepository<Order> OrderRepository
    {
        get
        {
            if (_orderRepository == null)
            {
                _orderRepository = new BaseRepository<Order>(_context);
            }
            return _orderRepository;
        }
    }

    public BaseRepository<Product> ProductRepository
    {
        get
        {
            if (_productRepository == null)
            {
                _productRepository = new BaseRepository<Product>(_context);
            }
            return _productRepository;
        }
    }

    public BaseRepository<ApplicationUser> UserRepository
    {
        get
        {
            if (_userRepository == null)
            {
                _userRepository = new BaseRepository<ApplicationUser>(_context);
            }
            return _userRepository;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _disposed = true;
    }
}

所以现在 - 没有“服务”和新的 n 层层 - 我在我的控制器中使用它。

 public class ProductController : Controller
{
    private UnitOfWork _unitOfWork = new UnitOfWork();

    // GET: /Product/
    [Route]
    public ActionResult Index()
    {
        var model = _unitOfWork.ProductRepository.All();
        return View(model);
    }

// Etc...

但是现在我将所有内容分成单独的层,我不能这样做。而且我不想让我的 Unit of Work 类来映射 ViewModel。

这是我创建这个“服务”的尝试(甚至不确定它的正确名称):

ViewModelService

 public class ViewModelService : IViewModelService
 {
    private readonly UnitOfWork _unitOfWork = new UnitOfWork();

    public T GetSingle<T>(int key)
    {
        // Get appropriate repository based on T1?

        throw new System.NotImplementedException();
    }

    public IQueryable<T> GetAll<T>()
    {
        throw new System.NotImplementedException();
    }
}

现在我面临一个问题——我如何确保在我打电话时:

_viewModelService.GetSingle&lt;ProductVM&gt;(id);

它自己(通过反射?)计算出它应该调用:

_unitOfWork.ProductRepository.Find(id)

在存储库内部?

哇,我觉得我在解释这一点时做得很糟糕! :)

TL;DR

我有一个 UnitOfWork 类:

 public class UnitOfWork : IDisposable
{
    private readonly DbContext _context = new DbContext();

    private BaseRepository<Order> _orderRepository;
    private BaseRepository<Product> _productRepository;
    private BaseRepository<ApplicationUser> _userRepository;
    private bool _disposed;

    public BaseRepository<Order> OrderRepository
    {
        get
        {
            if (_orderRepository == null)
            {
                _orderRepository = new BaseRepository<Order>(_context);
            }
            return _orderRepository;
        }
    }

    public BaseRepository<Product> ProductRepository
    {
        get
        {
            if (_productRepository == null)
            {
                _productRepository = new BaseRepository<Product>(_context);
            }
            return _productRepository;
        }
    }

    public BaseRepository<ApplicationUser> UserRepository
    {
        get
        {
            if (_userRepository == null)
            {
                _userRepository = new BaseRepository<ApplicationUser>(_context);
            }
            return _userRepository;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _disposed = true;
    }
}

现在我想创建一个通用包装器

public class ViewModelService : IViewModelService
{
    private readonly UnitOfWork _unitOfWork = new UnitOfWork();

    public T GetSingle<T>(int key)
    {
        // Get appropriate repository based on T1?

        throw new System.NotImplementedException();
    }
}

如何使用 reflection 以便当我请求 GetSingle&lt;ProductVM&gt;(id) 时,包装器会将其转换为对 _unitOfWork.ProductRepository.Find(id); 的调用——因此包装器知道调用 UoW 内的正确存储库.

哇哦。

【问题讨论】:

  • 也许你可以创建类似unitOfWork.GetRepository&lt;ProductVM&gt;() 的东西,它将创建所需的类型化存储库。然后在 GetSingle 方法中,您将使用存储库实例,并将 id 保存到本地字典?

标签: c# generics design-patterns reflection n-tier-architecture


【解决方案1】:

其中一种方法是拥有一个通用抽象类,并让具体的子类覆盖这个提供实际存储库的特定部分。这样,大部分代码都可以放在抽象类中,并且仍然可以精确地提供子类级别所需的内容。

 // two generic types
 // TDTO - view model type
 // TDomain - domain type
 public abstract class AbstractViewModelService<TDTO, TDomain> : IViewModelService
 {
     private UnitOfWork _uow { get; }        

     public AbstractViewModelService( UnitOfWork uow )
     {
         this._uow = uow;
     }

     public abstract IRepository<TDomain> Repository { get; }

     public TDTO GetSingle<TDTO>(int key)
     {
         // Get appropriate repository based on T1?
         // it is here ->

         return Mapper.DynamicMap<TDTO>( this.Repository.Find( key ) );
     }
 }

 public class UserViewModelService : AbstractViewModelService<UserDto, User> 
 {
     public override IRepository<User> Repository
     {
         get
         {
             return this._uow.UserRepository;
         }
     }
     ...
 }

 public class AccountViewModelService : AbstractViewModelService<AccountDto, Account> 
 {
     public override IRepository<Account> Repository
     {
         get
         {
             return this._uow.AccountRepository;
         }
     }
     ...
 }

【讨论】:

  • 这不会在我的控制器与 UserViewModelService 和其他服务之间创建紧密耦合吗?
  • 还有其他方法吗?您可以抽象所有这些服务并使它们可注入,但我认为这没有意义。这就是您拥有存储库的原因 - 抽象数据库层。抽象更高层听起来有点矫枉过正。
  • 我同意这种矫枉过正。我正在抽象存储库,因为它从我的控制器无权访问的数据层返回实体。所以我尝试创建一个返回视图模型的中间人。我在哪里改变我的设计?我需要将我的实体暴露给我的表示层吗?
  • 你的设计没问题,抽象存储库没问题,有一个具体的服务层也没问题。不必公开域实体,这是您创建将域映射到 dto 的服务层的方向,这没关系。
【解决方案2】:

为什么不使用 MEF? 打破你的 UOW 它将组成存储库 - 你也将受益于你不会有重复的

get
    {
        if (_productRepository == null)
        {
            _productRepository = new BaseRepository<Product>(_context);
        }
        return _productRepository;
    }

将元数据添加到每个导出项目,然后在

 public T GetSingle<T>(int key)
{
    // Get appropriate repository based on T1?

    throw new System.NotImplementedException();
}

您可以根据它公开的元数据获取正确的插件。 或者您可以将所有存储库添加到 ServiceLoactor,这样您就可以编写 而不是

_unitOfWork.ProductRepository.Find(id);

ServiceLocator.Current.GetInstance<T>().find(id)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-13
    相关资源
    最近更新 更多