扩展MongoDB C# Driver的QueryBuilder
由于不想直接hardcode "ClassA.MemberA.MemberB" 这样的字符串 ,写了以下几个类,用于以下常用的场景:
1. 表达式转换成字符串函数: ExpToStr()
2. Collection函数:当有集合成员时,可以使用此类,将返回QueryCollection对象,这个类的代码之后附上
3. CollectionAs函数:当使用了继承,希望将基类转换为子类并返回子类的QueryCollection
使用示例:
//获得表达式的字符串形式
1. QueryEx<ClassA>.ExpToStr ((ClassA m)=> m.MemberA.MemberB.MemberC)
//集合.成员.字段
//PoppedSegments为集合,AssignedNetwork.Name为成员
//将返回PoppedSegments.AssignedNetwork.Name
2. QueryEx<MDDelivery>.Collection(x => x.PoppedSegments).Matches(p => p.AssignedNetwork.Name, bsonRegex),
//子类集合.成员.字段
//STPaymentTransaction为基类,STPaymentCompanyCredit为子类,Company字段在子类中
//将返回Payments.Company.Name
3. QueryEx<MDDelivery>.CollectionAs<STPaymentTransaction, STPaymentCompanyCredit>(x=>x.Payments).Matches(p=>p.Company.Name, bsonRegex)
//集合.集合.成员.字段
//Parcels为集合,STCustomPropertyRuntime为基类,STNumericPropertyRuntime为子类,CustomProps为STNumericPropertyRuntime中成员,Value为CustomProp中成员
//将返回Parcels.CustomProps.Value
4. QueryEx<MDDelivery>.Collection(x=>x.Parcels).CollectionMemberAs<STCustomPropertyRuntime, STNumericPropertyRuntime>(p=>p.CustomProps).Matches(p=>p.Value, bsonRegex),
实现代码:
public class QueryEx<TDocument>
{
public static QueryCollection<TDocument, TCollection> Collection<TCollection>(
Expression<Func<TDocument, IEnumerable<TCollection>>> collectionExpression)
{
return new QueryCollection<TDocument, TCollection>(collectionExpression);
}
//for those cases using inheritance
//e.g STPaymentTransaction
//Payments will return STPaymentTransaction
//need to cast to sub classes(STPaymentCompanyCredit) so that be able to filter by child members (e.g. Company)
public static QueryCollection<TDocument, TSub> CollectionAs<TCollection, TSub>(
Expression<Func<TDocument, IEnumerable<TCollection>>> collectionExpression)
where TSub : TCollection
{
var argParam = Expression.Parameter(typeof (TDocument), "x");
var memberStr = ExpToStr(collectionExpression);
MemberExpression nameProperty = Expression.Property(argParam, memberStr);
var subExp = Expression.Convert(nameProperty, typeof(IEnumerable<TSub>));
var exp = Expression.Lambda<Func<TDocument, IEnumerable<TSub>>>(
subExp,
argParam);
return new QueryCollection<TDocument, TSub>(exp);
}
/// <summary>
/// return string value for a expression:
/// for s.Name.Val1.Val2 will return Name.Val1.Val2
/// </summary>
/// <typeparam name="MDClass"></typeparam>
/// <typeparam name="Member"></typeparam>
/// <param name="exp"></param>
/// <returns></returns>
public static string ExpToStr<TDocument, Member>(Expression<Func<TDocument, Member>> exp)
{
return new QueryExpressionHelper().MemberExpression(exp);
}
}
public class QueryCollection<TDocument, TCollection>
{
private readonly QueryExpressionHelper _queryExpression;
private string _collectionName;
public string Context
{
get { return _collectionName; }
}
public QueryCollection(Expression<Func<TDocument, IEnumerable<TCollection>>> collectionExpression)
{
_queryExpression = new QueryExpressionHelper();
_collectionName = _queryExpression.MemberExpression(collectionExpression);
}
public QueryMember<TCollection, TMember> Member<TMember>(Expression<Func<TCollection, TMember>> exp)
{
var expStr = QueryEx<TCollection>.ExpToStr(exp);
var context = string.Format("{0}.{1}", _collectionName, expStr);
var obj = new QueryMember<TCollection, TMember>(context);
return obj;
}
public QueryCollection<TCollection, TMember> CollectionMember<TMember>(
Expression<Func<TCollection, IEnumerable<TMember>>> exp)
{
var expStr = QueryEx<TCollection>.ExpToStr(exp);
var obj = new QueryCollection<TCollection, TMember>(exp)
{
_collectionName = string.Format("{0}.{1}", _collectionName, expStr)
};
return obj;
}
/// <summary>
/// this method only support 1 layer nested(not for Query Collection.Collection , but for Collection.Member)
/// if member is collection and need convert to sub class
/// </summary>
/// <typeparam name="TMember">Base Type</typeparam>
/// <typeparam name="TMemberSub">Child Class Type</typeparam>
/// <param name="collectionExpression"></param>
/// <returns></returns>
public QueryCollection<TCollection, TMemberSub> CollectionMemberAs<TMember, TMemberSub>(
Expression<Func<TCollection, IEnumerable<TMember>>> collectionExpression)
where TMemberSub : TMember
{
var obj = QueryEx<TCollection>.CollectionAs<TMember, TMemberSub>(collectionExpression);
obj._collectionName = string.Format("{0}.{1}", _collectionName, obj._collectionName);
return obj;
}
public IMongoQuery LT<TMember>(Expression<Func<TCollection, TMember>> memberExpression, TMember value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.LT(string.Format("{0}.{1}", _collectionName, memberName), BsonValue.Create(value));
}
public IMongoQuery LT<TValue>(Expression<Func<TCollection, IEnumerable<TValue>>> memberExpression, TValue value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.LT(string.Format("{0}.{1}", _collectionName, memberName), BsonValue.Create(value));
}
public IMongoQuery EQ<TMember>(Expression<Func<TCollection, TMember>> memberExpression, TMember value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.EQ(string.Format("{0}.{1}", _collectionName, memberName), BsonValue.Create(value));
}
public IMongoQuery EQ<TValue>(Expression<Func<TCollection, IEnumerable<TValue>>> memberExpression, TValue value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.EQ(string.Format("{0}.{1}", _collectionName, memberName), BsonValue.Create(value));
}
public IMongoQuery NE<TMember>(Expression<Func<TCollection, TMember>> memberExpression, TMember value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.NE(string.Format("{0}.{1}", _collectionName, memberName), BsonValue.Create(value));
}
public IMongoQuery NE<TValue>(Expression<Func<TCollection, IEnumerable<TValue>>> memberExpression, TValue value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.NE(string.Format("{0}.{1}", _collectionName, memberName), BsonValue.Create(value));
}
public IMongoQuery In<TMember>(Expression<Func<TCollection, TMember>> memberExpression, params TMember[] values)
{
return In<TMember>(memberExpression, new List<TMember>(values));
}
public IMongoQuery In<TMember>(Expression<Func<TCollection, TMember>> memberExpression,
IEnumerable<TMember> values)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.In(string.Format("{0}.{1}", _collectionName, memberName), values.Select(x => BsonValue.Create(x)));
}
public IMongoQuery In<TCastC, TMember>(Expression<Func<TCastC, TMember>> memberExpression,
IEnumerable<TMember> values) where TCastC : TCollection
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.In(string.Format("{0}.{1}", _collectionName, memberName), values.Select(x => BsonValue.Create(x)));
}
public IMongoQuery In<TValue>(Expression<Func<TCollection, IEnumerable<TValue>>> memberExpression, IEnumerable<TValue> values)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.In(string.Format("{0}.{1}", _collectionName, memberName), values.Select(x => BsonValue.Create(x)));
}
public IMongoQuery In<TCastC, TValue>(Expression<Func<TCastC, IEnumerable<TValue>>> memberExpression, IEnumerable<TValue> values) where TCastC : TCollection
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.In(string.Format("{0}.{1}", _collectionName, memberName), values.Select(x => BsonValue.Create(x)));
}
public IMongoQuery Matches<TMember>(Expression<Func<TCollection, TMember>> memberExpression, BsonRegularExpression value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.Matches(string.Format("{0}.{1}", _collectionName, memberName), value);
}
public IMongoQuery Matches<TValue>(Expression<Func<TCollection, IEnumerable<TValue>>> memberExpression, BsonRegularExpression value)
{
var memberName = _queryExpression.MemberExpression(memberExpression);
return Query.Matches(string.Format("{0}.{1}", _collectionName, memberName), value);
}
}
public class QueryMember<TDocument, TCollection>
{
private readonly QueryExpressionHelper _queryExpression;
private string _collectionName;
public string Context
{
get { return _collectionName; }
}
public QueryMember(Expression<Func<TDocument, TCollection>> exp)
{
_queryExpression = new QueryExpressionHelper();
_collectionName = _queryExpression.MemberExpression(exp);
}
public QueryMember(string context)
{
_collectionName = context;
}
}
public class QueryExpressionHelper
{
public string Context;
public string MemberExpression<TMember>(Expression<TMember> expression)
{
MemberExpression me;
switch (expression.Body.NodeType)
{
case ExpressionType.MemberAccess:
me = expression.Body as MemberExpression;
break;
case ExpressionType.Convert:
dynamic convertedBody = expression.Body;
me = convertedBody.Operand as MemberExpression;
break;
default:
throw new NotSupportedException(string.Format("Member with node type {0} is not supported. expression {1}", expression.Body.NodeType, expression));
}
var stack = new Stack<string>();
while (me != null)
{
stack.Push(me.Member.Name);
me = me.Expression as MemberExpression;
}
var expStr = string.Join(".", stack.ToArray());
return expStr;
}
}
public static class QueryMoney
{
public static IMongoQuery Value(string name, double val)
{
var accuracy = 0.005;
return Query.And(
Query.LT(name, new BsonDouble(val + accuracy)),
Query.GT(name, new BsonDouble(val - accuracy)));
}
}