Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Architecting LINQ to SQL applications, part 3

DAOs and Repositories

One of the concerns we want to separate our domain from, is how we persist the domain model. The domain should not need to where: file, database, service etc. or how. In the specific context of an RDBMS, because we do not want the domain to be relational database or its schema we do not want any SQL statements or ADO.NET objects within our domain. It is not just that the external schema may change but also that there may a mismatch when mapping between the two, the impedance mismatch problem.

DAO

A Data Access Object (DAO) is a pattern for encapsulating data access. A DAO is an abstraction that provides services for retrieving, inserting, updating, and deleting objects from a persistent store.  Fowler calls a DAO a Data Mapper. We will use this term from now on, because the term DAO means different things to different people.

Data Mapper

Because the Data Mapper knows the details of RDBMS, how to connect to a DB, relational schemas, SQL etc. it cannot live in the domain, instead it is an infrastructure service. Because we do not want our domain to depend on concrete classes, we need to provide an interface for the Data Mapper, so that we can depend on an abstraction from the domain. We want to be insulated from change in either the persistence mechanism or medium.

Test-first development will drive out this abstraction, because we want to replace the dependency on the DB at runtime (to both prevent issues with shared fixtures and slow tests).The domain, or an application service, uses the Data Mapper for persistence.

Most hand-rolled or code-generation Data Mappers are the simplest form using hard-coding, configuration files, or reflection to map between a class in the domain and a table in the RDBMS. Within .NET, a Data Mapper often contains ADO.NET code, using dynamic SQL or stored procedures. Internally a Data Mapper tends to use a DataReader for performance. Of course the Data Mapper does not have to be abstracting a DB, and we might be using XMLDocument etc. under the hood.

The biggest advantages to the Data Mapper pattern are  

  • Simplicity: it leaves us with a clean domain model where the orthogonal concern of persistence is separated out of from the domain classes.  
  • Extensibility:  By depending on an abstraction, instead of a concrete type, we can swap implementations (through a factory or IoC container) which allows us the flexibility to meet new needs, such as support for multiple vendors SQL implementations. 
  • Maintainability: Having all of the data access code for a class in one place, reduces duplication and shotgun surgery when we need to update our data access code.

There are a number of complimentary patterns we can add when we use a Data Mapper:

  • Identity Map: When we load an entity we want future loads of that same entity to return the same reference. This is not for performance, a happy side effect, but ensures that we don’t lose any pending changes next time we reload, and allows us to compare for equality by address.
  • Unit of Work: Records the deltas between an object when loaded and when persisted so that we only update with changes not everything. The unit of work also allows us to bundle up changes and make them together, so as to improve performance.
  • Query Object: We don’t want to pollute our domain space with SQL when we want to perform dynamic queries so we need an alternative way of representing a query.Instead of rolling-our-own for a Data Mapper we can adopt the expedient of buying an off-the-shelf product in the RDBMS space called an Object Relational Mapping Tool.

Repository

If we have a hand rolled Data Mapper, or ORM, although we can make calls to it from the domain, we still want to encapsulate that interaction.

repository 

There are a number of reasons for this but the most important are:

  • DRY: We don’t want repeated code, setting up a Query object to do a common query for example, so by encapsulating into a single method we prevent that. 
  • Shotgun Surgery: We don’t want to have to modify our calls to the Data Mapper in many places, so by adding the code to a single class we don’t have to go for searching it out within our application. 
  • Gateway: We might want to change our ORM at some point in the future. Encapsulating the calls to the ORM allows us to limit the modifications we have to make to the classes that are implemented using the ORM.

The Repository pattern looks and feels like a collection to the domain. This makes it clear and simple to work with.  The unit of work often remains outside of the repository, because we may want to update multiple elements on a repository or multiple repositories at once. The repository is implemented by calls to the Data Mapper. Building a repository with LINQ is almost trivial:

public class NorthwindRepository
{
    private IQueryable<Customer> customers;
    public NorthwindRepository(DataContext context)
    {           
        customers = context.GetTable<Customer>();       
    }

    public Customer FindCustomer(string customerId)
    {           
        return (from c in customers
            where c.CustomerID == customerId
            select c).Single<Customer>();
    }
}

