我正在构建一个存储库,我在很多地方看到过2个不在存储库外暴露IQueryable的原因.
1)首先是因为不同的LINQ提供程序可能表现不同,这种差异应该包含在存储库中.
2)第二是防止服务级别开发人员修改数据库查询,以免意外导致性能问题.
我想问题2只能通过将所有查询逻辑保留在存储库中并且不允许任何形式的外部查询构建来防止?但这对我来说似乎有点不切实际.
问题1似乎可以通过使用数据对象模式来解决.
例如 public IEnumerable<T> FindBy(Query query)
我的问题是,为什么我不仅仅传递一个lambda表达式,因为它与提供者无关,似乎为我提供了与查询对象相同的功能,以及相同的分离级别?
例如 public IEnumerable<T> FindBy(Expression<Func<T,bool>> predicate)
有什么理由不这样做吗?它违反了一些规则吗?最佳做法?我应该知道什么?
我在GridView中实现分页.从这篇文章中,我需要两种方法:
public IQueryable BindEmployees(int startRowIndex, int maximumRows)
{
EmployeeInfoDataContext dbEmp = new EmployeeInfoDataContext();
var query = from emp in dbEmp.Employees
join dept in dbEmp.Departments
on emp.DeptID equals dept.DeptID
select new
{
EmpID = emp.EmpID,
EmpName = emp.EmpName,
Age = emp.Age,
Address = emp.Address,
DeptName = dept.DepartmentName
};
return query.Skip(startRowIndex).Take(maximumRows);
}
Run Code Online (Sandbox Code Playgroud)
和
public int GetEmployeeCount()
{
// How can I not repeat the logic above to get the count?
}
Run Code Online (Sandbox Code Playgroud)
如何GetEmployeeCount从第一种方法获取第二种方法的值BindEmployees?我的意思是不重复逻辑(查询)?
我如何声明这样的变量?
var rData = from nc in ctx.NEWSLETTER_CLIENTS
join ni in ctx.NEWSLETTER_INDICES on nc.INDEX_NUM
equals ni.INDEX_NUM
select new
{
ClientID = nc.CLIENT_ID,
Email = nc.CLIENT_EMAIL_ADDRESS,
Index = nc.INDEX_NUM,
MainClass = ni.MAIN_CLASS,
SubClass = ni.SUB_CLASS,
App1 = ni.VALUE_1,
App2 = ni.VALUE_2,
App3 = ni.VALUE_3,
App4 = ni.VALUE_4
};
// Now I need to declare on a variable named fData under the function scope,
// so I can later use it:
var fData = ...; //What do I declare here?
if(x)
fData …Run Code Online (Sandbox Code Playgroud) 我已经阅读了一些文章,声明IEnumerable用于模仿存储过程或限制您的数据库.外部提供商丢失了延迟加载能力.
IQueryable可以为开发人员提供更大的灵活性.懒惰装载就在那里.
在性能方面,两者都消耗了大量的性能..哪一个更优选?
看完这个问题后,我需要澄清一些事情.
IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
Run Code Online (Sandbox Code Playgroud)
问题:
1)可以这么说:在第一个查询中,SQLServer正在运行整个操作,包括where子句,只返回相关的行 - 而第二个查询SELECT *...并将所有 行返回 到C#和THEN过滤器?
2)如果我只有一个集合 - 在记忆中怎么样?(var lstMyPerson = new List<MyPerson>())
IQueryable<MyPerson> lst = from c in lstMyPerson
where c.City == "<City>"
select c;
Run Code Online (Sandbox Code Playgroud)
VS
IEnumerable<MyPerson> custs = from c in lstMyPerson
where c.City == "<City>"
select c; …Run Code Online (Sandbox Code Playgroud) 更新2013-08-22:
看了"建立一个IQueryable提供者系列"(感谢链接!)后,我进一步了解了一下.我相应地更新了代码.它仍然没有完全工作.如果我正确理解了教程,GetEnumerator则在请求多个元素的情况下调用(例如,通过ToList()调用queryable或任何聚合函数).所以GetEnumerator包装器的所有实现都要Execute在提供者上调用一个并传递可查询的表达式.在另一种情况下,如果只请求一个元素,Execute则直接调用.queryable的表达式还反映了它是单个元素还是多个元素.它是否正确?
不幸的是,现在我在调用源查询提供程序时得到一个InvalidOperationException,说"Sequence包含多个元素"Execute.这是什么意思?我只是传递表达式而没有任何翻译,因为如上所述涉及相同的类型.IEnumerable代码中的翻译位可能不完整,但是现在我甚至没有达到这一点.
我正在尝试使用单个底层IQueryable作为数据源来实现一个简单的IQueryable包装器,该数据源为每个结果对象调用转换函数.
我认为这将是相对微不足道的,因为包装器唯一要做的就是翻译.但是我无法让我的实现工作.
请看下面我到目前为止所得到的内容.对于某些查询,它正在工作,但我在某些时候收到StackOverflowException InvalidOperationException.我想这是因为我的queryable和我的查询提供程序之间的循环关联.但我不明白如何正确实现这一点.
在这里我的问题和想法:
1.为什么IQueryable有一个Provider又可以再次返回IQueryable?这不是要求无休止的递归吗?
2.为什么不能实现IEnumerator?为什么FirstOrDefault不使用枚举器来获取元素?当我调试应用程序时GetEnumerator()没有被我的可查询器上的FirstOrDefault()调用.
3.由于在每种情况下都没有使用枚举器,因此调用翻译函数的正确位置在哪里?QueryProvider的Execute-methods似乎是正确的地方.但在某些情况下,我仍然需要在枚举器中进行翻译调用吗? 更新:我知道我意识到我需要提供自己的IEnumerable实现,TranslatingEnumerator并从我的Execute方法返回这个枚举.为了获得枚举器GetEnumerator调用Execute(见下文).请求枚举器的LINQ代码似乎确保表达式实际返回一个IEnumerable.
关于代码的一些附注:
翻译源类型名为TDatabaseEntity,翻译目标类型名为TBusinessEntity.
我基本上提供了一个IQueryable,它从基础IQueryable中获取结果对象,并将它们转换为TBusinessEntity 类型 对象.
我知道表达式也需要翻译.但是我将它放在一边,因为在我的实际应用程序中,我使用相同类型的TBusinessEntity和TDatabaseEntity,因此Expression可以直接传递.
尽管属性相同,但结果对象仍然需要转换为其他实例.更新:我的翻译层已经在我的应用程序中工作,并且还负责相关实体.它只是'实现一个IQueryable包装'的东西,我坚持.
我担心整个实现都是错误的 - 代码中的TODO只是我自己的注释.
背景:我在我的数据访问层中实现了自己从DbContext接收的实体的分离,以防止我的业务层与实际实体联系 - 由于EF和其他要求的一些错误,我不能直接使用EF分离实体.
谢谢你的帮助!
internal class TranslatingQueryable<TDatabaseEntity, TBusinessEntity> : IQueryable<TBusinessEntity>
{
private readonly IQueryProvider _provider;
private readonly IQueryable<TDatabaseEntity> _source; …Run Code Online (Sandbox Code Playgroud) 我有一个'无法转换为SQL'的方法,我想在IQueryable上执行,有没有办法强制IQueryable执行而不必将其存储在某个中间类中?
我有一个对象图,我正在使用EF CodeFirst和AutoMapper从数据库加载到DTO中: -
public class Foo
{
public int Id { get; set; }
public virtual ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
}
public class FooDto
{
public IEnumerable<BarDto> Bars { get; set; }
}
public class BarDto
{ …Run Code Online (Sandbox Code Playgroud) 想象一个类,让我们说可以与一个IList<T>或一个一起使用的分页IQueryable<T>.
该类将具有一个int TotalItems属性,该属性(不是那么令人惊讶)获取/设置可查询或可枚举参数的计数.
如果我IEnumerable<T>用作参数,
//simplified
public Pagination(IEnumerable<T> query)
{
TotalItems = query.Count();
}
Run Code Online (Sandbox Code Playgroud)
该Count()方法将是(如果我没看错,这就是问题所在)Enumerable.Count().因此,即使查询是IQueryable<T>(继承自IEnumerable<T>),它也将被枚举(使用"db query"显然不需要).
那么有什么方法可以Queryable.Count()在我IEnumerable<T>实际使用时使用方法IQueryable<T>,或者我必须改变我的设计,例如在这种情况下,2 ctor
//simplified
public Pagination(IEnumerable<T> query)
{
TotalItems = query.Count();
}
public Pagination(IQueryable<T> query)
{
TotalItems = query.Count();
}
Run Code Online (Sandbox Code Playgroud)
编辑
我也明白,IQueryable<T>沿用IEnumerable<T>无关的事实,IEnumerable<T>并IQueryable<T>具有相同的扩展名的方法,而且它的好,有延期的方法相同的名字,"看起来他们做同样的",但我认为它仍然有时令人困惑......
好奇心的一般问题
它们是框架中的其他示例,具有相同的"体系结构":继承+扩展方法的通用名称?
随着.NET 3.5和IQueryable<T>界面的引入,将出现新的模式.虽然我已经看到了规范模式的许多实现,但我还没有看到使用这种技术的许多其他模式.Rob Conery的Storefront应用程序是另一个使用IQueryable<T>它可能导致一些新模式的具体示例.
有用的IQueryable<T>界面出现了什么样的模式?
iqueryable ×10
c# ×8
linq ×6
.net ×3
ienumerable ×2
.net-3.5 ×1
asp.net ×1
automapper ×1
lazy-loading ×1
objectquery ×1