在仓储模式中,我应该在哪里实现Distinct方法?

4

我已经建立了标准的仓库接口:

public interface IRepository<T, TKey> : IDisposable where T : class
{
    T Get(TKey id);
    IEnumerable<T> Get();
    IEnumerable<T> Find(Expression<Func<T, bool>> whereClause);
    T Add(T entity);
    void Delete(T entity);
    bool Save();
    int Update(T entity);
}

这是一个与之对应的实现:

public class EfRepository<T, TKey> : IRepository<T, TKey> where T : class
{
    protected DbContext _context;

    public EfRepository(DbContext context) { ...}

    public virtual T Get(TKey id) {...}
    public virtual IEnumerable<T> Get() { ...}

    public virtual IEnumerable<T> Find(Expression<Func<T, bool>> whereClause)  { ...}
    public T Add(T entity) { ...}
    public void Delete(T entity) { ...}
    public bool Save() { ...}
    public int Update(T entity) { ...}
}

我有一个服务层位于所有这些之上:

public class VehicleService: IVehicleService
{
    private readonly IRepository<Vehicle, int> _repository;

    public VehicleService(IRepository<Vehicle, int> repository)
    {
        _repository = repository;
    }

    public IEnumerable<int> GetModelYears()
    {
        // ?? help?
    }
}

我的问题是,Distinct方法应该在哪里实现?一个LINQ查询看起来像这样:

context.Set<Vehicle>().Select(x => x.ModelYear).Distinct();

我还没有想到如何在存储库层级通用编写一个Distinct方法,而且我认为那不是正确的放置位置。

我们选择在存储库中不返回IQueryable。但我也没有将任何实际的DbSet对象暴露给服务层。

也许更好的问题是,这样做是正确的方式吗?是否有更好的方法?


糟糕,有两个用户给出了我要采纳的答案,但我该如何确定谁是“被采纳”的人?难道让他们打一架吗? - Jason La
这完全取决于你,伙计;) - thsorens
选择了thsoren的答案,因为它更详细。但是也给user902553的答案点了赞,因为基本上是同样的内容并且提供了选项/解释。谢谢你们两个。 - Jason La
2个回答

1
这里有几个选项:
1)最有效的方法是执行您的操作并将结果存储在变量中,然后使用该变量而不是多次执行相同的操作。
2)如果您需要多次执行相同的操作,可以将其封装在一个函数中,并在需要时调用该函数。
context.Set<Vehicle>().Select(x => x.ModelYear).Distinct();

在您的代码库中的某个部分。正如您可能已经知道的那样,这将确保SQL查询在返回结果集之前在数据库级别执行不同的操作。这可以确保不会从数据库返回大量数据集,只是为了让.Net在内存中进一步限制结果集。从那里开始,您将使您的_vehicleService.GetModelYears调用_vehicleRepository.GetDistinctModelYears。

2)如果您正在使用非常小的数据集,并且在少量行上执行内存中的Distinct功能所需的性能损失并不重要,那么您可以: a)让服务中的“GetModelYears()”方法调用_vehicleRepository.GetVehicles()方法,然后b)对从存储库返回的车辆列表执行Distinct操作。


谢谢,用户902553(生成的名字哈哈?)。数据集比较大,我想避免在内存中进行过滤,即使LINQ库非常高效。我可能会选择选项1。 - Jason La

1
如果这只是用于一种对象,而不是在存储库中全部使用,您可以创建一个新的存储库,其中包含该查询,但基于IRepository构建。
public interface IVehicleRepository : IRepository<Vehicle>{
    IEnumerable<int> GetDistinctVehicle();
}

public VehicleRepository : EfRepository<Vehicle>, IVehicleRepository{

    public VehicleRepository(DbContext context) : base(context)
    {
    }


    public IEnumerable<int> GetDistinctVehicle(){
        Context.Set<Vehicle>().Select(x=> x.ModelYear).Distinct();
    }

}

以此方式,您仍然可以使用所有IRepository的内容,同时也能使用您的额外方法。只需在所选的注入器中将IVehicleRepository绑定到VehicleRepository,并像处理IRepositories一样将其注入到构造函数中即可。

是的,你和user902553都指向了同一个方向。现在唯一的问题是,我该将谁标记为“已接受”的答案?如果我能将你们两个都标记为已接受的答案就好了... - Jason La
+1 我们使用仓库来抽象数据源并减少复杂性。 - jgauffin

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