C# 导航属性的总和为Linq到SQL
我试图找到一个解决方案,如何使用Linq to SQL在数据库服务器上执行C# 导航属性的总和为Linq到SQL,c#,entity-framework-6,C#,Entity Framework 6,我试图找到一个解决方案,如何使用Linq to SQL在数据库服务器上执行TotalConversions=>Statistics.Sum(Sum=>Sum.Conversions) 当前代码的问题是,统计信息是ICollection(IEnumerable),而不是IQueryable和SUM函数从数据库本地获取所有记录,并且仅在对结果求和时才获取。这不是我们的解决方案,因为统计数据包含数千条记录 public class User : Entity { public int Id {
TotalConversions=>Statistics.Sum(Sum=>Sum.Conversions)
当前代码的问题是,统计信息是ICollection
(IEnumerable),而不是IQueryable和SUM函数从数据库本地获取所有记录,并且仅在对结果求和时才获取。这不是我们的解决方案,因为统计数据包含数千条记录
public class User : Entity
{
public int Id { get; set; }
public virtual ICollection<Statistic> Statistics { get; set; }
[NotMapped]
public int TotalConversions => Statistics.Sum(sum => sum.Conversions);
}
它工作得很好,但另一个问题是这样的属性不能在linq查询中使用
[NotMapped]
public int TotalConversions
{
get
{
if (_totalConversions == null)
{
var databaseContext = GetDbContextFromEntity(this);
_totalConversions = databaseContext.Statistic.Where(s => s.UserId == Id).Sum(s => s.Conversions);
}
return (int)_totalConversions;
}
}
_context.User.Where(w=>w.Id==1 && w.Id == 2).OrderBy(o=>o.TotalConversions)
如何在Model
中的计算属性中执行SUM
,该属性将在数据库服务器上执行,也可以在select查询中使用。如果有EF
,这可能吗?这个问题。
当您将属性标记为[NotMapped]
时,您告诉EF此属性不应存在于数据库级别上。它仅在代码中可用(即数据在内存中时,而不是在数据库中时)
将此属性标记为[NotMapped]
本质上会阻止您使用TotalConversions
属性使用数据库操作(order by)
我在这里看到两种解决方案。一个简单,另一个不那么简单
Simple:在没有属性的情况下使用
OrderBy
中的计算。
在第二个示例中,您已经基本上做到了这一点,但是您可以更简洁地做到这一点,避免使用自定义属性
您要做的是:
_context.User.Where(w=>w.Id==1 && w.Id == 2).OrderBy(o => o.TotalConversions)
正如我提到的,你不能这样做。您可以做的是:
Sum()。实际上,您已将请求从“基于此.Net属性对集合进行排序”(这是不可能的)更改为“基于此SQL兼容评估对集合进行排序”
然而,我假设您正在尝试使用自定义属性,以避免在整个代码库中复制/粘贴相同的计算。如果这是你的目标,我完全同意你的意图,这是一个足够好的理由去尝试更复杂的选择
更复杂:将所需的OrderBy
参数定义为自定义静态User
属性。
您可以参数化OrderBy
参数。首先,OrderBy
方法是一种具有两种通用类型的通用方法:
IOrderedQueryable<A> OrderBy<A,B>(Expression<Func<A,B>> expression) { }
可以这样定义表达式属性:
[NotMapped]
public int TotalConversions => Statistics.Sum(sum => sum.Conversions);
[NotMapped]
public static Expression<Func<User,int>> TotalConversionsLambda = (user => user.Statistics.Sum(sum => sum.Conversions));
对于编译器(和EF),这相当于来自更简单方法的解决方案,因此将以相同的方式工作。但是,这还有另外一个好处,即定义lambda一次(干式),而不是在代码库中的任何地方复制/粘贴它
解释。
参数化表达式可能有点令人困惑。至少在我开始使用它们时,情况就是这样。因此,也许有一个更简单的解释
请注意,我们可以交换文字值:
DoSomething(5);
对于包含值的变量:
int myValue = 5;
DoSomething(myValue);
本例使用整数,但我们可以对任何类型执行此操作(string
,bool
,…应该是显而易见的)。这也适用于参考类型:
DoSomething(new User() { Name = "John Doe" });
与:
User john = new User() { Name = "John Doe" };
DoSomething(john);
Expression<Func<Foo,Bar>> myValue = (foo => foo.BarValue);
DoSomething(myValue);
表达式
有点复杂(由于其复杂的泛型类型和lambda表示法),但其工作原理完全相同:
DoSomething(foo => foo.BarValue);
与:
User john = new User() { Name = "John Doe" };
DoSomething(john);
Expression<Func<Foo,Bar>> myValue = (foo => foo.BarValue);
DoSomething(myValue);
老实说,与简单地定义一个独立于表达式属性工作的属性相比,我确实认为表达式的重复编译最终可能会对性能造成更大的伤害:
[NotMapped]
public static Expression<Func<User,int>> TotalConversionsLambda = (user => user.Statistics.Sum(sum => sum.Conversions));
[NotMapped]
public int TotalConversions
{
get
{
return this.Statistics.Sum(sum => sum.Conversions);
}
}
[未映射]
公共静态表达式TotalConversionsLambda=(user=>user.Statistics.Sum(Sum=>Sum.Conversions));
[未映射]
公共整数转换
{
得到
{
返回this.Statistics.Sum(Sum=>Sum.Conversions);
}
}
选择权在你。如果你想(学究般地)坚持干燥,你可以使用第一个;但这样做的性能成本最终可能对你的伤害大于坚持干货对你的好处
我没有编译表达式的性能成本的确切数字,也不知道您的优先级(性能高于性能?性能高于性能?),因此我无法为您做出决定。如果在最后一段代码的where和OrderBy()子句之间添加.ToList(),您将能够在Linq中使用TotalConversions函数。。ToList()之后的任何原因现在都将是Linq toEntities@uk2k05order by应该在数据库内部执行,如果您添加ToList(),查询将成为可枚举的,并且之后的所有操作都将在本地执行,现在将其镜像到~200k条记录上?!只需跳过Where,假设查询只有OrderBy\u context.User.OrderBy(o=>o.TotalConversions)。这将不起作用,因为Linq to SQL提供程序不会将TotalConversion转换为SQL。使用您的建议_context.User.ToList().OrderBy(o=>o.TotalConversions)所有排序都将在本地执行。很快-这是不可能的(不幸)。@IvanStoev是的,我提供的解决方案不可能实现,但我99%确定可以使用带编译的谓词。问题不在于实现,而在于[NotMapped]
属性。一旦标记了这样的属性,就不能在L2E查询中使用它。如果您不标记它,EF将在db表中查找列。“小添加”部分将无法按预期工作,因为统计信息是导航属性,因此它将要么将整个集合加载到内存中(如果启用延迟加载),要么返回0(甚至可能引发异常)@Evk:这个小加法是基于OP自己的初始代码:public int TotalConversions=>Statistics.Sum(Sum=>Sum.Conversions)
。您所说的是正确的,但该评论对OP的代码同样正确。在任何情况下(我的或他的)
[NotMapped]
public static Expression<Func<User,int>> TotalConversionsLambda = (user => user.Statistics.Sum(sum => sum.Conversions));
[NotMapped]
public int TotalConversions
{
get
{
return TotalConversionsLambda.Compile().Invoke(this);
}
}
[NotMapped]
public static Expression<Func<User,int>> TotalConversionsLambda = (user => user.Statistics.Sum(sum => sum.Conversions));
[NotMapped]
public int TotalConversions
{
get
{
return this.Statistics.Sum(sum => sum.Conversions);
}
}