The Repository Pattern with Linq to Fluent NHibernate and MySQL
I have heard a lot of good things about NHibernate, but have never had the opportunity to use it. In this post I will describe how to get started using Fluent NHibernate with Linq to NHibernate using MySQL for a database.
The Back Story
To add a bit of concreteness to the design / code, let’s imagine that we need to build out the back-end for a delivery truck management system. The business goal is to strap each truck with internet (3G) GPS radios. The radios will report geo-location tracking information for the truck every 5 minutes. In addition, drivers will be scheduled / assigned to the trucks. Here is a simple database schema for our fictional example:
Each truck can have only one driver (one-to-one relationship), but each truck will have many locations (one-to-many relationship).
Installing MySQL
MySQL is a popular open source relational database management system (RDBMS). It is available in Windows and Linux flavors. In fact it is the ‘M’ in the ‘LAMP’ stack. MySQL is used in many high-profile, large-scale products including WIkipedia, Google, and Facebook.
I will be developing for a Windows-based server. You can download the MySQL bits from the developer download page. Be sure to download the following:
- MySQL Community Server – This is the database manager system.
- MySQL Workbench – This is a GUI tool database design and management.
- .NET Connector – This is a .NET based MySQL driver that you can use in your .NET project to interact with the database.
The site also provides a thorough guide to installing MySQL on Windows.
Create the Tables and Columns
Once the MySQL components are installed, start the MySQL Workbench to begin creating the database table. Click the ‘+’ to add a new database schema.
Enter a database name (TruckTracker), click ‘Apply’ and follow the instructions for adding the database.
To add a table double-click the ‘Add Table’ button.
On the ‘Table’ tab, enter a table name.
On the ‘Columns’ tab enter the columns for the table.
Click ‘Apply’, follow the directions and ‘Close’. Repeat these steps to enter the other two tables.
Notice that each table has an auto-indexing column called ‘Id’. This is the primary key for each table. The ‘locations’ and ‘drivers’ table also have a ‘TruckId’ column that will be used as the foreign key. Let’s hook those up.
Right-click the ‘drivers’ table and select the ‘Alter table…’ option. Click the ‘Foreign Keys’ tab and add a foreign key to look like the following:
Click ‘Apply’ and follow the directions to add the relationship between the ‘drivers’ and ‘trucks’ tables. Repeat for the ‘locations’ table.
Now we have completed the database schema.
Adopting a naming convention with your tables and columns makes your schema more clear. It also allows you to more easily write and use your data access code. In the above example I named the tables in the plural form (‘trucks’). When I create my entity I will name it ‘Truck’ since it is a single database row. In addition, I generally have an ‘Id’ as a primary key. Foreign keys columns are XyzId, where Xyz is the other table name in singular form. Foreign key relations ships are named FK_ABC_MNO where ABC is the current table and MNO is the referred table.
The Repository Pattern
There is always a lot of debate on databases (NoSQL vs Relational, SQL Server vs MySQL vs MongoDB) and data access patterns. This example will leverage a Repository pattern to implement this back-end design. The Repository pattern has the following benefits:
- Encapsulates the code that handles the impedance mismatch between the database and the entities. This will actually be handled by NHibernate.
- Hides the persistence implementation (MySQL, SQL Server…etc) details from other code layers. Again, this will be handled by NHibernate.
- Abstracts away the create, read update and delete (CRUD) operations.
- Facilitates DRY implementations by providing a generic implementation for common CRUD operations.
I have seen many twists or slight variations on the Repository pattern. They all eventually define a repository interface that can be used to create concrete implementations. Most of the variations involve factoring the CRUD operations into various interfaces. Here is what I have settled in on:
public interface IReadOnlyRepository<TEntity> where TEntity:class { IQueryable<TEntity> All(); TEntity FindBy(Expression<Func<TEntity, bool>> expression); IQueryable<TEntity> FilterBy(Expression<Func<TEntity, bool>> expression); } public interface IRepository<TEntity> : IReadOnlyRepository<TEntity> where TEntity:class { bool Add(TEntity entity); bool Add(IEnumerable<TEntity> items); bool Update(TEntity entity); bool Delete(TEntity entity); bool Delete(IEnumerable<TEntity> entities); } public interface IIntKeyedRepository<TEntity> : IRepository<TEntity> where TEntity:class { TEntity FindBy(int id); }
I have separated the ability to ‘read’ from ‘write’. This allows certain repositories to provide a ‘read-only’ interface. Notice that the read methods either return the entity type or an IQueryable collection of entity types. This allows LINQ to be leverage to further refine the resulting set. In addition, we can also use LINQ’s delayed execution to provide optimized queries to the database (more later when discussing LINQ to NHibernate).
The read methods also include two methods that accept ‘Expression’ types. These allow the repository to provide flexibility by leveraging LINQ expressions for filtering. The ‘write’ CRUD operations are then provided by implementing the IRepository. The last interface provides a repository with integer type primary keys (like we have in our tables).
The above interfaces really provide re-usable application-level infrastructure and should be contained in their own assembly. Here is a screenshot of the project containing the Repository infrastructure.
Nothing in this project is specific to a database selection or to a particular application. In other words, this is all re-usable goodness. Later we will look at a NHibernate implementation of these interfaces.
Create the Application Entities
The entities are the code objects that represent the data in the database. Tables are generally mapped to classes and columns are generally mapped to properties. However, that rule can (and often is) broken to provide a more application specific entity that can be a combination of multiple data tables / columns.
The entities are passed around the application and will cross application layers (data access, business logic, presentation). To keep these objects re-usable in the various layers, they will also be contained in their own assembly.
Here is the implementation for the entity objects:
public class Driver { public virtual int Id { get; private set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual Truck Truck { get; private set; } } public class Location { public virtual int Id { get; private set; } public virtual DateTime Timestamp { get; set; } public virtual double Latitude { get; set; } public virtual double Longitude { get; set; } public virtual Truck Truck { get; set; } } public class Truck { public virtual int Id { get; private set; } public virtual string Name { get; set; } public virtual string Type { get; set; } public virtual string PlateNumber { get; set; } public virtual Driver Driver { get; private set; } public virtual IList<Location> Locations { get; private set; } public Truck() { Locations = new List<Location>(); } public virtual void AddDriver(Driver driver) { driver.Truck = this; Driver = driver; } public virtual void AddLocation(Location location) { location.Truck = this; Locations.Add(location); } }
Notice the almost direct mapping of the database column names and the entity properties. This is possible only with a good naming convention. All the property and method elements are ‘virtual’ because this is required by NHibernate. NHibernate uses ‘virtual’ elements for the ‘lazy loading’ implementation.
An important feature of these entities, is that they do not betray how the persistence mechanism is implemented. They are clean from CRUD. They are light-weight because they carry only the minimum properties and methods. The ‘Truck’ entity has two helper methods (‘AddDriver’ and ‘AddLocation’) that make some of data access layer code cleaner.
One final thing to notice is instead of mapping the ‘TruckId’ foreign key the ‘Truck’ entity itself is mapped. NHibernate supports lazily loading these relationships (object graph). Same is true for the ‘Locations’ data associated with each truck. A list of ‘Location’ elements will be lazy loaded by NHibernate when we request a ‘Truck’ object.
Installing NHibernate
NHibernate is an ORM, or Object-Relational-Mapper for the Microsoft .NET platform. ORM tools provide a framework for mapping object-oriented domain models to traditional relational database tables / columns. The NHibernate framework can supply most of the common CRUD operations. You can download NHibernate on their Source Forge page. Be sure to download the following:
One of the best ways to improve your “mad code skillz” is to read code. The above image also shows the NHibernate source code is available.
This will get you the NHibernate and the LINQ to NHibernate assemblies. LINQ to NHibernate extends NHibernate to provide optimized queries based upon LINQ expressions that support LINQ’s delayed execution concept.
LINQ to NHibernate plays an important role in our Repository pattern implementation. It allows us to get away with the ‘All’ method and not worry about paging concerns. Obviously we will want to have paging. However, with LINQ we get paging essentially for free by using he ‘Take’ and ‘Skip’ extension methods. LINQ to NHibernate takes the incoming LINQ expression (any Take, Skip, Where…), generates a SQL query and has the database execute this query the first time an element is requested.
Also hop on over to the Fluent NHibernate site and download the latest and greatest. Historically, NHibernate (and many other ORMs) are configured via XML. Fluent NHibernate lets you write mappings in strongly typed C# code. This allows for easy refactoring, improved readability and more concise code.
Entity Mapping with NHibernate
Create a new project to contain all the NHibernate specific code that we are about to generate. Here is a screenshot of my complete project.
The ‘Map’ folder contains all the mappings between my entities and the database tables / columns. This is done using the fluent interface provided by Fluent NHibernate. For more information on Fluent NHibernate visit their wiki. The following is the mapping code.
public class LocationMap : ClassMap<Location> { public LocationMap() { Table("locations"); Id(x => x.Id); Map(x => x.Timestamp).Column("DateTime"); Map(x => x.Latitude); Map(x => x.Longitude); References(x => x.Truck).Column("TruckId"); } } public class DriverMap : ClassMap<Driver> { public DriverMap() { Table("drivers"); Id(x => x.Id); Map(x => x.FirstName); Map(x => x.LastName); References(x => x.Truck).Column("TruckId"); } } public class TruckMap : ClassMap<Truck> { public TruckMap() { Table("trucks"); Id(x => x.Id); Map(x => x.Name); Map(x => x.Type); Map(x => x.PlateNumber); HasOne(x => x.Driver).LazyLoad().Cascade.All(); HasMany(x => x.Locations).LazyLoad().Inverse().Cascade.All(); } }
The power of Fluent NHibernate is that the intent of the above code is fairly clear without knowing a thing about Fluent NHibernate. The only somewhat tricky stuff is mapping the one-to-one (truck to driver) and the one-to-many (truck to locations) relationships.
The NHibernate Repository Implementation
Now it is time to implement the Repository interface using NHibernate. Since the entities are integer keyed we will implement the ‘IIntKeyedRepository’ interface.
public class Repository<T> : NHibernateContext, IIntKeyedRepository<T> where T : class { private readonly ISession _session; public Repository(ISession session) { _session = session; } #region IRepository<T> Members public bool Add(T entity) { _session.Save(entity); return true; } public bool Add(System.Collections.Generic.IEnumerable<T> items) { foreach (T item in items) { _session.Save(item); } return true; } public bool Update(T entity) { _session.Update(entity); return true; } public bool Delete(T entity) { _session.Delete(entity); return true; } public bool Delete(System.Collections.Generic.IEnumerable<T> entities) { foreach (T entity in entities) { _session.Delete(entity); } return true; } #endregion #region IIntKeyedRepository<T> Members public T FindBy(int id) { return _session.Get<T>(id); } #endregion #region IReadOnlyRepository<T> Members public IQueryable<T> All() { return _session.Linq<T>(); } public T FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression) { return FilterBy(expression).Single(); } public IQueryable<T> FilterBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression) { return All().Where(expression).AsQueryable(); } #endregion }
This is a generic implementation that can be used to provide the common CRUD operations for all entity types. This keeps our code DRY and is one of the benefits of the Repository pattern. If there is a need for entity specific logic (business logic), then the Decorator Pattern can be used to extend this generic implementation.
This implementation also inherits from the ‘NHibernateContext’ base class. This base class provides the LINQ context and enables the LINQ features (dynamically generated queries, delayed execution). The above code uses the LINQ features to implement the ‘All’ method.
For the most part, the other methods use features on the NHibernate ISession object. This object is injected into the repository via constructor injection. This sets us up to use an IOC container to control object lifetimes. This is extremely important with the ISession instance.
Configure NHibernate for MySQL
The next process that is necessary is to configure NHibernate to connect to our MySQL database. Once again, Fluent NHibernate will allow us to write ‘fluent’ code to complete this task.
public class NHibernateHelper { private readonly string _connectionString; private ISessionFactory _sessionFactory; public ISessionFactory SessionFactory { get { return _sessionFactory ?? (_sessionFactory = CreateSessionFactory()); } } public NHibernateHelper(string connectionString) { _connectionString = connectionString; } private ISessionFactory CreateSessionFactory() { return Fluently.Configure() .Database(MySQLConfiguration.Standard.ConnectionString(_connectionString)) .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())) .BuildSessionFactory(); } }
The code above creates an instance of NHibernate’s ISessionFactory. This factory is used to create an ISession instance which will be then be injected into our repository implementation.
A Simple NHibernate Unit of Work Implementation
Typical database operations are transactional. That is all the operations are committed if they all succeed or none of the operations are committed if any operations fails. They all go in or none go in. Operations could be stacked up (multiple adds, an update and a delete). Is is usually a good idea to have this transactional code (or unit of work code) factored out. There are many ‘unit of work’ implementations. Some are more complex. However, this one will meet our needs:
public interface IUnitOfWork : IDisposable { ISession Session { get; } void Commit(); void Rollback(); } public class UnitOfWork : IUnitOfWork { private readonly ISessionFactory _sessionFactory; private readonly ITransaction _transaction; public ISession Session { get; private set; } public UnitOfWork(ISessionFactory sessionFactory) { _sessionFactory = sessionFactory; Session = _sessionFactory.OpenSession(); Session.FlushMode = FlushMode.Auto; _transaction = Session.BeginTransaction(IsolationLevel.ReadCommitted); } public void Dispose() { Session.Close(); } public void Commit() { if(!_transaction.IsActive) { throw new InvalidOperationException("No active transation"); } _transaction.Commit(); } public void Rollback() { if(_transaction.IsActive) { _transaction.Rollback(); } } }
In addition to committing the transaction, this code allows the operations to be committed or rolled back.
Putting It All Together
I have put together a few ‘tests’ that demonstrate how to use the Fluent LINQ to NHibernate repository that has been built. These example not only show how to use the code, but also demonstrate the power of the design choices.
First an example of how to save data to the database.
[TestMethod] public void Add_100_Trucks_With_1000_Location_Points_Each() { NHibernateHelper helper = new NHibernateHelper(_connectionString); for (int i = 0; i < 100; i++) { // Notice the unit of work we are using is to commit // one truck's data at a time. UnitOfWork unitOfWork = new UnitOfWork(helper.SessionFactory); Repository<Truck> repository = new Repository<Truck>(unitOfWork.Session); Truck truck = CreateTruck(string.Format("Truck {0}", i + 1), 1000); repository.Add(truck); unitOfWork.Commit(); } } private static Truck CreateTruck(string name, int numberOfLocations) { Truck truck = new Truck { Name = name, PlateNumber = string.Format("ABC-{0}", name), Type = string.Format("Type {0}", name) }; Driver driver = new Driver { FirstName = "Bob", LastName = "Cravens" }; truck.AddDriver(driver); for (int j = 0; j < numberOfLocations; j++) { Location location = new Location { Timestamp = DateTime.Now.AddMinutes(5*j), Latitude = 10.0f + j, Longitude = -10.0f - j }; truck.AddLocation(location); } return truck; }
This test method is clear enough to stand on its own. Do note that what is being ‘Added’ is an object graph containing a truck, driver, and 1000 locations. NHibernate is hiding a lot of complexity by adding the individual entities and ensuring the foreign key relationships are maintained.
The next example shows how to count the total number of ‘Location’ rows in the database.
[TestMethod] public void Count_The_Number_Of_Locations_In_The_DB() { NHibernateHelper helper = new NHibernateHelper(_connectionString); UnitOfWork unitOfWork = new UnitOfWork(helper.SessionFactory); Repository<Location> repository = new Repository<Location>(unitOfWork.Session); // This call uses LINQ to NHibernate to create an optimized SQL query. // So instead of pulling all the entities from the DB and then using // LINQ to count them, it instead pushes the counting to the DB by // generating the appropriate SQL. Much much much faster! int count = repository.All().Count(); }
Here LINQ to NHibernate is optimizing the SQL query to make the database count the rows.
Here are two more examples:
[TestMethod] public void Given_A_Driver_Determine_The_Last_Known_Location() { NHibernateHelper helper = new NHibernateHelper(_connectionString); UnitOfWork unitOfWork = new UnitOfWork(helper.SessionFactory); Repository<Driver> driverRepo = new Repository<Driver>(unitOfWork.Session); Driver driver = driverRepo.All().First(); if(driver!=null) { // At this point LINQ to NHibernate has not loaded all the Location entities. // Because of LINQ's delayed execution, the following query can be optimized // to let the DB do the filtering. Location lastLocation = driver.Truck.Locations.OrderByDescending(x => x.Timestamp).First(); } } [TestMethod] public void Get_The_Last_10_Locations_Of_A_Given_Truck() { NHibernateHelper helper = new NHibernateHelper(_connectionString); UnitOfWork unitOfWork = new UnitOfWork(helper.SessionFactory); Repository<Truck> repository = new Repository<Truck>(unitOfWork.Session); Truck truck = repository.All().First(); if(truck != null) { // Again the power of LINQ to NHibernates optimized queries and delayed execution. IEnumerable<Location> pagedLocations = truck.Locations.OrderByDescending(x => x.Timestamp).Take(10); } }
These also use the power of LINQ to NHibernate to optimize queries and delay the execution. The first shows how the full object graph can be navigated and how to use LINQ to sort the ‘Location’ entries by their timestamp. The second is an example of how we could use the LINQ ‘Take’ (we could also have the ‘Skip’) extension methods can be used to implement paging.
Summary
The NHibernate ORM eco-system provides a really powerful platform to implement your data access layer. Fluent NHibernate allows configuration to occur in your code and LINQ to NHibernate provides the optimized queries and delayed execution that we are used to having with LINQ to SQL. Although there is no visual tooling to create your maps like in LINQ to SQL, that is not a limiting factor. The mapping files are easy to generate and are very clean.
I haven’t finished working though all of this yet, but wanted to say thanks. This really hit the spot for me – the article goes into just the right amount of detail, and the sample code, complete with tests has given me a lot to learn from.
Nice site too!
Very concise article.
How would you hook up the UOW in an MVC application where you would have one UOW per request? Preferably with Windsor
your wrote: I have heard a lot of good things about NHibernate, but have never had the opportunity to use it. In this post I will describe how to get started using Fluent NHibernate with Linq to NHibernate using MySQL for a database.
but how can you teach other if you never used it? :-/
Hi Hung,
I recently integrated this repository into an ASP.NET MVC application using Ninject. You can probably infer from there how it is wired up using Windsor.
<a href=http://blog.bobcravens.com/2010/07/11/AnASPNETMVCTruckTrackerApplicationUsingGoogleMaps.aspx">Use Ninject To Wire It Up</a>
Bob
@Sasha – docendo discitur
Very nice post, I really like it. Thanks
Very nice post, I really like it. Thanks
Hi there, came across your post as I’m working through an implementation of this for a project I’m currently working on. I’m just wondering – how does the Linq select scale for operations on very large databases, seeing as you end up using LINQ to Objects?
I don’t know if I have an exact answer. Fundamentally, LINQ gets transformed to SQL. If you need highly optimized SQL then LINQ might not be the right choice. Some have rolled their own LINQ to Objects equivalent to be better control the SQL / performance. I have not run into a scenario where LINQ didn’t work.
Bob
Hello and firstly nice write up of the repository and unit of work pattern implemented for NHibernate.
Decided to give this a whirl aswell and i’m running against the problem that NH 3.2 does not have a NHibernateContext class, or atleast not where i’m expecting it (Nhibernate.Linq)
And i missing something completely?
One small note by the way, why the generic repository? Why not a normal class with generic methods? So you can use one Repository object to query multiple object types.
Thanks for the kind words. I am not clear on the issues that you ran into using NH 3.2. The Github version was updated for NH 3.2 and I use this code at work. Can you elaborate a bit more on the issue that you are running into? Sorry, this response is so delayed. Hopefully you still remember the details. If not, no problems…my fault.
I find the generic repo pattern useful to get a basic foundation created that allows accessing all database tables. For simple applications, this is most of what I find that I need. More complex apps, may need additional logic and in that case a higher level repository may be in order.
Bob
@F.B. ten Kate, I understand that you can’t see Linq in NHibernate 3 and above because it is deprecated and a new funcionality called IQueryOver , ICriteria are added amongst Linq which is highly faster at executing the queries at the database itself instead of loading all the records and filtering them on the Repository level which Linq does.
I actually got caught up in that class as well with the _session.Linq call. Turns out, that is a reference to NHibernate 2.x. Turns out, so is NHibernateContext is also a 2.x object. AFAIK, ignore it and use _session,Query in lieu of _session.Linq
http://stackoverflow.com/questions/4768212/nhibernate-isession-does-not-contain-a-definition-for-linq
Thanks for the reply Brian. Hope that others find this useful.
What about referencing you entities in RepositoryModule.cs file since it’s not ok to have any reference to entities in the UI. Should I just create an IRepositoryFile.cs and RespositoryFile. cs file of every entity in my core module and then Bind them using ninject, to avoid putting a reference to my entities in the ninject file?
Recently I have been using this generic repository code: https://github.com/rcravens/GenericRepository in my projects. For most simple projects, this is sufficient. I use Ninject to build the various instances (e.g. IDbSessionFactory). Outside of Ninject, the application only knows that an instance of IDbSessionFactory exists. It doesn’t know that it is implemented with NHibernate, EF, or LINQ to SQL.
The biggest goals that I have when using this code are:
1. Hiding the ORM / Database implementation details to all layers beyond the repository. There is an ORM / Database specific implementation, but only the interfaces are passed around the application.
2. I like to have ‘dumb’ entities. In other words, I don’t like having database methods (e.g. save) hanging off the entities. I feel that once entities are striped down to just their properties, they are safe to pass around the application.
Bob
Hello,
I got one question. I am working on new project and i got problem with retrive data to datagridview. I got class osoby (in eng. persons) and Działy (in eng. departments). One person can have 1 Department. In Table osoby i got information like (Name, Surname, and foreign key of Department (Dział) – id from Dział table. I would like to populate the datagridview and its ok, but only for Dział column it retreives the namee of project and entity name (but no values!). How could I retrive those id’s or better way the names from Dzialy table? Please of help.
That’s what i use for now:
IList osoby = dbSession.Linq().ToList();
dgvPersons.DataSource = osoby;
Hi,
this is nice post, i have one question related to tracking system.how i take data from server when my vehicle tracking device send data to server.
Hello !!
Thank’s for the simplicity and the quality of you article. I really apreciate the reusable benefit on th DRY when you creating an abstract class to server as base respository class.
While triying to do it myself, i got the idea to make all my Entity framework classe inherit from a Common class called Identifiable. This class only focus on the capability of classes to have an Id(mapped to the corresponding unique Id in database).
Since i can identify every object with its Id, i can implement a concrete method GetById() like this :
public T GetById(int id)
{
return this.Context.Set().Single(p => p.ID == id);
}
And finally i just have one an generic Class GenericRepository to manage my domain model.
I did something similar. I finally ended up with the following generic repository: https://github.com/rcravens/GenericRepository
I use this as a quick starting point for a lot of my apps.
Hi,
I am implementing Repository Pattern in one of my project based on ASP.NET MVC4 and N-Tier Architecture. I am little confused as how to implement Custom Membership with Repository Pattern. Here are how I have implemented my classes so far.
Generic Repository
public interface IRepository where T : class
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
IQueryable GetAll();
T FindBy(Expression<Func> expression);
IQueryable FilterBy(Expression<Func> expression);
}
Repository Base Class
public abstract class Repository : IRepository where T : class
{
private STNDataContext _stnDataContext;
private readonly IDbSet _dbSet;
protected Repository(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
_dbSet = StnDataContext.Set();
}
protected IDatabaseFactory DatabaseFactory { get; private set; }
public STNDataContext StnDataContext
{
get { return _stnDataContext ?? (_stnDataContext = new STNDataContext()); }
}
public void Add(T entity)
{
_dbSet.Add(entity);
_stnDataContext.Commit();
}
public void Delete(T entity)
{
_dbSet.Remove(entity);
}
public void Update(T entity)
{
_dbSet.Attach(entity);
_stnDataContext.Entry(entity).State = EntityState.Modified;
_stnDataContext.Commit();
}
public IQueryable GetAll()
{
return _dbSet.ToList().AsQueryable();
}
public T FindBy(Expression<Func> expression)
{
return FilterBy(expression).SingleOrDefault();
}
public IQueryable FilterBy(Expression<Func> expression)
{
return GetAll().Where(expression).AsQueryable();
}
User Repository Interface
public interface IUserRepository : IRepository
{
}
User Repository
public class UserRepository : Repository, IUserRepository
{
public UserRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}
Heres my Business Logic Layer
Generic Service Interface
public interface IService
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
IEnumerable GetAll();
T FindBy(Expression<Func> expression);
IEnumerable FilterBy(Expression<Func> expression);
}
Service Base
public class Service : IService where T : class
{
public void Add(T entity)
{
throw new NotImplementedException();
}
public void Delete(T entity)
{
throw new NotImplementedException();
}
public void Update(T entity)
{
throw new NotImplementedException();
}
public IEnumerable GetAll()
{
throw new NotImplementedException();
}
public T FindBy(Expression<Func> expression)
{
throw new NotImplementedException();
}
public IEnumerable FilterBy(Expression<Func> expression)
{
throw new NotImplementedException();
}
}
User Service Interface
public interface IUserService : IService
{
}
User Service Implementation
public class UserService : Service, IUserService
{
private readonly IUserRepository _userRepository;
private readonly IRoleRepository _roleRepository;
public UserService(IUserRepository userRepository, IRoleRepository roleRepository)
{
_userRepository = userRepository;
_roleRepository = roleRepository;
}
public IList GetAllUsers()
{
return _userRepository.GetAll().ToList();
}
public User GetUser(int id)
{
return _userRepository.FindBy(x => x.UserId == id);
}
public User GetUser(string userName)
{
return _userRepository.FindBy(x => x.Username.Equals(userName));
}
public void CreatUser(User user)
{
_userRepository.Add(user);
}
public IList GetUsersForRole(string roleName)
{
return _userRepository.FilterBy(x => x.Role.RoleName.Equals(roleName)).ToList();
}
public IList GetUsersForRole(int roleId)
{
return _userRepository.FilterBy(x => x.Role.RoleId == roleId).ToList();
}
public IList GetUsersForRole(Role role)
{
return GetUsersForRole(role.RoleId);
}
}
Hi Bob,
I am developing one application using ASP.NET MVC4, N-Tier Architecture and Repository Pattern. I want to implement Custom Membership functionality and refering your gpsnerd application as reference. I am little confused implementing the same. Can you help?
I am not very sure on Am I going the right way? If yes how do i implement my UserService class.
If no, what changes do i need to implement.
Any help on this is highly appreciable. Thanks in advance
Nice article ….will you please tell me how i start to learn the nhibernate and linq ……..
Hi Bob! Awesome post!
Im wondering though, in the IUnitOfWork, adding the Session property; doesen’t that couple your interface to NHibernate? Aren’t you supposed to be able to swap out the IUnitOfWork implementation with, say, Entity Framework?
Jeff – I agree. At some point later I did some refactoring and landed here:
https://github.com/rcravens/GenericRepository
I have found this as a great starting point for many applications. I generally pull in the NHibernate implementation, but you could use EF. Also if there is a lot of ‘persistence logic’ then I will generally create non-generic repositories to contain that logic….
public class UserRepository : IKeyedRepository
{
// application specific persistence logic
}
Thanks for the comment.
Wow! In the end I got a web site from where I be able
to genuinely get useful data regarding my study and knowledge.
hellow,why could’nt I open the truckTruker Project, can only open the datalayer part while failed loading the web part , i use vs2012 , i know you may used vs2010 for your project, so how could i fix that problem, It would be great to get your help
NHibernateContext not found in nhibernate 3.3
Great article, Bob! Thank you.
Might sound like an obvious question, but, what’s the point of UoW being disposable if you don’t wrap it in a using statement? Also, I think it makes sense to commit the transaction during the dispose itself, again the purpose being to hide that away in the UoW as the single point of failure, minimum a facade managing ISession/ITransaction. UoW could also then be a provider for a transactional IRepository.
i use the fulent nhibernate ,and it happened to some error,i can’t resolute it,the following is the error.
[ContextInfo]=error performing isolated work;
[Exception]=NHibernate.HibernateException: error performing isolated work —> System.InvalidOperationException: The requested operation cannot be completed because the connection has been broken.
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
at System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName)
at System.Data.SqlClient.SqlInternalConnection.BeginTransaction(IsolationLevel iso)
at System.Data.SqlClient.SqlConnection.BeginDbTransaction(IsolationLevel isolationLevel)
at System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction()
at NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
— End of inner exception stack trace —
at NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
at NHibernate.Transaction.AdoNetWithDistributedTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
at NHibernate.Engine.Transaction.Isolater.DoIsolatedWork(IIsolatedWork work, ISessionImplementor session)
at NHibernate.Engine.TransactionHelper.DoWorkInNewTransaction(ISessionImplementor session)
at NHibernate.Id.TableGenerator.Generate(ISessionImplementor session, Object obj)
at NHibernate.Id.TableHiLoGenerator.Generate(ISessionImplementor session, Object obj)
at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
at SharpArch.NHibernate.NHibernateRepositoryWithTypedId`2.SaveOrUpdate(T entity) in d:BuildsSharpArch2SolutionsSharpArch.NHibernateNHibernateRepositoryWithTypedId.cs:line 191
at Eap.Models.Community.Servers.CommunityService.OperateJoinCoporation(JoinCorporationDto dto) in d:Builds9WICSGWICSG_14D4_appsSourcesWICSGwebbaseeap-appsEap.ModelsCommunityServersCommunityService.cs:line 172****** End ******
****** Start ******
HostName:[WIN-EQLUNES9OJF] [(null)] – [Datetime=2014-10-14 10:06:16:712];[Custom Message]=Community.Service.Services.InsertJoinCoporation;
[ContextInfo]=While preparing select TOP (1) joincorpor0_.JoinCorporationId as JoinCorp1_13_, joincorpor0_.UserId as UserId13_, joincorpor0_.Status as Status13_, joincorpor0_.UserName as UserName13_, joincorpor0_.EnterpriseId as Enterpri5_13_, joincorpor0_.EnterpriseName as Enterpri6_13_, joincorpor0_.Mobile as Mobile13_, joincorpor0_.CorporationId as Corporat8_13_ from Community_JoinCorporations joincorpor0_ where joincorpor0_.UserId=@p0 and joincorpor0_.CorporationId=@p1 an error occurred;
[Exception]=NHibernate.ADOException: While preparing select TOP (1) joincorpor0_.JoinCorporationId as JoinCorp1_13_, joincorpor0_.UserId as UserId13_, joincorpor0_.Status as Status13_, joincorpor0_.UserName as UserName13_, joincorpor0_.EnterpriseId as Enterpri5_13_, joincorpor0_.EnterpriseName as Enterpri6_13_, joincorpor0_.Mobile as Mobile13_, joincorpor0_.CorporationId as Corporat8_13_ from Community_JoinCorporations joincorpor0_ where joincorpor0_.UserId=@p0 and joincorpor0_.CorporationId=@p1 an error occurred —> System.InvalidOperationException: The requested operation cannot be completed because the connection has been broken.
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
at System.Data.SqlClient.SqlDelegatedTransaction.Initialize()
at System.Transactions.TransactionStatePSPEOperation.PSPEInitialize(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
at System.Transactions.TransactionStateActive.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Open()
at NHibernate.Connection.DriverConnectionProvider.GetConnection()
at NHibernate.AdoNet.ConnectionManager.GetConnection()
at NHibernate.AdoNet.AbstractBatcher.Prepare(IDbCommand cmd)
— End of inner exception stack trace —
at NHibernate.AdoNet.AbstractBatcher.Prepare(IDbCommand cmd)
at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd)
at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session)
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes)
at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.List(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.List(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results)
at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results)
at NHibernate.Impl.AbstractSessionImpl.List(IQueryExpression queryExpression, QueryParameters parameters)
at NHibernate.Impl.ExpressionQueryImpl.List()
at NHibernate.Linq.DefaultQueryProvider.ExecuteQuery(NhLinqExpression nhLinqExpression, IQuery query, NhLinqExpression nhQuery)
at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
at Eap.Models.Community.Servers.CommunityService.OperateJoinCoporation(JoinCorporationDto dto) in d:Builds9WICSGWICSG_14D4_appsSourcesWICSGwebbaseeap-appsEap.ModelsCommunityServersCommunityService.cs:line 152****** End ******
hi ,
thanks for the post
Very good man….
that’s professional code