For years we (a lot of people I know and myself included) have been using the Unit of Work and Repository pattern combined with each other. This makes quite a lot of sense as, in most cases, they both have something to do with your database calls.

When searching for both of these patterns you’ll often be directed to a popular article on the Microsoft documentation site. The sample code over there has a very detailed implementation on how you can implement both of these patterns for accessing and working with your database. I kind of like this post as it goes in great length to describe both the unit of work- and repository pattern and the advantages of using them. I see a lot of projects/companies having implemented the pattern combo like described in the Microsoft article. I can’t really blame them as it’s one of the top hits when you search for it in any search engine.

There is a downside to this sample though. It violates the Open/Closed principle which states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. Whenever you need to add a new repository to your database context, you also need to add this repository to your unit of work, therefore violating the open/closed principle.

It also violates the Single Responsibility Principle, which states “everymoduleorclassshould have responsibility over a single part of thefunctionalityprovided by thesoftware, and that responsibility should be entirelyencapsulatedby the class. All itsservicesshould be narrowly aligned with that responsibility.” or in short “A class should have only one reason to change.”. The reason why the sample implementation violates this principle is because it is handling multiple responsibilities. The unit of work’s purpose should be to encapsulate and commit or rollback transactions of atomic operations. However, it’s also creating and managing the several repository objects, therefore having multiple responsibilities.

Implementing the unit of work and repository pattern can be done in multiple ways. Derek Greer goes on about this at great length about this in an old post of him. As always there are several ways to improve the design. You might even want to keep the mentioned design in the Microsoft example, because ‘it-just-works’. For the sake of cleaner code I’ll describe one of the ways, which I personally like very much, to improve the software design. By adding a decorator to the project the functional code will be much cleaner.

First thing you have to consider is implementing some form of CQRS in your software design. This will make your live much easier when splitting the command, unit of work and repository functionality. You can perfectly implement the described solution without implementing CQRS, but why would you want to do this?

I’ll just assume you have a command handler in your application. The interface will probably look similar to the following piece of code.

public interface IIncomingFileHandler<in TCommand>
	where TCommand : IncomingFileCommand
{
	void Handle(TCommand command);
}

The actual command handler can be implemented like the following piece of code.

public class IncomingFileHandler<TCommand> : IIncomingFileHandler<TCommand>
    where TCommand : IncomingFileCommand
{
    private readonly IRepository<Customer> customerRepository;
    private readonly IRepository<File> fileRepository;
    
    protected IncomingFileHandler(IRepository<Customer> customerRepository, IRepository<File> fileRepository)
    {
        this.customerRepository = customerRepository;
        this.fileRepository = fileRepository;
    }

    public void Handle(TCommand command)
    {
        //Implement your logic over here.
        var customer = customerRepository.Get(command.CustomerId);
        customer.LatestUpdate = command.Request;
        customerRepository.Update(customer);
        var file = CreateNewIncomingFileDto(command);
        fileRepository.Add(file);

        return;
    }
}

All of the necessary repositories are injected over here so we can implement the logic for this functional area. The implementation doesn’t make much sense, but keep in mind it’s just an example. This piece of code wants to write to the database multiple times. We could implement the call to the SaveChanges() method inside the Update- and Add-methods, but that’s a waste of database requests and you’ll sacrifice transactional consistency.

At this time nothing is actually written back to the database, because the SaveChanges isn’t called anywhere and we aren’t committing (or rolling back) any transaction either. The functionality for persisting the data will be implemented in a transaction handler, which will be added as a decorator. The transaction handler will create a new TransactionScope, invoke the Handle-method of the actual IIncomingFileHandler<TCommand> implementation (in our case the IncomingFileHandler<TCommand>), save the changes and commit the transaction (or roll back).

A simple version of this transaction decorator is shown in the following code block.

public class IncomingFileHandlerTransactionDecorator<TCommand> : IIncomingFileHandler<TCommand> 
    where TCommand : IncomingFileCommand
{
    private readonly IIncomingFileHandler<TCommand> decorated;
    private readonly IDbContext context;

    public IncomingFileHandlerTransactionDecorator(IIncomingFileHandler<TCommand> decorated, IDbContext context)
    {
        this.decorated = decorated;
        this.context = context;
    }

    public void Handle(TCommand command)
    {
        using (var transaction = context.BeginTransaction())
        {
            try
            {
                decorated.Handle(command)

                context.SaveChanges();
                context.Commit(transaction);
            }
            catch (Exception ex)
            {
                context.Rollback(transaction);
                throw;
            }
        }
    }
}

This piece of code is only responsible for creating a transaction and persisting the changes made into the database.

We are still using the repository pattern and making use of the unit-of-work, but each piece of code now has its own responsibility. Therefore making the code much cleaner. You also aren’t violating the open/closed principle as you can still add dozens of repositories, without affecting anything else in your codebase.

The setup for this separation is a bit more complex compared to just hacking everything together in one big file/class. Luckily Autofac has some awesome built-in functionality to add decorators. The following two lines are all you need to make the magic happen.

