You’ve probably seen it if you are reading this, but if you haven’t then go visit www.tenprinthello.com – links to many of the projects and sites I’ve put on the web.
Wednesday, 28 July 2010
Thursday, 22 July 2010
XML-free …compiled mappings WITHOUT Fluent NHibernate – sample project available
In the earlier posts in the series, we saw how to write conventions that map Id and properties on domain objects automatically. The point of the series is to show that writing your own set of conventions is actually a pretty simple if you are familiar with System.Reflection, and you can have a very fine grained level of control over your NHibernate mappings. To this end, I’ve created a sample project for download on google code.
The project contains a simple domain model and a suite of convention that generate NHibernate mappings for identities, properties and many-to-one relationships for the domain model, and should provide a good starting point for your own convention suite. Just run the project and you can see the generated xml.
Wednesday, 21 July 2010
Reading CSVs into anonymous types
Following on from my post on using anonymous types to build strongly typed sql queries, here’s one on extracting data from csv files into a data structure of your choosing.
Suppose we have a CSV file like this:
Date,Bank,Account,Description,In,Out,Category
"2010-07-21","Halifax Online (UK)","Halifax Current Account","Direct Debit - Ukonline","","£9.99","Utilities & bills"
"2010-06-21","Halifax Online (UK)","Halifax Current Account","Direct Debit - Ukonline","","£9.99","Utilities & bills"
Using my snazzy new method, we can parse it into an array of anonymous-typed objects using some code like this:
Wow. Notice that the DateColumn is an actual DateTime object, not just a string. So what just happened? Basically, the type defined in the call to FromCsv is interrogated using reflection, the name of each parameter is matched up to the correct column from the CSV file, and the value converted into the correct Type using Convert.ChangeType.
Here’s the code. Do with it what you will.
IEnumerable<T> FromCsv<T>(T template, string csv)
{
using (var ms = new MemoryStream()){
using (var sw = new StreamWriter(ms)){
sw.Write(csv);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
var tfp = new Microsoft.VisualBasic.FileIO.TextFieldParser(ms){
Delimiters = new[] {","}, HasFieldsEnclosedInQuotes = true
};
var headers = tfp.ReadFields();
var @params = typeof(T).GetConstructors().Single().GetParameters().Select (p => new {Type = p.ParameterType, Index = Array.IndexOf(headers, p.Name)}).ToArray();
while(!tfp.EndOfData){
var data = tfp.ReadFields();
yield return (T) Activator.CreateInstance(typeof(T), @params.Select(p => Convert.ChangeType(data[p.Index], p.Type)).ToArray());
}
}
}
}
Note the use of TextFieldParser, the MS way to get at your CSVs. If you don’t like the VisualBasic namespace, feel free to do it some other way.
Friday, 16 July 2010
XML-free convention based compiled mappings WITHOUT Fluent NHibernate – Part 3 –
So in part 2, we came up with code to find and sort our conventions. Now we need some conventions! In my model , I use hilo int Id {get; private set;} properties on my entity classes, mapped to a column called {EntityName} + Id. If you do something different, write your own convention!
First up, the IClassConvention interface:
public interface IClassConvention
{
void Apply(Type entityType, @class classElement, IEnumerable<Type> entityTypes, hibernatemapping mapping);
}
As you can see, the current entityType and its class mapping are passed in, along with references to the other entity types and the whole mapping document. This lets you do things like check to see if a particular property is an Entity or a component.
public class CreateHiloIdIfTypeHasIntIdProperty : IClassConvention
{
public void Apply(Type entityType, @class classElement, IEnumerable<Type> entityTypes, hibernatemapping mapping)
{
//use reflection to get the Id property from the current class
var idProperty = entityType.GetProperty("Id", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (idProperty != null)
{
//if the id property exists, add a new id element to the @class element
classElement.id = new id()
{
name = "Id",
generator = new generator() {@class = "hilo"},
column = {new column() {name = entityType.Name + "Id"}}
};
}
}
}
Remember, you can write your own convention for this. You could get really clever with this: imagine you use Identity generated columns, but the names are inconsistent in the db – why not look up the table schema and find the identity column and use the name from that?
Here is a convention for turning properties on a type into properties on the @class.
public class CreateBasicProperty : IClassConvention, IRunAfter<CreateHiloIdIfTypeHasIntIdField>, IRunAfter<CreateHiloIdIfTypeHasIntIdProperty>
{
//list of types. If you need longs, just add them!
private readonly IList<Type> basicTypes = new[] { typeof(Guid), typeof(Guid?), typeof(int), typeof(int?), typeof(string), typeof(bool), typeof(bool?), typeof(DateTime), typeof(DateTime?) }.ToList().AsReadOnly();
public void Apply(Type type, @class @class, IEnumerable<Type> entityTypes, hibernatemapping mapping)
{
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.Name != @class.id.name) //check its not the Id property!
.Where(p => basicTypes.Contains(p.PropertyType));
foreach (var propertyInfo in properties)
{
@class.property.Add(new property()
{
name = propertyInfo.Name,
});
}
}
}
That’s all for this post! Next time we’ll see a convention for doing many-to-one mappings.
UPDATE: A sample project is available for download on google code. It contains the convention for many-to-one mapping.
Thursday, 15 July 2010
XML-free convention based compiled mappings WITHOUT Fluent NHibernate – Part 2 – calling the conventions
In part 1 we found out how to generate a mapping file using c# and Linq To XSD. In this post we will extend that to show the use of conventions.
The first thing we need an automapping framework to do is to create a class element in our xml mapping file for each of the entity types in our project. We will need a list of the entities in our project:
var types = typeof(User).Assembly.GetTypesSafe().Where(t.Namespace.StartsWith("Servit.Domain.Entities")).ToList();
var mappingXDoc = new hibernatemapping();
foreach (var type in types)
{
var @class = new @class()
{
name = type.AssemblyQualifiedName,
table = type.Name + "s", //feel free to use a more advanced pluralization method (http://bit.ly/b98JK6) – adding an s works for me!
};
mappingXDoc.@class.Add(@class); //LINQ to XSD didn't pluralize the @class collection,
//it might have been better if it generated mappingXDoc.classes instead of mappingXDoc.@class...
}
A pretty simple convention, but it will do for now. So far we have a mapping document with all our classes, but they are all empty – no properties! We need some conventions. The idea is that these conventions should be easy peasy to write so that you don’t need a big framework like FNH to get your mapping written.
Imagine we want to add conventions to do the following:
- add an Id element for each class with an Id property, mapped to a entity.Name + "Id" column in the db
- add property elements for each int, string, bool etc. etc. except the id property
- Add many-to-one properties automatically
Lets suppose we have an interface IClassConvention. We’re going to get hold of the conventions and apply them to the mappingXDoc variable we defined in the bootstrap code from part 1. Note the use of the TopologicalSort method from my last post, called via an extension method, because we want the conventions to execute in a certain order.
//get all the convention types in our assembly
var conventions = typeof(IClassConvention).Assembly.GetTypesSafe()
.Where(t => typeof (IClassConvention).IsAssignableFrom(t))
.Where(t => t.CanBeInstantiated()) //check they aren't abstract or have open generic types
.TopoSort((t, potentialTypes) => potentialTypes.Where(pt => typeof (IRunAfter<>).MakeGenericType(t).IsAssignableFrom(pt)))
.Select(t => (IClassConvention)Activator.CreateInstance(t)).ToList(); //and instantiate them
foreach (var convention in conventions)
{
foreach (var type in types)
{
var @class = mappingXDoc.@class.Single(c => c.name == type.AssemblyQualifiedName);
convention.Apply(type, @class, types, mappingXDoc);
}
}
OK, so we haven’t got any conventions yet, but you can see how we call them and apply them. This is pretty much the whole “framework” right there. As you can see, there isn’t much to it.
In the next post, we’ll implement a convention or two.
Using topological sort to order rules in rules engines
In this post Ruby-on-Rails, convention over configuration world that we live in, its a natty technique to write a rules engine to help keep your code decoupled and easy to work with.
A typical example might be a global exception handling system. Suppose we want to return a message instead of a YSOD when Application_Error fires. Suppose we have the following code:
public interface IExceptionHandler
{
bool HandleException(HttpContextBase httpContext, Exception ex);
}
class LogErrorExceptionHandler : IExceptionHandler
{
public bool HandleException(HttpContextBase httpContext, Exception ex)
{
//log the exception somehow!
return false;
}
}
class UnknownExceptionHandler : IExceptionHandler
{
public bool HandleException(HttpContextBase httpContext, Exception ex)
{
httpContext.Response.Clear();
httpContext.Response.Status = "500";
httpContext.Response.TrySkipIisCustomErrors = true;
httpContext.Response.Write("There was an error :(");
return true;
}
}
In our global.asax we have something like so:
protected void Application_Error()
{
Exception unhandledException = Server.GetLastError().GetBaseException();
IEnumerable<IExceptionHandler> handlers = SL.Current.GetAll<IExceptionHandler>();
foreach (var exceptionHandler in handlers)
{
if (exceptionHandler.HandleException(new HttpContextWrapper(HttpContext.Current), unhandledException))
{
break;
}
}
}
As you can see, we loop through all the IExceptionHandlers in a chain of responsibility, resolved from our IOC container. If one of them handles the error, we stop. Another example of an exception handler might be one that looked at the exception type, and, if it is a ValidationException, it returns a summary of the validation errors that occurred. Remember though, this is just an example, and you can use a rules engine like this for just about anything (determining discounts or picking notification services or running NHibernate automapping rules)
This is all fine and dandy, but there is a flaw. What order are the handlers going to be executed in? If the logging handler or the validation handler are after the UnknownExceptionHandler, they wont be executed! We need to instill some order in our rules.
Suppose we now create a marker interface IRunAfter<T> and mark up our UnknownExceptionHandler like so:
class UnknownExceptionHandler : IExceptionHandler, IRunAfter<LogErrorExceptionHandler>
{
...
}
By marking up our classes with the IRunAfter class, we can add ordering to our handlers. We need a function to sort them. This type of sort is called a topological sort – essentially turning the IEnumerable into a graph. I did a little hacking on Patrick Dewane’s sort function, and came up with TopologicalSort.cs. The function has this signature:
public delegate IEnumerable<T> GetDependentTypes<T>(T item, IEnumerable<T> potentialDependencies);
public static IEnumerable<T> PerformTopoSort<T>(IEnumerable<T> items, GetDependentTypes<T> getDependentTypes) { ... }
Now, when we resolve our enumerable of IExceptionHandlers, we can call the sort function and get our handlers into the right order.
TopologicalSort.GetDependentTypes<Type> getDependentTypes =
(t, potentialTypes) => potentialTypes.Where(pt => typeof(IRunAfter<>).MakeGenericType(t).IsAssignableFrom(pt));
var handlers = TopologicalSort.PerformTopoSort(SL.Current.GetAll<IExceptionHandler>(), getDependentTypes);
Of course, you can define your own rules for determining getDependentTypes instead of IRunAfter. You can even have it determine the rules at runtime, just by using a different implementation of getDependentTypes. Now that the rules are sorted, we can use them. Good times!
XML-free convention based compiled mappings WITHOUT Fluent NHibernate – Part 1
Fluent NHibernate is a very popular framework, but for me, it has a few issues. I find the API somewhat hard to discover (it is an abstraction over the XML configuration which I know well), and sometimes features I really want aren’t implemented, or take some time to arrive. This of course isn’t the FNH teams fault, but FNH has a huge scope and the extension points aren’t always there.
I ended up writing my own system for typed mappings that I feel is simpler, and allows you to build a mapping framework that does exactly the job you need it to. This series of posts will introduce the approach.
Given the formula domain assemblies + automapping framework = nhibernate mapping.xml it is obvious we will need to somehow manipulate and produce an nhibernate mapping xml file in a structured way, using c# code. The way I do this is to use the LINQ To Xsd project to generate a statically typed representation of an NHibernate mapping file. Here is a simple mapping file, with the classic xml mapping on the left (thanks fincher.org), and Linq To Xsd mapping on the right:
You can see how similar the xml and the code are. This allows you to use easily migrate from xml mapping to code mappings. We can get rid of magic strings and make the mapping refactor proof very easily, e.g. by replacing "NHibernatePets.Pet, NHibernatePets" with typeof(Pet).AssemblyQualifiedName, and using static reflection to get the property names.
To try this out, download nhibernate-configuration.cs and nhibernate-mapping.cs (the LINQ to XSD generated files) and include them in your project, then bootstrap your NHibernate configuration using this snippet:
public static Configuration GetConfiguration(string connectionString)
{
var cfg = new Configuration();
var mappingXDoc = new hibernatemapping()
{
//add your mappings here
};
cfg.SetProperty(NHibernate.Cfg.Environment.Dialect, "NHibernate.Dialect.MsSql2008Dialect");
cfg.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, "NHibernate.Driver.SqlClientDriver");
cfg.SetProperty(NHibernate.Cfg.Environment.ConnectionString, connectionString);
cfg.SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, "NHibernate.Connection.DriverConnectionProvider");
cfg.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName);
cfg.AddXml(mappingXDoc.ToString());
return cfg;
}
Now you should be able to get started with writing typed mappings. Next time we’ll get started on conventions.
