Kin*_*sin 16 c# linq-to-entities entity-framework anonymous-types
我正在尝试构建类似条件查询的东西,以便从底层数据库中获取所需的数据.
目前我有以下查询(工作正常)
var eventData = dbContext.Event.Select(t => new
{
Address = true ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
});
Run Code Online (Sandbox Code Playgroud)
如果我改成它
var includeAddress = true; // this will normally be passed as param
var eventData = dbContext.Event.Select(t => new
{
Address = includeAddress ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
});
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
"AnonymousEventGetAddress"类型出现在单个LINQ to Entities查询中的两个结构不兼容的初始化中.A型可以在同一个查询两个地方被初始化,但只有当同一属性在两个地方设置和这些属性以相同的顺序设置.
我在这里做错了什么(因为true它正在工作)以及如何解决这个问题?
我知道将else-part改为
new AnonymousEventGetAddress
{
AddressLine1 = null,
CityName = null
}
Run Code Online (Sandbox Code Playgroud)
将工作.但是,如果我改变属性的顺序,那么这也将失败.
使用的类定义如下:
public class AnonymousEventGetAddress : BaseAnonymousObject<AnonymousEventGetAddress>
{
public string AddressLine1 { get; set; }
public string CityName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
而BaseAnonymousObject<AnonymousEventGetAddress>定义:
public abstract class BaseAnonymousObject<TAnonymous>
where TAnonymous : BaseAnonymousObject<TAnonymous>
{
// this is used in case I have to return a list instead of a single anonymous object
public static Expression<Func<IEnumerable<TAnonymous>>> Empty => () => new TAnonymous[]{}.AsEnumerable();
}
Run Code Online (Sandbox Code Playgroud)
小智 9
对于未来的读者来说,这个重复的内容(一年后添加)是解决我的问题的关键:
该类型出现在单个 LINQ to Entities 查询中的两个结构不兼容的初始化中
当您查看它时,错误消息非常清楚。如果您在同一个 Linq 表达式中多次实例化一个对象,请不要打乱初始化顺序。对我来说,这正是我正在做的事情。同步两个实例化调用之间的属性初始化让编译器再次焕发阳光。
在这种情况下:
new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
}
Run Code Online (Sandbox Code Playgroud)
不同于
new AnonymousEventGetAddress()
Run Code Online (Sandbox Code Playgroud)
在OP查询版本1中,可以肯定地说,由于条件,else分支中的异常初始化永远不会发生true,为什么它可能被丢弃,对于版本2来说,这一定不会发生,我们只剩下两个初始化订单、属性 1 和 2 与根本没有属性。这应该可以做到:
includeAddress
? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
}
: new AnonymousEventGetAddress
{
AddressLine1 = null,
CityName = null
}
Run Code Online (Sandbox Code Playgroud)
我不知道为什么EF有这样的要求,但重要的是需求存在,我们需要考虑它.
第一个代码是有效的,因为它true是一个编译时常量,所以编译器在编译时解析它,最后得到两个表达式之一(基本上删除了三元运算符).而在第二种情况下,它是一个变量,因此表达式树包含原始表达式,并且由于上述EF要求而在运行时失败.
前一段时间我试图where通过实现一个试图解析bool变量的自定义方法来解决这个和类似问题(说实话,主要是动态过滤器),从而在运行时做类似于第一个编译器的东西案件.当然代码是实验性的而没有经过测试,但似乎可以正确处理这些场景,所以你可以尝试一下.用法很简单:
var eventData = dbContext.Event.Select(t => new
{
Address = includeAddress ? new AnonymousEventGetAddress
{
AddressLine1 = t.Address.AddressLine1,
CityName = t.Address.AddressCityName
} : new AnonymousEventGetAddress(),
}).ReduceConstPredicates();
Run Code Online (Sandbox Code Playgroud)
这是使用的辅助方法:
public static partial class QueryableExtensions
{
public static IQueryable<T> ReduceConstPredicates<T>(this IQueryable<T> source)
{
var visitor = new ConstPredicateReducer();
var expression = visitor.Visit(source.Expression);
if (expression != source.Expression)
return source.Provider.CreateQuery<T>(expression);
return source;
}
class ConstPredicateReducer : ExpressionVisitor
{
int evaluateConst;
private ConstantExpression TryEvaluateConst(Expression node)
{
evaluateConst++;
try { return Visit(node) as ConstantExpression; }
finally { evaluateConst--; }
}
protected override Expression VisitConditional(ConditionalExpression node)
{
var testConst = TryEvaluateConst(node.Test);
if (testConst != null)
return Visit((bool)testConst.Value ? node.IfTrue : node.IfFalse);
return base.VisitConditional(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.Type == typeof(bool))
{
var leftConst = TryEvaluateConst(node.Left);
var rightConst = TryEvaluateConst(node.Right);
if (leftConst != null || rightConst != null)
{
if (node.NodeType == ExpressionType.AndAlso)
{
if (leftConst != null) return (bool)leftConst.Value ? (rightConst ?? Visit(node.Right)) : Expression.Constant(false);
return (bool)rightConst.Value ? Visit(node.Left) : Expression.Constant(false);
}
else if (node.NodeType == ExpressionType.OrElse)
{
if (leftConst != null) return !(bool)leftConst.Value ? (rightConst ?? Visit(node.Right)) : Expression.Constant(true);
return !(bool)rightConst.Value ? Visit(node.Left) : Expression.Constant(true);
}
else if (leftConst != null && rightConst != null)
{
var result = Expression.Lambda<Func<bool>>(Expression.MakeBinary(node.NodeType, leftConst, rightConst)).Compile().Invoke();
return Expression.Constant(result);
}
}
}
return base.VisitBinary(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (evaluateConst > 0)
{
var objectConst = node.Object != null ? TryEvaluateConst(node.Object) : null;
if (node.Object == null || objectConst != null)
{
var arguments = new object[node.Arguments.Count];
bool canEvaluate = true;
for (int i = 0; i < arguments.Length; i++)
{
var argumentConst = TryEvaluateConst(node.Arguments[i]);
if (canEvaluate = (argumentConst != null))
arguments[i] = argumentConst.Value;
else
break;
}
if (canEvaluate)
{
var result = node.Method.Invoke(objectConst != null ? objectConst.Value : null, arguments);
return Expression.Constant(result, node.Type);
}
}
}
return base.VisitMethodCall(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (evaluateConst > 0 && (node.NodeType == ExpressionType.Convert || node.NodeType == ExpressionType.ConvertChecked))
{
var operandConst = TryEvaluateConst(node.Operand);
if (operandConst != null)
{
var result = Expression.Lambda(node.Update(operandConst)).Compile().DynamicInvoke();
return Expression.Constant(result, node.Type);
}
}
return base.VisitUnary(node);
}
protected override Expression VisitMember(MemberExpression node)
{
object value;
if (evaluateConst > 0 && TryGetValue(node, out value))
return Expression.Constant(value, node.Type);
return base.VisitMember(node);
}
static bool TryGetValue(MemberExpression me, out object value)
{
object source = null;
if (me.Expression != null)
{
if (me.Expression.NodeType == ExpressionType.Constant)
source = ((ConstantExpression)me.Expression).Value;
else if (me.Expression.NodeType != ExpressionType.MemberAccess
|| !TryGetValue((MemberExpression)me.Expression, out source))
{
value = null;
return false;
}
}
if (me.Member is PropertyInfo)
value = ((PropertyInfo)me.Member).GetValue(source);
else
value = ((FieldInfo)me.Member).GetValue(source);
return true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13654 次 |
| 最近记录: |