Documentation under construction: (01 Jan 2015)

Latest Versions

NRepository.Core 2.4.1
NRepository.EntityFramework 2.4.0
NRepository TestKit 2.4.0
NRepository.MongoDb 2.0.3

N.B. The current document is being rewritten. As this project is done in my spare time it may take longer than I'd like. However if you want any of the how to guides written first (or have any new suggestions) let me know via the discussion board and i'll try and get them completed first. First come first served on the guides though. This rewrite also includes all of the sample projects so watch this space.

Currently available nuget packages ( available from NuGet.org):

NRepository.Core: Install-Package NRepository.Core
NRepository.EntityFramework: Install-Package NRepository.EntityFramework
NRepository.TestKit: Install-Package NRepository.TestKit
NRepository.MongoDb: Install-Package NRepository.MongoDb

Old Documentation can be found below

############ The following links are currently placeholders only ############

  • Getting Started
    • Overview
    • Queries - Retrieving data using NRepository
      • IQueryRepository
      • QueryStrategies
      • SpecficationStrategies
      • Events
      • Interception
    • Commands - Updating data using NRepository
      • ICommandRepository And IRepository
      • Add, Modify, Delete & Save
      • Events
      • Interception
    • Testing Your Code
      • Overview
      • NRepository.TestKit
  • NRepository Implementations
    • Core
      • Repository Implementations
      • Query Strategies
      • Specification Strategies
      • Interface Extensions
    • EntityFramework
      • Repository Implementations
      • QueryStrategies
      • Events (used for testing, logging etc.)
      • Interceptions
      • Interface Extensions
      • How to ....
        • Adding Property tracking to your application
        • Using projection to simplify your entities and code.
        • Calling stored procedures
        • Turn your DBContext into an in InMemoryDatabase
        • Using Dapper with NRepository
    • MongoDb
      • Interface Extensions
  • NRepository.TestKit
    • Repository Implementations
  • Extending NRepository
    • Extension through interception
    • Extension through new implementations
    • Queries
    • Commands
  • Samples
    • WebApi
    • MVC
  • How to...
    • Adding caching to your application
    • How to stop entities bleed out of your services
    • Adding simple tracking / auditing into your application.
    • Creating / Converting a WCF / OData resource into an NRepository implementation through interception.
    • Mixing Multiple sources into a single repository interface.

########################################################

Strategies.

NRepository provides a strategy driven framework for building CQRS style commands and queries.
Encapsulating common search criteria into strategies improves maintainability as there is only one place where those criteria are defined. Similarly, the purpose of the strategy can be clearly defined removing any ambiguity when choosing which strategy to use. NRepository applies the strategies to a data set retrieved from a repository.

Repositories

A repository represents a source of data. NRepository can interact with a repository by issuing queries to retrieve data or sending commands to manipulate data. These two interactions are managed by two corresponding interfaces.

Interfaces

ICommandRepository

Represents a repository that can have commands run against it.

void Add<T>(T entity)
Task AddAsync<T>(T entity)
void Add<T>(T entity, IAddCommandInterceptor addInterceptor)
Task AddAsync<T>(T entity, IAddCommandInterceptor addInterceptor)
void Modify<T>(T entity)
Task ModifyAsync<T>(T entity)
void Modify<T>(T entity, IModifyCommandInterceptor modifyInterceptor)
Task ModifyAsync<T>(T entity, IModifyCommandInterceptor modifyInterceptor)
void Delete<T>(T entity)
Task DeleteAsync<T>(T entity)
void Delete<T>(T entity, IDeleteCommandInterceptor deleteInterceptor)
Task DeleteAsync<T>(T entity, IDeleteCommandInterceptor deleteInterceptor)
int Save();
Task<int> SaveAsync();
int Save(ISaveCommandInterceptor savingStrategy);
Task<int> SaveAsync(ISaveCommandInterceptor savingStrategy);

Overloads

Each of the above methods can also accept a command interceptor object
There are also async versions of these methods

IQueryRepository

Represents a repository that can have queries run against it
Methods
T GetEntity<T>(…) Returns a single entity using the passed strategies
T GetEntities<T>(…) Returns all entities using the passed strategies
Task<T> GetEntityAsync<T>(…) Async version of GetEntity
Task<List<T>> GetEntitiesAsync<T>(…) Async version of GetEntities