builder.RegisterGeneric(typeof(IncomingFileHandler<>)).Named("commandHandler", typeof(IIncomingFileHandler<>));
builder.RegisterGenericDecorator(typeof(IncomingFileHandlerTransactionDecorator<>), typeof(IIncomingFileHandler<>), fromKey: "commandHandler");

This tells Autofac to use the IncomingFileHandlerTransactionDecorator as a decorator for the IncomingFileHandler.

After having implemented the setup you are good to go. So, whenever you think of implementing the unit-of-work and repository pattern in your project, keep in mind the suggestions in this post.

In m'n database maak ik gebruik van een idenity veld dat bestaat uit een Guid. Deze guid wordt automatisch gegenereerd binnen SQL Server door middel van de newsequentialid() functie (dit kan uiteraard ook met newid()). Dit betekend dus dat ik in m'n code niet een guid hoef aan te maken, omdat SQL dit al zelf doet. In de op te slaan entity vul ik het ID veld dus ook niet.

Nadat het insert commando is uitgevoerd verwachtte ik dat het ID veld zou worden gevuld met de nieuw aangemaakte guid, maar niets is minder waar. Het blijft een Guid.Empty (00000000-0000-0000-0000-000000000000). In het EF1.0 werkte dit inderdaad niet, daar was helemaal geen support voor Guid identity velden. Er was echter beloofd om dit wel op te lossen in het EF4.0. Na een kleine zoektocht kom ik op allerlei pagina's en sites die het hebben over EF1.0 en Linq-to-Sql, iets waar ik in het verleden ook al dit probleem heb gehad.

Met de ervaring die ik uit het verleden had, heb ik ook al even zelf zitten klikken en zag dat de StoreGeneratedPattern op None stond. Deze heb ik op Identity en op Computed gezet. Beide keren was dit niet de oplossing.

Uiteindelijk ben ik op een post van Lee Dumond uitgekomen: http://leedumond.com/blog/using-a-guid-as-an-entitykey-in-entity-framework-4/

Hier loopt hij tegen hetzelfde probleem aan als mij.

Hij heeft hier de oplossing beschreven, namelijk dat je zelf, handmatig, de XML van het edmx-bestand moet aanpassen en dan bij het veld instellen dat dit een Identity veld is.

In de edmx:StorageModels moet je zelf bij het juiste veld aangeven dat deze automatisch wordt aangemaakt:

<edmx:StorageModels>

……

<EntityContainerName="ProjectModelStoreContainer">

<EntitySetName="Item"EntityType="ProjectModel.Store.Item"store:Type="Tables"Schema="dbo"/>

<EntitySetName="ItemType"EntityType="ProjectModel.Store.ItemType"store:Type="Tables"Schema="dbo"/>

……

Dit heeft als resultaat dat het veld in het model ook echt een Identity veld wordt. Normaal kun je dit namelijk niet aanpassen in de properties:

Wel blijft het raar dat dit niet automatisch goed wordt geregeld. Hopelijk wordt dit wel opgelost in een servicepack of EF5.0.

Momenteel ben ik met een project bezig dat met 2 tiers werkt (de database even niet meegerekend). Er is een presentatie laag (MVP) en deze staat in verbinding met een WCF service, de 2e tier. Deze 2e tier heeft dus een service laag, maar ook een business- (BL) en een data access laag (DAL). Dit is tegenwoordig een redelijk standaard design.

