简体   繁体   中英

Convert Func<T1,bool> to Func<T2,bool>

I have a DTO's and POCO's, and i use Func<T,bool> for "where". DTO and POCO entities have the identical properties (name and type of property).

I have managed to convert - translate DTO Expressions to POCO expressions using ExpresionsVisitor class.

I use expressions to include related entities (entity.Include) and Func as where.

There is a code example bellow, and i need help on implementing Translate method.

How can i do this?

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; }
    }

You can't "translate" a function. You need to implement it because of the strong typing. What you need is a translator for ADTO into A, which is trivial:

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

Then you need to understand what you are actually doing. The "function" is an execution upon data, it is not data itself. So you need two implementations, but they are identical:

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

Next, you need to unify them. This is the real culprit. What you need is to tell the compiler who is who, choose one of these cases (just one, not all of them):

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

What I would advise is to go for the abstract version, it has several advantages. At that point you can do this:

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

And use one function implementation for all derivatives, A and ADTO included.

Obviously, the abstract version will contain the common properties, that is ALL you have shown: Id and Name. I understand you'll have an awful lot more properties and types in a real world application.

What you really want is to abstract away the DTO functionality from the payload so you can implement a generic repository. That's what I've done years ago, at least. The problem later on is injecting lambdas to filter data independently of the derived type, but it is possible.

Sooner or later you'll see that you will have to understand very (VERY) well these arguments: covariance and contravariance

I can give you a little more help:

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


EDIT 2:

To reply to the comment how I implemented CRUD: I didn't.

CRUD is a concept of DAO, just as DTO is. In my code, I used the repository pattern and unit of work, which are a generalization and do without a facade.

This is a quick link to explain the difference: http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/

The repository is used this way:

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

As you can see in 3 lines you can implement a query that is translated into SQL:

SELECT * FROM Customers WHERE Age >= @minimumAge;

The final problem when implementing a generic repository is the injection of the filter predicate. A pointer to the solution is that the predicate really is something like this:

Func<bool, T>

Where T is the actual entity type (eg Customer ). To query customers it will be:

Func<bool, Customer>

The generic repository will then be:

Repository<Customer>

That said, implementing the Repository<T> is a blood bath because you need to derive all entities from a unique class ( AbstractDomainObject in my case) and it has to be compatible with the actual Entity Framework.

The AbstractDomainObject is pretty much the same you wrote:

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;
        }
    }
}

At some point, covariance and contravariance gets really complex when you have the AbstractFactory , Repository<T> and AbstractDomainObject .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM