我正在尝试使用带有表达式的find操作创建一个通用存储库。我现在有以下内容(我使用FastMapper从我的实体对象投影到外部合同对象):
public override List<T> Find(Expression<Func<T, bool>> predicate)
{
var collection = _database.GetCollection<Y>(_collectionName);
return collection.AsQueryable<Y>().Project().To<T>().Where(predicate).ToList();
}
问题是我得到以下异常:“不支持投影后谓词的位置。”
我可以执行以下操作,但这会严重影响性能,因为它会涉及在执行过滤之前从数据库中检索每条记录:
public override List<T> Find(Expression<Func<T, bool>> predicate)
{
var collection = _database.GetCollection<Y>(_collectionName);
return collection.AsQueryable<Y>().Project().To<T>().ToList().AsQueryable().Where(predicate).ToList();
}
我想知道是否有办法将表达式从T转换为Y对象,以便我可以执行以下操作(我认为这将是最高效的,因为它然后将过滤传递到数据库并且仅对结果集执行Project):
public override List<T> Find(Expression<Func<T, bool>> predicate)
{
var collection = _database.GetCollection<Y>(_collectionName);
return collection.AsQueryable<Y>().Where(predicate).Project().To<T>().ToList();
}
任何帮助将不胜感激。感谢。
更新
所以,使用这个问题的信息(Question),我能够更接近我想要的东西。我现在能够做到以下几点:
public override List<T> Find(Expression<Func<T, bool>> predicate)
{
var newPredicate = TransformPredicateLambda<T, Y>(predicate);
var collection = _database.GetCollection<Y>(_collectionName);
return collection.AsQueryable<Y>().Where(newPredicate).Project().To<T>().ToList();
}
我唯一要解决的问题是此时检索fastmapper映射(如果属性为null部分):
protected override Expression VisitMember(MemberExpression node)
{
var dataContractType = node.Member.ReflectedType;
var activeRecordType = _typeConverter(dataContractType);
var property = activeRecordType.GetProperty(node.Member.Name);
if (property == null)
{
}
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
property
);
return converted;
}
基本上,我的一些对象可能如下所示:
//object used throughout my code
public class Store
{
public string StoreId {get; set;}
public Account Account {get; set;}
...
}
//object used in access layer only
public class Store
{
public string StoreId {get; set;}
public string AccountId {get; set;}
...
}
在我的初始化脚本中,我定义了一个类型适配器,如下所示:
TypeAdapterConfig<Store, Models.Store>.NewConfig()
.MapFrom(a => a.Id, s => s.StoreId != null ? new ObjectId(s.StoreId) : new ObjectId())
.MapFrom(d => d.AccountId, s => s.Account.AccountId)
.IgnoreNullValues(true);
答案 0 :(得分:1)
在将查询交给MongoDB之前,你无法从Y到T进行投影的原因是MongoDB对T不了解。我们只知道Y(因为这是集合的类型)。例如:
class Person
{
[BsonElement("fn")]
public string FirstName { get; set; }
[BsonElement("ln")]
public string LastName { get; set; }
}
class PersonView
{
public string FullName { get; set; }
}
您的投影机就是这样的:
person => new PersonView { FullName = person.FirstName + person.LastName }
我们无法访问投影机中的代码,也不知道FullName是FirstName和LastName的串联,因此无法告诉MongoDB执行此操作。
驱动程序的1.x版本中的LINQ支持无法定位聚合框架,这是唯一合法的地方,并且只假设您的投影机生成Expression<Func<Person, PersonView>>
而不是编译{{1 }}。但是,2.x版本的驱动程序会对此有更好的支持,尽管它实际上取决于FastMapper在其下面做了什么。
==
所以,你现在的选择是:
将方法签名更改为Func<Person, PersonView>
。这不仅可以满足您的需求,还可以将您投影的文档限制为仅通过过滤器的文档。
如果T继承自Y,您可以从OfType开始,甚至不需要投影机:Expression<Func<Y, bool>>