!!!Overloads
The parameters to the GetEntity method and GetEntities method can be combinations of
Predicate An expression
Query Strategy One or more instances of the IQueryStrategy interface
SpecificationStrategy One instance of the ISpecificationStrategy interface

Examples
1) Retrieving data with a predicate

var studentsBornIn1963 = StudentRepository.GetEntities<IStudent>(
	x => x.DateOfBirth.Year == 1963	);
2) Retrieving a single entity with a predicate

var Camilla = _QueryRepository.GetEntity<IStudent>(x => x.Surname == "Parker-Bowles");

3) Retrieving multiple entities without throwing an exception

var noException = _QueryRepository.GetEntity<IStudent>(x => x.StudentType == StudentTypes.Postgraduate, false);

IRepository

This interface is a combination of IQueryRepository and ICommandRepository and is used to provide read/write access to a repository.
Implementation

InMemoryRepository

This implementation of IRepository is used to manipulate data in memory.
Constructors
The constructor takes any combination of:

A collection object containing the initial contents of the repository
An implementation of the IRepositoryEventHandlers interface
An implementation of the IRepositoryInterceptors interface

Example
Building an instance of InMemoryRepository using an array.


var students = new IStudent[]
{
	new Student{
			StudentType = StudentTypes.Postgraduate,
			DateOfBirth = new DateTime(1995, 12, 1),
			FirstName = "Brian",
			Surname = "Blessed" },
	new Student{
			StudentType = StudentTypes.Undergraduate,
			DateOfBirth = new DateTime(1963, 9, 5),
			FirstName = "Charles",
			Surname = "Windsor" },
	new Student{
			StudentType = StudentTypes.Postgraduate,
			DateOfBirth = new DateTime(1981, 12, 25),
			FirstName = "Camilla",
			Surname = "Parker-Bowles"}
};

StudentRepository = new InMemoryRepository(students);

EntityFrameworkQueryRepository

This implementation of IRepository is used to manipulate data via the Entity Framework. It resides in a separate package downloadable from NuGet.
Constructors
EntityFrameworkRepository(dbContext context) Constructs an entity framework repository object using the passed Entity Framework context to access the underlying data
Overrides
The constructor can also accept an implementation of the IRepositoryEventsHandlers and IRepositoryInterceptors interfaces.

Example
Constructing an EntityFrameworkRepository from an Entity Framework DB context.


public class StudentEntity
{
	public int StudentEntityId { get; set; }
	public string FirstName { get; set; }
	public string Surname { get; set; }
	public DateTime DateOfBirth { get; set; }
	public int StudentType { get; set; }
}

public class StudentContext : DbContext
{
	public DbSet<StudentEntity> Students { get; set; }
}

var context = new EntityFrameworkRepository(new StudentContext());

Queries

Queries are specific to the task they perform. Combining the common functionality provided by strategies into a single query allows the query to be specific in its purpose but not re-implement common functionality. NRepository combines strategies into queries by applying them to a data set in the sequence they are defined. As strategies implement IQueryable, the strategies are not applied at the time they are defined, but, rather, at the time the data is retrieved. This means that only the data defined in the combined strategies is retrieved.

Interfaces

IQueryStrategy

This interface defines the functionality that strategies should implement in order to act on the data returned by the repository. Typically, creation of query strategies is performed by inheriting from the QueryStrategy abstract class.

ISpecificationStrategy

This interface generates an expression that can be used to filter the data set returned by the repository. The expressions returned by implementations of this interface can be combined using Boolean operators such as and (&), or (|), not (!). Creation of specification strategies is normally performed by inheriting from the SpecificationQueryStrategy abstract class.
Implementations

SpecificationQueryStrategy

Abstract implementation of ISpecificationQueryStrategy
Methods
Expression<Func<T, bool>> SatisfiedBy() Returns an expression to apply to the data read from the repository. (abstract)

Example
1) A simple specification strategy that restricts the data set to undergraduates


