I took some time over the weekend to make big changes to my CodeSmith templates for Paul Wilson’s O/R Mapper. The changes are significant, so read on if you use them or plan on using them or are curious about them.
The biggest change is that I am removing the static service classes from the templates and distributing them as stand-alone libraries. Of course, source code will be provided along with DLLs. Considering these classes were rarely changed by users, it makes sense. It also makes the library much more accessible to folks who don’t own CodeSmith — now, CodeSmith is used to generate your project’s entity and service classes, not the framework library. This opens the door for more users of Wilson’s O/R Mapper to use this framework.
As a result, in a clean distribution, you would have the following:
- CodeSmith templates to generate your project’s entity classes, service classes, and unit tests
The first four are the stand-alone libraries (assemblies) previously created as part of the CodeSmith templates (things like
TransactionManager, both of which are now called
TransactionService). Only the first two are needed (Services and Entities). The Log4NetIntegration and NLogIntegration assemblies are used only if you want to integrate such logging into your service layer (more on that soon).
The ORMapper.Entities library provides the interfaces and base classes for all your entity objects. Not much has changed in here. All user entity objects derive from
EntityBase<T> (read-only entities) or
PersistableEntityBase<T> (updateable entities). The
IIdentity interface is implemented by your project’s identity classes.
The ORMapper.Services library provides the service classes to do the actual data retrieval and manipulation. It includes the singleton classes
DataService (the service that exposes the underlying Wilson O/R mapper engine),
TransactionService (simple transaction management),
RetrieveService<T> (a generic class for retrieving entity objects), and
PersistService<T> (a generic class for persisting entity objects).
ServiceLayerConfiguration class allows configuration settings to be provided in your application’s config file. This is a standard configuration handler that you register in your configuration file as follows:
<configuration> <configsections> <section name="serviceLayer" type="ORMapper.Services.ServiceLayerSettings,ORMapper.Services" /> </configsections> <servicelayer mappingsFile="Mappings.config" connectionString="ORMapper" logger="" /> </configuration>
The values shown above are default values. Unlike in the past, your Mappings.config file must be copied to your output directory on compilation (in the past it was an embedded resource).
log4net as your logger, and by referencing the appropriate integration library assembly with your project, logging will be added automatically using the appropriate logger. This method allows easy switching between libraries and opens the door for adding other logging mechanisms in the future.
NLog integration will use NLog’s standard methods for finding a configuration file (application config, nlog.config, etc.). Log4Net integration will by default look in the application configuration file for settings unless you specify the name of your configuration file in an application settings key named
log4net-config-file, as shown below.
<appsettings> <add key="log4net-config-file" value="log4net.config" /> </appsettings>
The above would tell log4net to get its configuration from a file named log4net.config (by default in your application’s startup directory).
There are two CodeSmith templates provided. The first, MappingsFile.cst, will generate the Mappings.config file based on a given database. (Unlike in previous distributions, this one works.) The second, ClassGenerator.cst, will generate your project’s entity and service classes based on a Mappings.config file.
The entity classes are much as they were in the past. Partial classes separate auto-generated code so you can safely add functionality without overwriting it by re-running the templates, and an
IIdentity class is created for each entity. Note that the templates will generate uncompilable code if you do not have a primary key specified, so be sure to specify a primary key for each table. (Why you wouldn’t do this anyway, I have no idea.) Entity classes are now generated in a separate project uncluttered by the library code that now exists in the ORMapper.Entities assembly.
The service classes are new, and provide a simpler mechanism to expanding your service layer. Each of your entity classes will be given a (singleton) service class, which will be little more than a strongly-typed wrapper around the
PersistService<T> class. Partial classes are used to separate auto-generated code so you can safely add functionality, much like with entity classes.
How does this change things? In the past, you would retrieve and update a User entity by writing the following code.
User user = Retrieve<user>.RetrieveFirst(); user.IsActive = false; Persist</user><user>.Save(user);
With the addition of the service layer, you now do this.
User user = UserService.RetrieveFirst(); user.IsActive = false; UserService.Save(user);
It’s somewhat easier to read, and you can now expand the functionality provided by your UserService class — something you couldn’t do before with the generic classes. Of course, you could still use the generic classes, as the service classes inherit from the base
As for the unit test generator that comes with the templates, I made some small changes and, more importantly, successfully ran the tests (even tested it against both logging systems).
I don’t have an official distribution available for this yet, but I do have source code for you to download that includes the source code for all ORMapper.* projects, CodeSmith templates, and a small example project based on a single table that I’ve used as a test framework. Feel free to check it out and provide feedback.
When will a final distribution be available? I want to test some more, clean up some code, and add some more XML help comments… and get some feedback from the community. Let me know what you think.
(Please don’t send comments via e-mail — post them here!)