IQueryRepository

(Documentation still under construction)

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 strategy pattern. NRepository has 2 flavours of strategy patterns that each uses:
  • IQueryStrategy
  • ISpecificationQueryStrategy

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;
        }
    }

IQuerySpecificationQueryStrategy<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", false) & !
                   new TextSearchSpecificationStrategy<Person>(p => p.Name, "Pan", false));
            
            // All persons where Peter is not found in the name
            persons = repository.GetEntities<Person>(
                   !new TextSearchSpecificationStrategy<Person>(p => p.Name, "Peter", false));


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);
        }
    }

Example code

            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));

            // 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();

Last edited Jun 26, 2014 at 10:52 PM by KelbobK5, version 2