public class StudentIsUndergraduateSpecificationQueryStrategy : SpecificationQueryStrategy>IStudent>
{
	public override Expression<Func<IStudent, bool>> SatisfiedBy()
	{
		return x => x.StudentType == StudentTypes.Undergraduate:
	}
}

2) A more complex specification strategy that takes a parameter

public class StudentIsOlderThanSpecificationQueryStrategy : SpecificationQueryStrategy<IStudent>
{
	private int years;

	public StudentIsOlderThanSpecificationQueryStrategy(int years)
  	{
 		this.years = years;
 	}

	public override Expression<System.Func<IStudent, bool>> SatisfiedBy()
	{
		return x => x.DateOfBirth < DateTime.Now.AddYears(-this.years);
	}
}
Example of using these in a query:


var StudentsEligibleForMatureStudentGrant = StudentRepository.GetEntities<IStudent>(
	new StudentIsUndergraduateSpecificationQueryStrategy() &
	new StudentIsOlderThanSpecificationQueryStrategy(25)	 );

QueryStrategy

Abstract implementation of IQueryStrategy
Methods
IQueryable<T> GetQueryableEntities<T>(object additionalData) // Override to manipulate data from the repository. Use the QueryableRepository property to access the underlying repository (abstract)

Example of a query strategy that projects one interface into another


public override IQueryable<T> GetQueryableEntities<T>(object additionalData)
{
	var query = QueryableRepository.GetQueryableEntities<IStudent>(additionalData);
	return (IQueryable<T>)
		query.Select(
				x =>
				new StudentWithAge
				{
					DateOfBirth = x.DateOfBirth,
					StudentType = x.StudentType,
					Age = DateTime.Now.Year - x.DateOfBirth.Year
				});
}

Example of using these in a query

var StudentsWithAge = StudentRepository.GetEntities<IStudentWithAge>
	(
		new StudentWithAgeQueryStrategy()
	);
// Example of using Query Strategy and Specification Strategy in a single query
var StudentsWithAge = StudentRepository.GetEntities<IStudentWithAge>(
	new StudentIsUndergraduateSpecificationQueryStrategy() &
	new StudentIsOlderThanSpecificationQueryStrategy(25),
	new StudentWithAgeQueryStrategy());

NRepository provides the following strategies as part of the core and entity framework packages.
  • TextSearchSpecificationStrategy
  • ExpressionSpecificationQueryStrategy
  • ExpressionQueryStrategy
  • AggregateQueryStrategy
  • ConditionalAggregateQueryStrategy
  • ConditionalQueryStartegy
  • OfTypeQueryStrategy
  • OrderByDescendingQueryStrategy
  • ThenByDescendingQueryStrategy
  • OrderByQueryStrategy
  • ThenByQueryStrategy
  • PagingQueryStrategy
  • TakeQueryStrategy
  • SkipQueryStrategy
  • ReverseQueryStrategy

Entity Framework Strategies

AsNoTrackingQueryStrategy
EagerLoadingQueryStrategy

Commands

Commands are committed directly against the repository. To support this ICommandRepository defines a suite of methods to support inserts, updates and deletes against the repository, either with our without interception.
The available commands are listed above under the Command Repository section
NRepository and Event Sourcing
NRepository provides a mechanism to raise events in response to the supported commands. The event handlers are passed in when the Repository object is constructed.
Interfaces
IRepositoryEvent
This interface provides a base for any event that is raised by the system. NRepository provides the following implementations:
EntityAddedEvent Raised when an entity is added to the repository using the Add method.
EntityDeletedEvent Raised when an entity is deleted from the repository using the Delete method
EntityModifiedEvent Raised when an entity is modified in the repository using the Modify method
EntitySavedEvent Raised when the changes are committed to the repository using the Save method
RepositoryQueryEvent Raised when the repository is queried

With the exception of the Query Event, the implementation stores the entity that the command is working on.

IRepositorySubscribe

Classes that implement this interface define a method for handling a specific type of event, as indicated by the generic type.
Methods
void Handle<T>(T repositoryEvent) // Called by NRepository when an event has been raised. The type of T must implement the IRepositoryEvent interface.
Example
A sample handler for the Add Entity event

