IQueryRepository
(Documentation still under construction)
The page contains the following sections:
Introduction
IQueryRepository contains 2 main overloaded methods:
- GetEntity<T>
- GetEntities<T>
The GetEntity<T> method returns a single entity and by default throws an EntitySearchRepositoryException if the result is not equal to 1. The GetEntities<T> returns an list of IQueryable<T> entities. Both methods can be fully customised using the a couple of strategy pattern implementations which are described in the following section
Strategies
Query Strategies are fundamental to IQueryRepository. They allow the data ( via the Queryable )to be filtered and ordered and where Entity Framework is concerned allow for additional statements to be appended to the linq query, statements such as eager loading (include) and no tracking. NRepository has 2 flavours of strategy patterns that ituses:
Each strategy can be chained to create more complex strategies and using the Condition extension method means that these more complex strategies can occur in-line rather multiple if statements to build up the necessary strategy (See main help page for example)
IQueryStrategy
This is a general strategy that can be used to add additional statements to the IQueryable<T> object. Data can be filtered, ordered, using entity framework the eager loading of entities can all be performed using this type of strategy. An implementation of this type of strategy can be seen below:
public class FilterByEffectiveDateProductQueryStrategy : QueryStrategy
{
private readonly DateTime? effectiveDate;
public FilterByEffectiveDateProductQueryStrategy(DateTime effectiveDate)
{
if (effectiveDate != DateTime.MinValue)
this.effectiveDate = effectiveDate;
}
public override IQueryable<T> GetQueryableEntities<T>(object additionalData)
{
if (!typeof(Product).IsAssignableFrom(typeof(T)))
throw new ApplicationException("Only types derived from product can be used with this strategy: " + GetType().Name);
var query = QueryableRepository.GetQueryableEntities<T>(additionalData);
if (this.effectiveDate == null || !this.effectiveDate.HasValue)
return query;
var filteredQuery = query.OfType<Product>().Where(p =>
p.BasicInformationRevisions.Any(
e => e.EffectiveFrom <= effectiveDate &&
(e.EffectiveUntilDate >= effectiveDate || e.EffectiveUntilDate == null)));
return (IQueryable<T>)filteredQuery;
}
}
ISpecificationQueryStrategy<T>
This type of strategy is a derivative of IQueryStrategy but can only be used for filtering data. One of the benefits of this strategy type is that you can combine these strategies using the And, Or & Not operators.
// All persons that contain Peter in the name but not those that also contain Pan
persons = repository.GetEntities<Person>(
new TextSearchSpecificationStrategy<Person>(p => p.Name, "Peter") & !
new TextSearchSpecificationStrategy<Person>(p => p.Name, "Pan"));
// All persons where Peter is not found in the name
persons = repository.GetEntities<Person>(
!new TextSearchSpecificationStrategy<Person>(p => p.Name, "Peter"));
A simple but powerful text search specification strategy can be seen below :
public class TextSearchSpecificationStrategy<TEntity> : SpecificationQueryStrategy<TEntity> where TEntity : class
{
private Expression<Func<TEntity, bool>> _Expression;
public TextSearchSpecificationStrategy(Expression<Func<TEntity, object>> propertyName, string searchString, bool isCaseSensitive = false)
: this(PropertyInfo<TEntity>.GetMemberName(propertyName), searchString, isCaseSensitive)
{
}
public TextSearchSpecificationStrategy(string propertyName, string searchString, bool isCaseSensitive = false)
{
if (String.IsNullOrEmpty(propertyName))
throw new ArgumentException("propertyName is null or empty.", "propertyName");
if (String.IsNullOrEmpty(searchString))
throw new ArgumentException("searchString is null or empty.", "searchString");
IsCaseSensitive = isCaseSensitive;
PropertyName = propertyName;
SearchString = searchString;
_Expression = CreateSearchExpression(PropertyName, SearchString, IsCaseSensitive);
}
public string SearchString
{
get;
private set;
}
public string PropertyName
{
get;
private set;
}
public bool IsCaseSensitive
{
get;
private set;
}
public override Expression<Func<TEntity, bool>> SatisfiedBy()
{
return _Expression;
}
private static Expression<Func<TEntity, bool>> CreateSearchExpression(string propertyName, string searchString, bool isCaseSensitive)
{
var paramExp = Expression.Parameter(typeof(TEntity), "type");
var propExp = Expression.Property(paramExp, propertyName);
var methodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
if (!isCaseSensitive)
{
var valueExp2 = Expression.Constant(searchString.ToUpper(), typeof(string));
var toUpperMethodInfo = typeof(string).GetMethod("ToUpper", new Type[0]);
var upperCall = Expression.Call(propExp, toUpperMethodInfo, null);
var methCall2 = Expression.Call(upperCall, methodInfo, valueExp2);
return Expression.Lambda<Func<TEntity, bool>>(methCall2, paramExp);
}
var valueExp = Expression.Constant(searchString, typeof(string));
var methCall = Expression.Call(propExp, methodInfo, valueExp);
return Expression.Lambda<Func<TEntity, bool>>(methCall, paramExp);
}
}
Included Strategies
Below are a list of the available strategies in both NRepository.Core & NRepository.EntityFramework assemblies,
new TextSearchSpecificationStrategy<Person>(p => p.Name, "Pet", isCaseSensitive:true) |
new ExpressionSpecificationQueryStrategy<Person>(p => true);
new ExpressionQueryStrategy<Person>(p => p.Name == "A Name");
new AggregateQueryStrategy(new DefaultQueryStrategy());
new ConditionalAggregateQueryStrategy(true,new DefaultQueryStrategy());
new ConditionalQueryStrategy(true,new DefaultQueryStrategy());
new OfTypeQueryStrategy(typeof(Person));
new OrderByDescendingQueryStrategy("Name");
new OrderByDescendingQueryStrategy<Person>(p => p.Partner.Name);
new ThenByDescendingQueryStrategy("Name");
new ThenByDescendingQueryStrategy<Person>(p => p.Partner.Name);
new OrderByQueryStrategy("Name");
new OrderByQueryStrategy<Person>(p => p.Partner.Name);
new ThenByQueryStrategy("Name");
new ThenByQueryStrategy<Person>(p => p.Partner.Name);
new PagingQueryStrategy(5,10);
new TakeQueryStrategy(5);
new SkipQueryStrategy(10);
new ReverseQueryStrategy();
// NRepository.Entity Stragegies
new AsNoTrackingQueryStrategy();
new EagerLoadingQueryStrategy("Name");
new EagerLoadingQueryStrategy<Person>(
p => p.Children,
p => p.Partner);
Advanced Features
Documentation 'not yet implemented'
Interceptions, projections and Anti corruption layers
Documentation 'not yet implemented'
Events
Documentation 'not yet implemented'
Sample code
Enjoy ;)
var data = new List<object>
{
new Person{Name = "Billy Bob", Partner= new Person{ Name= "Mrs P. Bob"}},
new Person{Name = "Bobby Bob", Partner= new Person{ Name= "Mrs T. Bob"}},
new Person{Name = "Peter Pan"},
new Person{Name = "Peter Jackson"},
new Person{Name = "Billy Ocean", Partner=new Person{Name = "Phil Collins"}},
new Person{Name = "Peter Parker",Partner = new Person{Name = "Aunt May"}},
new Person{Name = "Bat Man", Partner = new Person{Name = "Robin"}},
"A string which shows",
"data can contain any type of items"
};
var repository = new InMemoryRepository(data);
// Get all entities
var persons = repository.GetEntities<Person>();
// Simple Query
persons = repository.GetEntities<Person>();
// Filtering
persons = repository.GetEntities<Person>(p => p.Name == "Name");
// All persons that contain Peter in the name but not those that also contain Pan
persons = repository.GetEntities<Person>(
new TextSearchSpecificationStrategy<Person>(p => p.Name, "Peter", false) & !
new TextSearchSpecificationStrategy<Person>(p => p.Name, "Pan", false),
new OrderByQueryStrategy("Name"));
// All persons where Peter is not found in the name
persons = repository.GetEntities<Person>(
!new TextSearchSpecificationStrategy<Person>(p => p.Name, "Peter", false));
// none filtering strategies
persons = repository.GetEntities<Person>(
new OrderByQueryStrategy<Person>(p => p.Name));
// ***********************************************************
// ENtityFramework
//
repository = new EntityFrameworkRepository(new MyDbContext());
// Entity framework strategies (Explicit Loading and no tracking)
persons = repository.GetEntities<Person>(
new AsNoTrackingQueryStrategy(),
new EagerLoadingQueryStrategy<Person>(
p => p.Children,
p => p.Partner));
// Explicit Loading
var person = repository.GetEntity<Person>(p => p.Name == "Peter Parker");
repository.Load(person,p => p.Partner);
// Explicit loading with filtering usig expression
person = repository.GetEntity<Person>(p => p.Name == "Peter Parker");
repository.Load(person, p => p.Children, p => p.Name.Count() > 6);
// Explicit loading with filtering usig specification query strategy
person = repository.GetEntity<Person>(p => p.Name == "Peter Parker");
repository.Load(person, p => p.Children, new ExpressionSpecificationQueryStrategy<Person>(p => p.Name.Count() > 6));
// Update the entity state of an object
repository.UpdateEntityState(person, EntityState.Detached);
// Add an entity
repository.Add(new Person());
// Save entities
repository.Save();