patterncsharpMinor
Specification pattern in EF part 1
Viewed 0 times
specificationpartpattern
Problem
I've been experimenting with different ways of reusing (and naming) the expressions I use in Entity Framework. I've tried static fields with
Firstly, I created a hierarchy for Specifications:
Specification.cs
NotSpecification
```
public class NotSpecification : Specification
{
private readonly Specification specification;
public NotSpecification(Specification specification)
{
if (specification == null)
Expressions and extension methods but neither are perfect. In my latest attempt to solve this problem, I'm attempting to implement the specification pattern!Firstly, I created a hierarchy for Specifications:
Specification.cs
public abstract class Specification
{
public abstract Expression> ToExpression();
public abstract bool IsSatisfiedBy(T target);
public static implicit operator Expression>(Specification specification)
{
if (specification == null)
{
throw new ArgumentNullException(nameof(specification));
}
return specification.ToExpression();
}
public Specification And(Specification other)
{
ValidateForCompose(other);
return new AndSpecification(this, other);
}
public Specification Or(Specification other)
{
ValidateForCompose(other);
return new OrSpecification(this, other);
}
public Specification Negate()
{
return new NotSpecification(this);
}
public static Specification operator ! (Specification specification)
{
if (specification == null)
{
throw new ArgumentNullException(nameof(specification));
}
return specification.Negate();
}
private void ValidateForCompose(Specification other)
{
if (ToExpression() == null)
{
throw new InvalidOperationException(
"Cannot compose an empty specification with another specification.");
}
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
}
}NotSpecification
```
public class NotSpecification : Specification
{
private readonly Specification specification;
public NotSpecification(Specification specification)
{
if (specification == null)
Solution
I might be missing something, but from what I can see
Consider making
Consider exposing operators
Here's an illustration of how I'm imaginging it:
And example client code
IsSatisfiedBy is only ever used by IsSatisfiedBy, and can be removed.Consider making
ToExpression protected, since with the implicit operator I don't see how exposing it is helpful.Consider exposing operators
& and | to make composition of more complicated specifications easier to read and write.Here's an illustration of how I'm imaginging it:
public abstract class Specification
{
protected abstract Expression> Expression { get; }
public static implicit operator Expression>(Specification specification)
{
if (specification == null)
{
throw new ArgumentNullException(nameof(specification));
}
return specification.Expression;
}
public Specification And(Specification second)
{
if (second == null)
{
throw new ArgumentNullException(nameof(second));
}
return new AndSpecification(this, second);
}
public static Specification operator &(Specification first, Specification second)
{
if (first == null)
{
throw new ArgumentNullException(nameof(first));
}
if (second == null)
{
throw new ArgumentNullException(nameof(second));
}
return new AndSpecification(first, second);
}
private sealed class AndSpecification : Specification
{
public AndSpecification(Specification first, Specification second)
{
Expression = first.Expression.And(second);
}
protected override Expression> Expression { get; }
}
// Similarly for or, not.
}And example client code
public class IsSomeExample : Specification
{
protected override Expression> Expression =>
new IsPrivate() | (new IsFromASubscription() & new LinkedPostingIsPublic());
}Code Snippets
public abstract class Specification<T>
{
protected abstract Expression<Func<T, bool>> Expression { get; }
public static implicit operator Expression<Func<T, bool>>(Specification<T> specification)
{
if (specification == null)
{
throw new ArgumentNullException(nameof(specification));
}
return specification.Expression;
}
public Specification<T> And(Specification<T> second)
{
if (second == null)
{
throw new ArgumentNullException(nameof(second));
}
return new AndSpecification(this, second);
}
public static Specification<T> operator &(Specification<T> first, Specification<T> second)
{
if (first == null)
{
throw new ArgumentNullException(nameof(first));
}
if (second == null)
{
throw new ArgumentNullException(nameof(second));
}
return new AndSpecification(first, second);
}
private sealed class AndSpecification : Specification<T>
{
public AndSpecification(Specification<T> first, Specification<T> second)
{
Expression = first.Expression.And(second);
}
protected override Expression<Func<T, bool>> Expression { get; }
}
// Similarly for or, not.
}public class IsSomeExample : Specification<Posting>
{
protected override Expression<Func<Posting, bool>> Expression =>
new IsPrivate() | (new IsFromASubscription() & new LinkedPostingIsPublic());
}Context
StackExchange Code Review Q#138282, answer score: 3
Revisions (0)
No revisions yet.