public void Handle(EntityAddedEvent repositoryEvent)
{
	var addedStudent = repositoryEvent.Entity as StudentEntity;

	if (addedStudent != null)
	{
		newStudents.Add(addedStudent);
	}
}

NRepository also provides default implementations of each handler.

IRepositoryEventsHandlers

Implementations of this interface define a handler for each of the events that are raised when commands and queries are invoked against the repository. Implementations of this interface will supply an implementation of IRepositorySubscribe for each of the possible events.

Properties
IRepositorySubscribe<EntityAddedEvent> // EntityAddedEventHandler Gets the event handler to call when entities are added
IRepositorySubscribe<EntityDeletedEvent> // EntityDeletedEventHandler Gets the event handler to call when entities are deleted
IRepositorySubscribe<EntityModifiedEvent> // EntityModifiedEventHandler Gets the event handler to call when entities are modified
IRepositorySubscribe<RepositorySavedEvent> // RepositorySavedEventHandler Gets the event handler to call when changes are committed to the repository
IRepositorySubscribe<RepositoryQueryEvent> // RepositoryQueriedEventHandler Gets the event handler to call when the repository is queried

Example
Notifying an external system when manipulating students

public class StudentAddedHandler : IRepositorySubscribe<EntityAddedEvent>
{
	private IRoomManagement examHall;
	public void Handle(EntityAddedEvent repositoryEvent)
	{
		var studentEntity = repositoryEvent.Entity as StudentEntity;
		if (studentEntity != null)
		{
			this.examHall.AddStudentToExamRoom(studentEntity);
		}
	}
}

public class StudentDeletedHandler : IRepositorySubscribe<EntityDeletedEvent>
{
	private IRoomManagement examHall;
	public void Handle(EntityDeletedEvent repositoryEvent)
	{
		var studentEntity = repositoryEvent.Entity as StudentEntity;		
		if (studentEntity != null)
		{
			this.examHall.RemoveStudentFromExamRoom(studentEntity);~
		}
	}
}

public class StudentModifiedHandler : IRepositorySubscribe<EntityModifiedEvent>
{
	private IRoomManagement examHall;
	public void Handle(EntityModifiedEvent repositoryEvent)
	{
		var studentEntity = repositoryEvent.Entity as StudentEntity;
		if (studentEntity != null)
		{
			this.examHall.ModifyStudentDetailsForExamRoom(studentEntity);
		}
	}
}

public class EventHandlersRegistration : IRepositoryEventsHandlers
{
	public IRepositorySubscribe<EntityAddedEvent> EntityAddedEventHandler
	{
		get { return new StudentAddedHandler(); }
	}
	public IRepositorySubscribe<EntityDeletedEvent> EntityDeletedEventHandler
	{
		get { return new StudentDeletedHandler(); }
	}
	public IRepositorySubscribe<EntityModifiedEvent> EntityModifiedEventHandler
	{
		get { return new StudentModifiedHandler(); }
	}
	public IRepositorySubscribe<RepositorySavedEvent> RepositorySavedEventHandler
	{
		get { return new DefaultRepositorySavedHandler(); }
	}
	public IRepositorySubscribe<RepositoryQueryEvent> RepositoryQueriedEventHandler
	{
		get { return new DefaultQueryEventHandler(); }
	}
}

var context = new EntityFrameworkRepository(new StudentContext(), new EventHandlersRegistration());

Interceptors

For query and command activities, NRepository provides a mechanism to intercept the request and act on it modifying the request if necessary. Interceptors can be specified when creating the repository or when invoking a command or query. An interceptor has full control over the data that gets passed to the repository including the responsibility to perform the action that it has intercepted. As a result, all interceptors accept an Action parameter that should be invoked to carry out the activity that was intercepted. NRepository also provides default implementations of each interceptor.
Interfaces

IAddCommandInterceptor

Classes that implement this interface can intercept Add commands on the repository
Methods
void Add<T>(ICommandRepository repository, Action<T> addAction, T entity) // Called to intercept an Add command on the repository. The addAction parameter should be invoked to add the entity to the repository

IModifyCommandInterceptor

