使用Mongo的存储库上的通用查找方法

时间:2015-02-06 20:44:44

标签: c# mongodb generics repository

我正在尝试使用带有表达式的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);

1 个答案:

答案 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在其下面做了什么。

==

所以,你现在的选择是:

  1. 将方法签名更改为Func<Person, PersonView>。这不仅可以满足您的需求,还可以将您投影的文档限制为仅通过过滤器的文档。

  2. 如果T继承自Y,您可以从OfType开始,甚至不需要投影机:Expression<Func<Y, bool>>