Объединение кода 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());
Другие вопросы по тегам