Classes that implement this interface can intercept Modify commands on the repository
Methods
void Modify<T>(ICommandRepository repository, Action<T> modifyAction, T entity) // Called to intercept a Modify command on the repository. The modifyAction should be invoked to modify the entity in the repository

IDeleteCommandInterceptor

Classes that implement this interface can intercept Delete commands on the repository
Methods
void Delete<T>(ICommandRepository repository, Action<T> deleteAction, T entity) //Called to intercept a Delete command on the repository. The deleteAction should be invoked to modify the entity in the repository

ISaveCommandInterceptor

Classes that implement this interface can intercept Save commands on the repository
Methods
int Save(ICommandRepository repository, Func<int> saveFunc) // Called to intercept a Save command on the repository. The saveFunc should be invoked and returned to persist the changes

IQueryInterceptor

Classes that implement this interface can intercept Query commands on the repository
Methods
IQueryable<T> Query<T>(IQueryRepository repository, IQueryable<T> query, object additionalQueryData)// Called to intercept a query passed to the repository. Pass the query parameter into the GetEntities method of the repository to invoke the original query

Example
1) Command interceptor that modifies a field in an entity

public class AddStudentInterceptor : IAddCommandInterceptor
{
	public void Add<T>(ICommandRepository repository, Action<T> addAction, T entity) where T : class
	{
		if (entity is StudentEntity)
		{
			var studentEntity = entity as StudentEntity;
			if ((studentEntity.FirstName == "Derek") && (studentEntity.Surname == "Coleman"))
			{
				studentEntity.DateOfBirth = DateTime.Now.AddYears(-21);
			}
		}
		addAction.Invoke(entity);
	}
}
2) Query interceptor that filters the requested query


public class StudentQueryInterceptor : IQueryInterceptor
{
	public IQueryable<T> Query<T>(IQueryRepository repository, IQueryable<T> query, object additionalQueryData) where T : class
	{
		if (typeof (T) == typeof (StudentEntity))
		{
			return repository.GetEntities<T>(((IQueryable<StudentEntity>) query).Where(x => x.FirstName != "Camilla"));
		}

		return repository.GetEntities<T>(query);
	}
}

3) Using an interceptor when adding an entity

var repository = new EntityFrameworkRepository(new StudentContext());

var newStudent = new StudentEntity
{
	DateOfBirth = new DateTime(1923, 9, 5),
	FirstName = "Philip",
	Surname = "Mountbatten",
	StudentType = 0
};

repository.Add(newStudent, new AddStudentInterceptor());

Defining Interceptors at Repository Construction

NRepository also allows interceptors to be defined when a repository is constructed by passing an implementation of the IRepositoryInterceptors interface.

Interfaces

IRepositoryInterceptors

This interface provides a property for each type of interceptor.
Properties
IAddCommandInterceptor AddCommandInterceptor //Gets the interceptor for add commands against the repository
IDeleteCommandInterceptor DeleteCommandInterceptor // Gets the interceptor for delete commands against the repository
IModifyCommandInterceptor ModifyCommandInterceptor // Gets the interceptor for modify commands against the repository
ISaveCommandInterceptor SaveCommandInterceptor // Gets the interceptor for save commands against the repository
IQueryInterceptor QueryInterceptor // Gets the interceptor for queries run against the repository

Example
Creating a repository with a set of interceptors.

public class StudentRepositoryInterceptors : IRepositoryInterceptors
{
	public IAddCommandInterceptor AddCommandInterceptor
	{
		get { return new AddStudentInterceptor(); }
	}
	public IDeleteCommandInterceptor DeleteCommandInterceptor
	{
		get { return new DefaultDeleteCommandInterceptor(); }
	}

	public IModifyCommandInterceptor ModifyCommandInterceptor
	{
		get { return new DefaultModifyCommandInterceptor(); }
	}
	public ISaveCommandInterceptor SaveCommandInterceptor
	{
		get { return new DefaultSaveCommandInterceptor(); }
	}
	public IQueryInterceptor QueryInterceptor
	{
		get { return new StudentQueryInterceptor(); }
	}
}

var context = new EntityFrameworkRepository(new StudentContext(), new StudentRepositoryInterceptors());

Last edited Jan 2, 2015 at 8:08 PM by KelbobK5, version 1