There are two alternatives to testability with a repository: the simplest is to have the repository implement an abstraction (i.e. an interface), which you then provide an in-memory stub for, for testing purposes. The other is to provide an abstraction for the Data Mapper that your repository depends upon and provide a test stub for that. The advantage here is if you want to confirm querying logic within your repository.

public class CustomerRepository
{
    private IUnitofWork workspace;
    public CustomerRepository(IUnitofWork workspace)
    {
        this.workspace = workspace;
    }
   
    public Customer FindCustomer(string customerId)
    {
        return (from c in workspace.Customers
            where c.CustomerID == customerId
            select c).Single<Customer>();       
    }
}

 Jimmy Nilsson shows an example of this in his book: Applying Domain Driven-Design and Patterns and I talk more about testability in Being Ignorant with LINQ to SQL.

Roles in LINQ to SQL

LINQ to SQL provides an ORM tool for use with .NET. Components within LINQ to SQL map to ORM patterns: LINQ to SQL’s DataContext contains an Identity Map that holds objects already loaded from the database. It also acts as a unit of work: you call the DataContext to submit your changes and it figures out the SQL statements necessary to figure out what has changed from the version last loaded. Note that LINQ to SQL optimizes queries that are against the primary key, by returning directly from the map rather than re-querying. For anything else, it cannot know what the returned set will be in advance, so it has to return themMy first piece of advice around LINQ to SQL, based on existing ORM practice, would be: LINQ to SQL is an ORM and may be safely called from within the domain layer. Calls to LINQ to SQL do  not need to be placed in the infrastructure layer (sometimes called data access layer in this role). LINQ to SQL fulfils the role of a Data Mapper within the infrastructure layer already, so this would just be a repetition of the abstraction. However, do consider wrapping interaction with the LINQ to SQL ORM within a Repository, instead of using it throughout the domain to simplify testing and maintenance.

Next time we’ll talk about dynamic queries.

 

 

About Ian Cooper

