c#在彼此之间转换谓词

时间:2013-12-08 19:10:45

标签: c# linq linq-to-sql predicate

我正在开发和多层应用程序解决方案,我的解决方案看起来像这样

  • Business.DomainObject
  • Business.Process(*实际业务层)
  • Data.Mapper
  • Data.Repository
  • Data.Sql.Entity(*具有.dbml文件的实际数据层)

我的 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

我该怎么做?

4 个答案:

答案 0 :(得分:2)

您必须分析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();
        }
    }
}

答案 1 :(得分:1)

如果你需要它,我创建了一个小的流畅的库来动态创建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

答案 2 :(得分:0)

您的代码适用于单个属性,例如

Repository.IsExists(p => p.Email.Equals("abc@xyz.com"));

但正如我所提到的,当我尝试这个时,我的域对象类有一些嵌套的类属性,如“City”,

Repository.IsExists(p => p.City.Id.Equals(city id));

它会引发异常;

  

属性   “Business.DomainObject.SystemCommon.ExtendedProperty.PrimaryKey   Id'未定义类型   'Data.Sql.Entity.LinqDataContext.City'

我理解这个例外,因为我的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 ..

我要感谢您的代码,当我查询单个属性时,它对我很有用。

问候

答案 3 :(得分:0)

感谢@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);
    }
}