Omdat ik het Entity Framework wil gebruiken (http://weblogjanv/Lists/Posts/Post.aspx?ID=186) zal alleen de DAL toegang moeten hebben tot het Model. De verschillende Entities worden wel in alle projecten gelinkt (service layer, busines layer en data access layer).

Het design heb ik al een tijd goed werkend, echter toen ik het geheel wilde gaan testen liep ik tegen problemen aan. De eerste fout die ik tegen kwam was de volgende:

"ArgumentException: The specified named connection is either not found in the configuration, not intented tob e used with EntityClient provider, or not valid"

Dit was al snel opgelost door de connectiestring in de web.config van de service te plaatsen.

Nadat ik dat had gedaan kwam er een vervelende fout naar voren, namelijk:

"MetadataException: Unable to load the specified metadata resource.".

Er is hier veel over te vinden op het internet en komt er op neer dat de metadata van de edmx niet in de dll zijn meegenomen, of dat de referenties in de connectiestring niet correct zijn.

Een van de oplossingen is dan om in dit gedeelte van de connectiestring:

metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl

de * tekens te vervangen met de naam van de dll, dan zou er zoiets moeten komen te staan:

metadata=res://ModelProject/Model.csdl|res://ModelProject/Model.ssdl|res://ModelProject/Model.msl

De reden om dit te doen, is omdat de applicatie blijkbaar moeite kan hebben met het vinden van de benodigde bestanden binnen de dll's.

Nog een oplossing zou kunnen zijn om de Metadata Artifact Processing te wijzigen.

Deze staat normaal op Embed in Output Assembly, maar zou dan op Copy to Output Directory gezet kunnen worden. De aangemaakte bestanden (csdl, ssdl en msl) moeten dan handmatig worden gekopieerd naar de root of bin map van de service, afhankelijk van wat je in de web.config zet.

Beide opties boden mij geen oplossing. Tijdens het zoeken naar een oplossing voor m'n probleem merkte ik op dat de dll van het Model project niet in m'n bin map werd geplaatst. Dan is het natuurlijk logisch dat de metadata niet gevonden kan worden, aangezien die ook niet aanwezig is.

Na veel zoeken heb ik nergens een antwoord kunnen vinden dat mij zou helpen. Overal werd in de service ook gelijk een verbinding gemaakt naar het Model, waardoor alles goed gaat. Vandaag had ik ineens een helder moment. Het kan natuurlijk zijn dat .NET/Visual Studio vind dat het Model project niet nodig is. Als ik de code zo een beetje door kijk, dan wordt nergens iets van het Model project gebruikt. Er is alleen een Reference gemaakt binnen het DAL project, maar dat is dan ook alles. Hierdoor zal de compiler waarschijnlijk denken dat het project overbodig is en dus niet in de bin map plaatsen.

Een kleine test bevestigde mijn vermoeden. Nadat ik een lege klasse had aangemaakt in het Model project en deze in de DAL gebruikte werd de Model dll ook in de bin map geplaatst.

In het Model project heb ik dus het volgende aangemaakt:

namespace OnsKoopje.Model

{

public class Empty

{

}

}

In m'n DAL project heb ik nu de volgende code geplaatst:

class ModelLink

{

private ModelLink()

{

var ep = newModel.Empty();

}

}

Aangezien het Model project nu wordt gebruikt in de DAL, wordt de dll nu ook mee gekopieerd naar de output directory van de service.

Het is een beetje een work-around voor het probleem, maar het werkt! Hopelijk wordt dit in de toekomst nog eens opgelost, tenzij ik echt met exotische dingen bezig ben, maar dat lijkt mij niet.

Momenteel ben ik bezig om een nieuw project op te zetten waar ik gebruik wil gaan maken van het Entity Framework 4.0 en WCF. Aangezien .NET 4.0 net uit is, zou dit geen probleem meer moeten zijn.

Nu heb ik nog niet eerder goed met het EF gewerkt, dus is het allemaal nog redelijk nieuw voor mij. Gelukkig zijn er veel mensen die hier al het een en ander over hebben geschreven en ook duidelijke voorbeelden hebben gemaakt hoe het kan worden geimplementeerd.

Omdat ik toch graag een n-layer applicatie wil opzetten liep ik (onder andere) tegen het probleem aan dat het database model niet direct bekend is zijn op de client (de website). De verschillende objecten/entities zullen dus moeten worden geserialized om op de website te kunnen gebruiken. Dit is natuurlijk iets waar al menig ander tegenaan is gelopen, dus heb ik gezocht naar mogelijke oplossingen.

De eerste nuttige hit die ik heb gevonden was een post van Eliska Flasko uit het MSDN Magazine van April 2010 (http://msdn.microsoft.com/en-us/magazine/ee336128.aspx). Een heel duidelijk stuk waar mij de grote lijnen wel duidelijk werden. Tijdens het doorlopen van dit stuk zag ik dat ze hier gebruik maken van het ADO.NET EntityObject Generator template en niet de Self-Tracking variant.

Dit kan ik natuurlijk ook doen, maar dan zal ik het bijhouden van versies en dergelijke in een later stadium zelf moeten gaan bijhouden. Aangezien het EF-team hier waarschijnlijk beter over heeft nagedacht dan ik zou kunnen, wil ik dus voor de Self-Tracking kiezen.

Uiteraard zijn er ook mensen die dit al een keer hebben geimplementeerd en ben ik daar dus naar op zoek gegaan. Al vrij snel kwam ik uit op een post van het ADO.NET team van juni 2009 (http://blogs.msdn.com/adonet/pages/feature-ctp-walkthrough-self-tracking-entities-for-the-entity-framework.aspx). Ook weer een heel duidelijke post met allerlei screenshots ter verduidelijking.

Tijdens het lezen van de posts zag ik trouwens ook dat je een database kunt laten genereren door het EF aan de hand van het model dat je hebt gemaakt. Handig!

In de post van het ADO.NET team komt het er eigenlijk op neer dat je de T4 templates van het ADO.NET Self-Tracking Entity Generator template in het project maakt waar het model ook in staat. Dit is ook verplicht, aangezien de templates het edmx-bestand nodig hebben.

Zodra dit is gedaan maak je een link naar de T4 templates in de andere projecten. Zo heb ik het gegenereerde model in het DAL project ingeladen en de gegenereerde entities in het Model project, het resultaat is hieronder te zien.

Op deze manier zal alleen de DAL met de database kunnen praten en kun je de Entities overal gebruiken binnen je solution, mits je de reference natuurlijk aanmaakt.

Omdat ik nu self-tracking entities heb gemaakt, worden eventuele wijzigingen automatisch bijgehouden zodra ze vanuit m'n WCF service worden geserialized en op de client (website) gedeserialized.

Het is natuurlijk nog afwachten of alles ook in 1x goed werkt, maar het ziet er in ieder geval behoorlijk eenvoudig uit.