我继承了一个使用Castle Windsor IRepository模式的系统来抽象远离DAL的LinqToSQL。
我能看到的主要问题是IRepository只实现了IEnumerable。因此,即使是最简单的查询也必须从数据表中加载所有数据,以返回单个对象。
目前的用法如下
using (IUnitOfWork context2 = IocServiceFactory.Resolve<IUnitOfWork>())
{
KpiFormDocumentEntry entry = context2.GetRepository<KpiFormDocumentEntry>().FindById(id, KpiFormDocumentEntry.LoadOptions.FormItem);
这使用lambda来过滤,就像这样
public static KpiFormDocumentEntry FindById(this IRepository<KpiFormDocumentEntry> source, int id, KpiFormDocumentEntry.LoadOptions loadOptions)
{
return source.Where( qi => qi.Id == id ).LoadWith( loadOptions ).FirstOrDefault();
}
所以它成为一个很好的扩展方法。
我的问题是,如何使用相同的接口/模式等,还可以实现IQueryable以正确支持LinqToSQL并获得一些严重的性能提升?
IRepository的当前实现/接口如下
public interface IRepository<T> : IEnumerable<T> where T : class
{
void Add(T entity);
void AddMany(IEnumerable<T> entities);
void Delete(T entity);
void DeleteMany(IEnumerable<T> entities);
IEnumerable<T> All();
IEnumerable<T> Find(Func<T, bool> predicate);
T FindFirst(Func<T, bool> predicate);
}
然后由SqlClientRepository实现,如此
public sealed class SqlClientRepository<T> : IRepository<T> where T : class
{
private readonly Table<T> _source;
internal SqlClientRepository(Table<T> source)
{
if( source == null ) throw new ArgumentNullException( "source", Gratte.Aurora.SHlib.labelText("All_TableIsNull",1) );
_source = source;
}
//removed add delete etc
public IEnumerable<T> All()
{
return _source;
}
public IEnumerator<T> GetEnumerator()
{
return _source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
目前的问题,在我们上面的示例中,.Where正在调用'GetEnumerator',然后将所有行加载到内存中,然后查找我们需要的行。
如果我更改IRepository以实现IQueryable,我无法实现所需的三种方法,因为这些方法在Table类中不公开。
我认为我应该将SQLClientRepository更改为如此定义
public sealed class SqlClientRepository<T> : IQueryable<T>, IRepository<T> where T : class
然后实现必要的方法,但我无法弄清楚如何传递表达式等因为它们是Table类的私有成员,如此
public override Type ElementType
{
get { return _source.ElementType; } //Won't work as ElementType is private
}
public override Expression Expression
{
get { return _source.Expression; } //Won't work as Expression is private
}
public override IQueryProvider Provider
{
get { return _source.Provider; } //Won't work as Provider is private
}
任何帮助都非常感谢将它从'加载'后迭代到数据库中的每一行'到'select x where id = 1'!
答案 0 :(得分:0)
如果要公开linq,可以停止使用存储库模式并直接使用Linq2Sql。原因是每个Linq To Sql提供商都有自己的自定义解决方案。因此,如果你公开LINQ,你会得到一个漏洞的抽象。那么使用抽象层是没有意义的。
您没有暴露LINQ,而是有两个选择:
答案 1 :(得分:0)
所以,虽然它可能不再是真正的抽象,但重点是在不更新已编写的所有查询的情况下将linq的好处发送给sql。
所以,我使IRepository实现了IQueryable而不是IEnumerable。
然后在SqlClientRepository实现中,我可以调用AsQueryable()将Table转换为IQueryable,然后一切都很好,就像这样。
现在到处都有人编写了IRepository()。其中(qi =&gt; qi.id = id)或类似的,它实际上将ID传递给sql server并且只返回一条记录而不是所有记录,并且循环通过寻找正确的。
/// <summary>Provides the ability to query and access entities within a SQL Server data store.</summary>
/// <typeparam name="T">The type of entity in the repository.</typeparam>
public sealed class SqlClientRepository<T> : IRepository<T> where T : class
{
private readonly Table<T> _source;
private readonly IQueryable<T> _sourceQuery;
IQueryable<T> Query()
{
return (IQueryable<T>)_source;
}
public Type ElementType
{
get { return _sourceQuery.GetType(); }
}
public Expression Expression
{
get { return _sourceQuery.Expression; }
}
public IQueryProvider Provider
{
get { return _sourceQuery.Provider; }
}
/// <summary>Initializes a new instance of the <see cref="SqlClientRepository{T}"/> class.</summary>
/// <param name="source">A <see cref="Table{T}"/> to a collection representing the entities from a SQL Server data store.</param>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is a <c>null</c> reference (<c>Nothing</c> in Visual Basic).</exception>
internal SqlClientRepository(Table<T> source)
{
if( source == null ) throw new ArgumentNullException( "source", "All_TableIsNull" ) );
_source = source;
_sourceQuery = _source.AsQueryable();
}