This is post #8 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.
Everything is in place for me to build out the repositories for Neno. These are NHibernate repositories, so the first thing I'll do is create a new project to keep the NHibernate dependency separate. The new project will be StackingCode.Neno.Repositories.NHibernate
.
Fortunately there isn't much to do. Repositories are mostly CRUD, and my base repository is already taking care of that. I only have a few methods to implement, like this one...
public class UserRepository : Repository<User, int>, IUserRepository
{
#region IUserRepository Members
public User GetByOpenIdIdentifier(string identifier)
{
return Session.Query<User>()
.Where(user => user.OpenIds
.Any(openId => openId.Identifier == identifier))
.SingleOrDefault();
}
#endregion
}
I'm enjoying the Linq support which is greatly improved in NHibernate 3.0. I've heard it can't do everything HQL and QueryOver can do, but for now everything is peaches and cream.
There is a small problem regarding posts. Some of them won't be published yet, so I need to make sure the current user can only see what they're supposed to see. Before I can do that, I need to find who the current user is. The IUserService.GetCurrentUser
method isn't implemented yet, but I can assume it will be. And it will be something I'll be using a lot, so I'm going to make a convenience method for it on the User
class.
public class User : Entity<int>
{
// ...
public static User Current
{
get { return Container.Get<IUserService>().GetCurrentUser(); }
}
// ...
}
Now I can create a filter for IQueryable<Post>
using an extension method.
public static class PostRepositoryExtensions
{
public static IQueryable<Post> FilterForCurrentUser(this IQueryable<Post> posts)
{
if (User.Current == null)
return posts.Where(post => post.IsPublished);
if (User.Current.IsAnAdministrator)
return posts;
if (User.Current.IsAnAuthor)
return posts.Where(post => post.IsPublished || post.Author.Id == User.Current.Id);
return posts.Where(post => post.IsPublished);
}
}
The implementation of the User.Current
method is a bit on the nose. Behind the scenes a service is being instantiated or retrieved from a container and a call to NHibernate is being made. Calling it up to four times here (and probably many more times in the rest of the application) could result in a lot of unnecessary hits on the user table. Each NHibernate session keeps a local cache, but it will still check the database to see if the version of the user has been changed by another session. Turning on NHibernate's second level cache will eliminate that problem, however, so I'll leave everything the way it is for now. There's no need to tune for performance until I have to.
If this issue doesn't arise again beforehand it will certainly come back if I ever implement a different ORM, or a different persistence mechanism like ADO.NET or a NoSQL database. If those technologies don't have a secondary cache I will have to rethink how User.Current
works. I suspect the solution will involve my IOC container.
Once the extension method filter is built, the PostRepository
ends up like this...
public class PostRepository : Repository<Post, int>, IPostRepository
{
#region IPostRepository Members
public Post GetByPublishDateAndSlug(DateTime publishDate, string slug)
{
return Session.Query<Post>()
.FilterForCurrentUser()
.Where(post => post.PublishDate.Date == publishDate.Date)
.Where(post => post.Slug == slug)
.SingleOrDefault();
}
public IQueryable<Post> GetList()
{
return Session.Query<Post>()
.FilterForCurrentUser();
}
#endregion
}
I'm not sure I'm happy with having the filter on the GetByPublishDateAndSlug
method. I think I would prefer a "not authorised" message instead of a "not found" message -- I may change that later on.
I'm a bit old school; I like to use XML files for my mappings. FluentNHibernate looks better and better all the time with their fluent and auto mappings, but I like having the power to dive right into a raw mapping file and tweak something if I need to. That doesn't mean I can't use FluentNHibernate to generate the initial XML files for me though. It's much easier to modify the few bits and pieces I want different from the default FluentNHibernate auto mappings than it is to write them all from scratch.
I really want an fnh2hbm.exe
type console application for this, but a quick google doesn't throw up any results. So I'll just roll my own quick and dirty implementation for now.
using System.IO;
using System.Reflection;
using FluentNHibernate.Automapping;
namespace StackingCode.Moja.fnh2hbm
{
public class Program
{
private Program(string assemblyFile, string @namespace)
{
Assembly assembly = Assembly.LoadFrom(assemblyFile);
AutoPersistenceModel autoMap = AutoMap.Assemblies(assembly);
if (@namespace != null)
autoMap = autoMap.Where(type => type.Namespace == @namespace);
autoMap.WriteMappingsTo(Directory.GetCurrentDirectory());
}
private static void Main(string[] args)
{
string assemblyFile = args[0];
string @namespace = args.Length > 1 ? args[1] : null;
new Program(assemblyFile, @namespace);
}
}
}
Running that console application generates five .hbm.xml
files for me, one for each domain class. I'll throw them into StackingCode.Neno.Repositories.NHibernate
as embedded resources where NHibernate can find them later. There are a few manual changes I want to make -- some table and column name changes -- and I'll remove a lot of the types for now to keep the mappings lean and flexible. NHibernate can just use reflection on startup.
That will do for now. There will probably be more changes when I hook it all up to a database. Tomorrow.
There are 0 comments.
Older
Inversion of Control
Newer
The Database
Older
Inversion of Control
Newer
The Database
browse with Pivot
Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue
Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)
Comments
Leave a Comment
Please register or login to leave a comment.