Объединение кода 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. Но я обнаружил, что такие решения редко возможны в чем-либо, кроме самых тривиальных сценариев.

Что вы делаете?


person cbp    schedule 26.08.2011    source источник
comment
Связанный пост - Реализация шаблона спецификации Entity Framework   -  person RBT    schedule 01.03.2019


Ответы (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());
person Eranga    schedule 26.08.2011
comment
По сути, у вас должна быть эквивалентность спецификации linq. - person Neelesh; 26.08.2011
comment
Хорошо, да, это работает, пока ваш ORM может обрабатывать требуемую спецификацию LINQ. Я думаю, это зависит от ORM, но это может быть сложно. - person cbp; 26.08.2011