2014-01-26 3 views
1

У меня есть DTO и POCO, и я использую Func<T,bool> для «где». Объекты DTO и POCO имеют одинаковые свойства (имя и тип свойства).Convert Func <T1,bool> to Func <T2,bool>

Мне удалось преобразовать - преобразовать выражения DTO в выражения POCO, используя класс ExpresionsVisitor.

Я использую выражения для включения связанных объектов (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; } 
    } 
+0

Измените 'public class ADTO' на' public class ADTO: A', и все готово;) Можете ли вы каким-либо образом преобразовать A в ADTO? (Я имею в виду, что вам не нужно вручную переписывать значения из одного в другое), подразумевается ли явное или явное литье? –

+0

Почему бы вам просто не скопировать свойства? Кажется тривиальным. Должна быть причина, по которой вы не можете этого сделать. – usr

+0

@AleksandarToplek DTO в этом примере - это сервисные контракты, а POCO - мои классы EF. Это преобразование выполняется в отдельном слое. Прямой связи между классами EF и контрактами нет. Работа выполняется в этом специализированном слое. – Olinad

ответ

2

Вы не можете «перевести» функцию. Вы должны реализовать его из-за сильной типизации. Что вам нужно, это переводчик ADTO в А, который является тривиальным:

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 от полезной нагрузки, чтобы вы могли реализовать общий репозиторий. Это то, что я сделал много лет назад, по крайней мере. Впоследствии проблема заключается в том, чтобы вводить lambdas для фильтрации данных независимо от производного типа, но это возможно.

Рано или поздно вы увидите, что вы должны понимать очень (ОЧЕНЬ) хорошо эти аргументы: covariance and contravariance

Я могу дать вам немного больше помощи:

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


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

окончательной проблемы при реализации унифицированного хранилища является инъекцией Filte r предикат. Указатель на решение является то, что предикат действительно что-то вроде этого:

Func<bool, T> 

Где Т фактический тип объекта (например, Customer). Для запроса клиентов будет:

Func<bool, Customer> 

Родовое хранилище будет тогда:

Repository<Customer> 

Тем не менее, осуществление Repository<T> является ванну крови, потому что вы должны получить все объекты из уникального класса (AbstractDomainObject в моем случае), и это должно быть совместимым с фактической структурой Entity Framework.

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

В какой-то момент, ковариации и контрвариантность становится очень сложным, когда у вас есть AbstractFactory, Repository<T> и AbstractDomainObject.

+0

Да. вы поняли, чего я хочу. У меня уже есть абстрактный класс переноса, который определяет поведение и интерфейс Facade в моем переносном слое, подверженном воздействию уровня wcf - операции CRUD. Если я правильно понял вас, у меня не было бы общего переводчика Func? У меня, должно быть, есть специальная реализация «где» с обеих сторон? Например. GetAllById, GetAllByName в моем интерфейсе Facade? Не могли бы вы уточнить, как вы это сделали в своем проекте? – Olinad

+0

Красивый подход. Но я должен сделать это. Мой общий прикладной уровень данных выглядит примерно так: http://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/. – Olinad

Смежные вопросы