我正在开发和多层应用程序解决方案,我的解决方案看起来像这样
我的Business.Process项目(业务层)只知道Business.DomainObject和Data.Repository项目,因此不知道与Data.Sql.Entity项目的关系。 我将业务(域)对象发送到存储库并在此项目中进行映射,然后我在存储库层中使用关系数据层进行CRUD处理。
我的传统域对象是这样的,它只有属性
public class UserBO
{
public string Email { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
我的业务层类是这样的,
Repository r = new UserRepository();
r.Insert(myUserDomainObject);
一切都很好,但我的“谓词”查询有一些问题。我的存储库界面中有一个方法
bool IsExists(Expression<Func<BusinessObject,bool>> predicate);
并在我的业务层中使用它;
Repository r = new UserRepository(); <br/>
r.IsExists(p => p.Email.Equals("email address"));
正如您所看到的,它的参数是“业务对象”,但我的实际存储库(与数据库连接)使用的是我的dbml文件中的“dataobject”。
public override bool IsExists(Expression<Func<DataObject, bool>> predicate){
return _table.Where(predicate).Any();
}
我想转换这两个谓词,例如我将发送UserBO并转换为UserDataObject
我怎样才能做到这一点?
您必须分析LambdaExpression的参数和主体并构建一个新的。您可以使用predicate.Body
访问它的主体,使用predicate.Parameters访问参数。新表达式具有UserBO类型的参数,而不是UserDataObject。此外,正文中的ParameterExpression必须使用新参数。
当然,这假设它是一个简单的场景,其中BO和DataObject具有相同的属性。更复杂的转换是可能的,但需要对表达式树进行更深入的分析。
为了给你一个起点,我将一个样本与一个非常相似的BusinessObject和DataObject放在一起。关键组件是从ExpressionVisitor派生的类,它执行转换:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConvertExpression
{
public class BusinessObject
{
public int Value { get; set; }
}
public class DataObject
{
public int Value { get; set; }
}
internal class ExpressionConverter : ExpressionVisitor
{
public Expression Convert(Expression expr)
{
return Visit(expr);
}
private ParameterExpression replaceParam;
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) == typeof(Func<BusinessObject, bool>))
{
replaceParam = Expression.Parameter(typeof(DataObject), "p");
return Expression.Lambda<Func<DataObject, bool>>(Visit(node.Body), replaceParam);
}
return base.VisitLambda<T>(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(BusinessObject))
return replaceParam; // Expression.Parameter(typeof(DataObject), "p");
return base.VisitParameter(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(BusinessObject))
{
var member = typeof(DataObject).GetMember(node.Member.Name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).FirstOrDefault();
if (member == null)
throw new InvalidOperationException("Cannot identify corresponding member of DataObject");
return Expression.MakeMemberAccess(Visit(node.Expression), member);
}
return base.VisitMember(node);
}
}
public class ConvertExpression
{
public static void Main()
{
BusinessObject[] bos = { new BusinessObject() { Value = 123 }, new BusinessObject() { Value = 246 } };
DataObject[] dos = { new DataObject() { Value = 123 }, new DataObject() { Value = 246 } };
Expression<Func<BusinessObject, bool>> boExpr = x => x.Value == 123;
var conv = new ExpressionConverter();
Expression<Func<DataObject, bool>> doExpr = (Expression<Func<DataObject, bool>>) conv.Convert(boExpr);
var selBos = bos.Where(boExpr.Compile());
Console.WriteLine("Matching BusinessObjects: {0}", selBos.Count());
foreach (var bo in selBos)
Console.WriteLine(bo.Value);
var compDoExpr = doExpr.Compile();
var selDos = dos.Where(doExpr.Compile());
Console.WriteLine("Matching DataObjects: {0}", selDos.Count());
foreach (var dataObj in selDos)
Console.WriteLine(dataObj.Value);
Console.ReadLine();
}
}
}
如果你需要它,我创建了一个小的流畅的库来动态创建lambda函数,而无需直接处理System.Linq.Expressions。在其他事物之间,它恰好包含了你所需要的东西,只是举个例子来说明你可以做的城市比较:
//Cached somewhere
var compareLambda= ExpressionUtil.GetComparer<CityBO>(p =>
p.Id.Value,ComparaisonOperator.Equal);
//Then in the execution
Repository.IsExists(p=>compareLambda(p,city id));
代码和文档在这里:Kendar Expression Builder与单元测试非常自我解释虽然nuget包在这里:Nuget Expression Builder
你的代码适用于单个属性,如
Repository.IsExists(p => p.Email.Equals("[email protected]"));
但正如我所提到的,当我尝试这个时,我的域对象类有一些嵌套的类属性,如“City”
Repository.IsExists(p => p.City.Id.Equals(city id));
它引发了一个例外;
属性'Business.SDomainObject.SystemCommon.ExtendedProperty.PrimaryKey Id'未定义类型'Data.Sql.Entity.LinqDataContext.City'
我理解这个例外,因为我的城市课这样;
public class CityBO : IDomainObject
{
public PrimaryKey Id { get; set; }
public string Name { get; set; }
public EntityReferance<CountryBO> Country { get; set; }
public LocationInfoBO Location { get; set; }
public StatusInfo Status { get; set; }
}
和PrimaryKey属性(类)是
public class PrimaryKey
{
public string Value { get; set; }
}
我正在使用这个属性,因为我的实体有不同的主键类型,而且我计划将来实现Nosql db。
我要感谢您的代码,当我查询单个属性时,它对我很有用。
问候
归功于@Markus,这是一个通用的转换器类......
internal class ExpressionConverter<TInput, TOutput> : ExpressionVisitor
{
public Expression<Func<TOutput, bool>> Convert(Expression<Func<TInput, bool>> expression)
{
return (Expression<Func<TOutput, bool>>)Visit(expression);
}
private ParameterExpression replaceParam;
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) == typeof(Func<TInput, bool>))
{
replaceParam = Expression.Parameter(typeof(TOutput), "p");
return Expression.Lambda<Func<TOutput, bool>>(Visit(node.Body), replaceParam);
}
return base.VisitLambda<T>(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(TInput))
return replaceParam;
return base.VisitParameter(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(TInput))
{
var member = typeof(TOutput).GetMember(node.Member.Name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).FirstOrDefault();
if (member == null)
throw new InvalidOperationException("Cannot identify corresponding member of DataObject");
return Expression.MakeMemberAccess(Visit(node.Expression), member);
}
return base.VisitMember(node);
}
}