Ian Cooper has over 18 years of experience delivering Microsoft platform solutions in government, healthcare, and finance. During that time he has worked for the DTi, Reuters, Sungard, Misys and Beazley delivering everything from bespoke enterpise solutions to 'shrink-wrapped' products to thousands of customers. Ian is a passionate exponent of the benefits of OO and Agile. He is test-infected and contagious. When he is not writing C# code he is also the and founder of the London .NET user group. http://www.dnug.org.uk
This entry was posted in LINQ, TDD. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Pingback: car accident stories

  • http://www.examplemag.com ExampleMag

    Thanks for a nice solution, really needed some help with LINQ.

  • http://www.metin2market.com metin2 yang

    nice posts

  • wulijun

    fhfchfgf

  • wulijun

    hgfhfdhdhd

  • http://www.heychristianlouboutin.com Christian Louboutin On Sale

    what’s your name? bmx

  • http://makemoneyonline18.blogspot.com zyxu cubar tyoue

    this is a very good post

  • http://www.worldofwarcraftwowgold.com wow gold cheap

    You can read them and get the main points you need fast wow gold. Which is an extremely usable spell that can be cast wow gold instant every three minutes, the Shaman will find that their instant wow gold and mana will drain faster. fast gold world of the popular and wonderful WOW online game is now mass market. If you need some wow gold cheap and WOW powerleveling, just as come to our website.

  • http://www.metin2.mobi metin2 yang

    metin2 yang
    metin2 yang

  • http://www.faysale.com wow gold
  • http://www.game4power.com buy wow gold

    WoW shares many wow gold of its features with previously launched games. Essentially, you battle with Cheapest wow gold monsters and traverse the countryside, by yourself or as a buy cheap wow gold team, find challenging tasks, and go on to higher Cheap Wow Gold levels as you gain skill and experience. In the course of your journey, you will be gaining new powers that are increased as your skill rating goes up. All the same, in terms of its features and quality, that is a ture stroy for this.WoW is far ahead of all other games of the genre the wow power leveling game undoubtedly is in a league of its own and cheapest wow gold playing it is another experience altogether.
    Even though WoW is a wow gold cheap rather complicated game, the controls and interface are done in buy warhammer gold such a way that you don’t feel the complexity. A good feature of the game is that it buy wow items does not put off people with lengthy manuals. The instructions cannot be simpler and the pop up tips can help you start playing the game World Of Warcraft Gold immediately. If on the other hand, you need a detailed manual, the instructions are there for you to access. Buy wow gold in this site,good for you ,WoW Gold, BUY WOW GOLD.

  • Mohammad Azam

    Hi,

    So, how are you initializing the DataContext and from where are you sending the context.

    public class NorthwindRepository
    {
    private IQueryable customers;
    public NorthwindRepository(DataContext context)
    {
    customers = context.GetTable
    ();
    }

    public Customer FindCustomer(string customerId)
    {
    return (from c in customers
    where c.CustomerID == customerId
    select c).Single();
    }
    }

  • SteveG

    I should add: isn’t the, ie. ISession(NH) or DataContext (LSQL) the Unit of work here?

  • SteveG

    I’d like you to expand on this unit of work sample code. I think you ran over it quite quickly and I’m not making the connection to what it is accomplishing in your code snippet above.

  • http://pretzelsteelersfan.blogspot.com/ Paul Brown

    Hey Ian,

    The LinqDataSource requires that the DataContext be exposed and actually forms the query effectively putting the them in the presentation layer. Dinesh seems to argue that this is ok in small-medium apps and I tend to agree (especially I legacy apps that are highly stable). Your thoughts?

  • http://blogs.msdn.com/mattwar Matt Warren

    SQLMetal can in fact create the XML mapping file for you. It is one of the output options.

  • Al

    Thanks Ian,

    XML mapping (SourceMapping) to classes makes sense to use my domain object. Is SqlMetal capable of creating the XML file for mapping? If not, it manual mapping the only option? IS MS going to provide a tool for creating the XML file for mapping?

  • Ian Cooper

    Hi Al,

    Sadly I think that the designers with LINQ are causing some of this confusion, because people imagine that their generated entities are not the domain level objects. They are, or should be, because only the .NET SDK implementation of Table and DataContext are within the infrastructure, the generated classes are domain level. What the designers are trying to do for you is to generate your domain model. Note that the classes are partial, so you can extend them for the required behavior by adding your own partial classes.

    The problem is that the usage pattern for the designers, is: design your data schema, and then generate your entities. This is the wrong way around. design your domain first, then map your schema from it. That is the key to an easy life with an ORM tool. (Bear in mind that if you want some sort of optimized schema for reporting you might need to consider separating your OLTP store and OLAP store anyway, so it should not worry you overly, to have your schema close to your domain model).

    What you should be doing is designing your domain layer, and then either attributing the class or using an XML file to map that domain to a schema. If you have a non-legacy scenario the domain should come first. I would suggest dumping the use of the designer. I think it is another case where an MS supplied designer makes then problem more, not less, complicated.

    I’ll have a lot more to say about this in a future post. But for now I would urge you all to work with the domain you want, then mark it up for persistence.

    Note that in legacy scenarios you may hit walls around some of the mapping options in LINQ to SQL (same as you would in, for example, Wilson O/R Mapper) and you need to think about using something like NHibernate instead.

    Again I’ll talk more about this in a future post, but the optimal scenarios for the mapping options provided by LINQ to SQL are where you have a greenfield application and the schema can be broadly similar to the domain model. Legacy schemas will tend to cause you grief.

  • Al

    I am reading your Architecting LINQ to SQL applications series with interest and hoping to find some answers to my questions. Here is a typical application layer:

    Data
    ——-
    Respository
    DataMapper

    Business
    ————-
    Facade
    Domain

    Presentation
    —————–
    View

    Now, I have my domain objects such as Person, Order. Presentation access domain objects through a Facade for operations; database for this example. Facade accesses the Repository, repository accesses the DataMapper which would map my domain object to a Data Access Object which will be the Linq entity that represents a Person table.

    Since my domain object is not one to one match to a Person entitiy, I will call my Linq to Sql generated entity PersonDAO. I will be mapping the PersonDAO to my Person object.

    I know I am missing something here but is this how this is supposed to work? How can I keep my domain objects clean without this kind of mapping?