Объединение кода C# и кода базы данных в Спецификации
Иногда вам нужно определить некоторые бизнес-правила, и шаблон спецификации является полезным инструментом. Например:
public class CanBorrowBooksSpec : ISpecification<Customer>
{
public bool Satisfies(Customer customer)
{
return customer.HasLibraryCard
&& !customer.UnpaidFines.Any();
}
}
Тем не менее, я часто нахожу, что мне нужно "вставить" эти правила в SQL для повышения производительности или удовлетворения таких вещей, как постраничные списки записей.
Затем у меня остается необходимость писать код для правил дважды, один раз в коде CLR и один раз в SQL (или языке ORM).
Как вы организовываете подобный код?
Кажется, лучше, если код хранится вместе в одном классе. Таким образом, если разработчик обновляет бизнес-правила, у него меньше шансов забыть обновить оба набора кода. Например:
public class CanBorrowBooksSpec : ISpecification<Customer>
{
public bool Satisfies(Customer customer)
{
return customer.HasLibraryCard
&& !customer.UnpaidFines.Any();
}
public void AddSql(StringBuilder sql)
{
sql.Append(@"customer.HasLibraryCard
AND NOT EXISTS (SELECT Id FROM CustomerUnpaidFines WHERE CustomerId = customer.Id)");
}
}
Однако мне это кажется довольно уродливым, поскольку мы сейчас смешиваем проблемы.
Другой альтернативой может быть использование решения Linq-To-YourORM, поскольку код LINQ может быть либо запущен для коллекции, либо преобразован в SQL. Но я обнаружил, что такие решения редко возможны ни при чем, кроме самых тривиальных сценариев.
Чем ты занимаешься?
1 ответ
Мы использовали шаблон спецификации с Entity Framework. Вот как мы подошли к нему
public interface ISpecification<TEntity>
{
Expression<Func<TEntity, bool>> Predicate { get; }
}
public class CanBorrowBooksSpec : ISpecification<Customer>
{
Expression<Func<Customer, bool>> Predicate
{
get{ return customer => customer.HasLibraryCard
&& !customer.UnpaidFines.Any()}
}
}
Затем вы можете использовать его против LINQ-to-Entities, таких как
db.Customers.Where(canBorrowBooksSpec.Predicate);
В LINQ-to-Objects, как
customerCollection.Where(canBorrowBooksSpec.Predicate.Compile());