gpt4 book ai didi

c# - ObjectSet 包装器不适用于 linqToEntities 子查询

转载 作者:可可西里 更新时间:2023-11-01 08:48:50 27 4
gpt4 key购买 nike

为了在密集的数据库使用系统中进行访问控制,我必须实现一个对象集包装器,其中将检查 AC。

主要目标是进行此更改以保留现有的数据库访问代码,该代码是通过对所有类的实体进行 linq 实现的(没有集中的数据库层)。

创建的 ObjectSetWrapper 是这样的:

public class ObjectSetWrapper<TEntity> : IQueryable<TEntity> where TEntity : EntityObject
{
private IQueryable<TEntity> QueryableModel;
private ObjectSet<TEntity> ObjectSet;

public ObjectSetWrapper(ObjectSet<TEntity> objectSetModels)
{
this.QueryableModel = objectSetModels;
this.ObjectSet = objectSetModels;
}

public ObjectQuery<TEntity> Include(string path)
{
return this.ObjectSet.Include(path);
}

public void DeleteObject(TEntity @object)
{
this.ObjectSet.DeleteObject(@object);
}

public void AddObject(TEntity @object)
{
this.ObjectSet.AddObject(@object);
}

public IEnumerator<TEntity> GetEnumerator()
{
return QueryableModel.GetEnumerator();
}

public Type ElementType
{
get { return typeof(TEntity); }
}

public System.Linq.Expressions.Expression Expression
{
get { return this.QueryableModel.Expression; }
}

public IQueryProvider Provider
{
get { return this.QueryableModel.Provider; }
}

public void Attach(TEntity entity)
{
this.ObjectSet.Attach(entity);
}

public void Detach(TEntity entity)
{
this.ObjectSet.Detach(entity);
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.QueryableModel.GetEnumerator();
}
}

它非常简单,适用于简单的查询,例如:

//db.Product is ObjectSetWrapper<Product>
var query = (from item in db.Product where item.Quantity > 0 select new { item.Id, item.Name, item.Value });
var itensList = query.Take(10).ToList();

但是当我有这样的子查询时:

//db.Product is ObjectSetWrapper<Product>
var query = (from item in db.Product
select new
{
Id = item.Id,
Name = item.Name,
SalesQuantity = (from sale in db.Sale where sale.ProductId == item.Id select sale.Id).Count()
}).OrderByDescending(x => x.SalesQuantity);

var productsList = query.Take(10).ToList();

我收到 NotSupportedException,说我无法为我的内部查询实体类型创建常量值:

Unable to create a constant value of type 'MyNamespace.Model.Sale'. Only primitive types or enumeration types are supported in this context.

如何让我的查询正常工作?我真的不需要让我的包装器成为 ObjectSet 类型,我只需要在查询中使用它。


已更新

我已经更改了我的类(class)签名。现在它也在实现 IObjectSet<>,但我得到了相同的 NotSupportedException:

public class ObjectSetWrapper<TEntity> : IQueryable<TEntity>, IObjectSet<TEntity> where TEntity : EntityObject

最佳答案

编辑:

问题是以下 LINQ 构造被翻译成 LINQ 表达式,其中包含您的自定义类 (ObjectSetWrapper)。

var query = (from item in db.Product
select new
{
Id = item.Id,
Name = item.Name,
SalesQuantity = (from sale in db.Sale where sale.ProductId == item.Id select sale.Id).Count()
}).OrderByDescending(x => x.SalesQuantity);

LINQ to Entities 尝试将此表达式转换为 SQL 语句,但它不知道如何处理自定义类(以及自定义方法)。

这种情况下的解决方案是替换IQueryProvider使用自定义的,它应该拦截查询执行并将包含自定义类/方法的 LINQ 表达式转换为有效的 LINQ to Entities 表达式(使用实体和对象集进行操作)。

表达式转换是使用派生自 ExpressionVisitor 的类执行的,执行表达式树遍历,替换相关节点,到LINQ to Entities可以接受的节点

第 1 部分 - IQueryWrapper

// Query wrapper interface - holds and underlying query
interface IQueryWrapper
{
IQueryable UnderlyingQueryable { get; }
}

第 2 部分 - 摘要 QueryWrapperBase (非通用)

