将Func <t1,bool>转换为Func <t2,bool> </t2,bool> </t1,bool>

时间:2014-01-26 15:44:05

标签: c# func

我有一个DTO和POCO,我使用Func<T,bool>代表“where”。 DTO和POCO实体具有相同的属性(属性的名称和类型)。

我设法转换 - 使用ExpresionsVisitor类将DTO表达式转换为POCO表达式。

我使用表达式将相关实体(entity.Include)和Func包含在其中。

下面有一个代码示例,我需要帮助实现翻译方法。

我该怎么做?

public class Program
    {
        static void Main(string[] args)
        {
            ADTO a = new ADTO
            {
                Id = 1,
                Name = "Test"
            };

            Func<ADTO,bool> dtoFunc = (dto => dto.Id == 1);

            Func<A, bool> func = Translate(dtoFunc);
        }

        private static Func<A, bool> Translate(Func<ADTO, bool> dtoFunc)
        {
            // Implementation here
            throw new NotImplementedException();
        }
    }

    public class A
    {
        public int Id { get; set; }

        public String Name { get; set; }
    }

    public class ADTO
    {
        public int Id { get; set; }

        public String Name { get; set; }
    }

1 个答案:

答案 0 :(得分:2)

你不能“翻译”一个功能。由于强类型,您需要实现它。 你需要的是ADTO到A的翻译,这是微不足道的:

private A Translate(ADTO adto)
{
    return new A() { Id = adto.Id, Name = adto.Name);
}

然后你需要了解你实际在做什么。 “函数”是对数据的执行,它不是数据本身。所以你需要两个实现,但它们是相同的:

Func<A, bool> funcA = (a => a.Id == 1);
Func<ADTO, bool> funcADTO = (adto => adto.Id == 1);

接下来,您需要统一它们。这是真正的罪魁祸首。你需要的是告诉编译器谁是谁,选择其中一个案例(只有一个,而不是全部):

A extends ADTO
ADTO extends A
A extends AbstractDataTransferObject, ADTO extends AbstractDataTransferObject
A implements IDataTransferObject, ADTO implements IDataTransferObject

我建议的是抽象版本,它有几个优点。 那时你可以这样做:

Func<AbstractDataTransferObject, bool> f = (adto => adto.Id == 1);

对所有衍生产品使用一个函数实现,包括A和ADTO。

显然,抽象版本将包含公共属性,即您显示的所有属性:Id和Name。我知道你在现实世界的应用程序中会有更多的属性和类型。

您真正想要的是从有效负载中抽象出DTO功能,以便您可以实现通用存储库。这就是我多年前所做的,至少。稍后的问题是注入lambda以独立于派生类型过滤数据,但这是可能的。

迟早你会发现你必须非常(非常)理解这些论点:covariance and contravariance

我可以给你一些帮助:

http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx


编辑2:

回复评论我是如何实施CRUD的:我没有。

CRUD是DAO的概念,就像DTO一样。在我的代码中,我使用了存储库模式和工作单元,这是一种概括,没有外观。

这是解释差异的快速链接:http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/

存储库以这种方式使用:

public List<Customer> GetOlderThan(int minimumAge, bool lazy)
{
    using(Repository<Customer> repo = RepositoryFactory.Create<Customer>(lazy))
    {
        return repo.Retrieve(c => c.Age >= minimumAge);
    }
}

正如您在3行中所看到的,您可以实现一个转换为SQL的查询:

SELECT * FROM Customers WHERE Age >= @minimumAge;

实现通用存储库时的 final 问题是注入过滤谓词。指向解决方案的指针是谓词实际上是这样的:

Func<bool, T>

其中T是实际实体类型(例如Customer)。要查询客户,它将是:

Func<bool, Customer>

通用存储库将是:

Repository<Customer>

那就是说,实施Repository<T>是一个血浴,因为你需要从一个独特的类(AbstractDomainObject在我的情况下)派生所有实体,它必须兼容使用实际的实体框架。

你写的AbstractDomainObject几乎一样:

public abstract class AbstractDomainObject
{
    private int _id = -1;
    public int ID
    {
        get
        {
            if (_id == -1)
            {
                throw new InvalidOperationException("domain object not yet persisted");
            }

            return _id;
        }
        set
        {
            if (_id != -1)
            {
                throw new InvalidOperationException("domain object already persisted");
            }

            _id = value;
        }
    }

    public bool IsPersisted
    {
        get
        {
            return _id != -1;
        }
    }
}

在某些时候,当你拥有AbstractFactoryRepository<T>AbstractDomainObject时,协方差和逆变得非常复杂。