Это скорее академическое любопытство, но я пытаюсь понять, как лучше всего выполнить следующее.
Представьте себе ситуацию, когда у вас есть объект Person
public class Person {
public string Name {get;set;}
public int Age {get;set;}
}
и контракт репозитория для их извлечения из некоторого хранилища постоянных данных ...
public class IPersonRepository {
public IEnumerable<Person> Search(*** SOME_METHOD_SIGNATURE ***);
}
Ваше потребительское приложение действительно не заботится о конкретной реализации . Он просто получит правильную конкретную реализацию из Unity / Ninject и начнет запросы.
IPersonRespository repo = GetConcreteImplementationFromConfig();
repo.Search( ... );
Что мне интересно, так это то, что вы бы использовали здесь для своей подписи метода, которая является гибкой и расширяемой независимо от реализации.
Вариант 1.
public IEnumerable<Person> Search(Expression<Func<Person, bool>> expression);
Это хорошо, потому что если вы используете контекст данных с поддержкой LINQ (например, EntityFramework), вы можете просто передать выражение непосредственно в свой контекст. Этот вариант, похоже, не работает, хотя, если ваша реализация должна использовать вручную созданные хранимые procs / sql / ADO.NET и т. Д.
Вариант 2.
public IEnumerable<Person> Search(PersonSearch parameters);
public class PersonSearch {
public int? Age {get;set;}
public string FullName {get;set;}
public string PartialName { get; set; }
}
Этот вариант кажется наиболее гибким (в том смысле, что он будет работать как с Linq, так и с обычным старым SQL.
Но от этого просто воняет «написанием собственного языка запросов», потому что вам нужно учитывать каждый возможный запрос, который может захотеть выполнить потребитель, например Age> = 18 && Возраст <= 65 && Имя LIKE '% John%'
Вариант 3.
Есть ли другие варианты?