abstract class QueryWrapperBase : IQueryProvider, IQueryWrapper
{
public IQueryable UnderlyingQueryable { get; private set; }

class ObjectWrapperReplacer : ExpressionVisitor
{
public override Expression Visit(Expression node)
{
if (node == null || !typeof(IQueryWrapper).IsAssignableFrom(node.Type)) return base.Visit(node);
var wrapper = EvaluateExpression<IQueryWrapper>(node);
return Expression.Constant(wrapper.UnderlyingQueryable);
}

public static Expression FixExpression(Expression expression)
{
var replacer = new ObjectWrapperReplacer();
return replacer.Visit(expression);
}

private T EvaluateExpression<T>(Expression expression)
{
if (expression is ConstantExpression) return (T)((ConstantExpression)expression).Value;
var lambda = Expression.Lambda(expression);
return (T)lambda.Compile().DynamicInvoke();
}
}

protected QueryWrapperBase(IQueryable underlyingQueryable)
{
UnderlyingQueryable = underlyingQueryable;
}

public abstract IQueryable<TElement> CreateQuery<TElement>(Expression expression);
public abstract IQueryable CreateQuery(Expression expression);

public TResult Execute<TResult>(Expression expression)
{
return (TResult)Execute(expression);
}

public object Execute(Expression expression)
{
expression = ObjectWrapperReplacer.FixExpression(expression);
return typeof(IQueryable).IsAssignableFrom(expression.Type)
? ExecuteQueryable(expression)
: ExecuteNonQueryable(expression);
}

protected object ExecuteNonQueryable(Expression expression)
{
return UnderlyingQueryable.Provider.Execute(expression);
}

protected IQueryable ExecuteQueryable(Expression expression)
{
return UnderlyingQueryable.Provider.CreateQuery(expression);
}
}

第 3 部分 - 通用 QueryWrapper<TElement>

class QueryWrapper<TElement> : QueryWrapperBase, IOrderedQueryable<TElement>
{
private static readonly MethodInfo MethodCreateQueryDef = GetMethodDefinition(q => q.CreateQuery<object>(null));

public QueryWrapper(IQueryable<TElement> underlyingQueryable) : this(null, underlyingQueryable)
{
}

protected QueryWrapper(Expression expression, IQueryable underlyingQueryable) : base(underlyingQueryable)
{
Expression = expression ?? Expression.Constant(this);
}

public virtual IEnumerator<TElement> GetEnumerator()
{
return ((IEnumerable<TElement>)Execute<IEnumerable>(Expression)).GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public Expression Expression { get; private set; }

public Type ElementType
{
get { return typeof(TElement); }
}

public IQueryProvider Provider
{
get { return this; }
}

public override IQueryable CreateQuery(Expression expression)
{
var expressionType = expression.Type;
var elementType = expressionType
.GetInterfaces()
.Single(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.GetGenericArguments()
.Single();

var createQueryMethod = MethodCreateQueryDef.MakeGenericMethod(elementType);

return (IQueryable)createQueryMethod.Invoke(this, new object[] { expression });
}

public override IQueryable<TNewElement> CreateQuery<TNewElement>(Expression expression)
{
return new QueryWrapper<TNewElement>(expression, UnderlyingQueryable);
}

private static MethodInfo GetMethodDefinition(Expression<Action<QueryWrapper<TElement>>> methodSelector)
{
var methodCallExpression = (MethodCallExpression)methodSelector.Body;
return methodCallExpression.Method.GetGenericMethodDefinition();
}
}

第 4 部分 - 最后是您的 ObjectSetWrapper

public class ObjectSetWrapper<TEntity> : IQueryable<TEntity>, IQueryWrapper where TEntity : class
{
private IQueryable<TEntity> QueryableModel;
private ObjectSet<TEntity> ObjectSet;

public ObjectSetWrapper(ObjectSet<TEntity> objectSetModels)
{
this.QueryableModel = new QueryWrapper<TEntity>(objectSetModels);
this.ObjectSet = objectSetModels;
}

public ObjectQuery<TEntity> Include(string path)
{
return this.ObjectSet.Include(path);
}

public void DeleteObject(TEntity @object)
{
this.ObjectSet.DeleteObject(@object);
}

public void AddObject(TEntity @object)
{
this.ObjectSet.AddObject(@object);
}

public IEnumerator<TEntity> GetEnumerator()
{
return QueryableModel.GetEnumerator();
}

public Type ElementType
{
get { return typeof(TEntity); }
}

public System.Linq.Expressions.Expression Expression
{
get { return this.QueryableModel.Expression; }
}

public IQueryProvider Provider
{
get { return this.QueryableModel.Provider; }
}

public void Attach(TEntity entity)
{
this.ObjectSet.Attach(entity);
}

public void Detach(TEntity entity)
{
this.ObjectSet.Detach(entity);
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.QueryableModel.GetEnumerator();
}

IQueryable IQueryWrapper.UnderlyingQueryable
{
get { return this.ObjectSet; }
}
}

关于c# - ObjectSet 包装器不适用于 linqToEntities 子查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21603086/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com