views:

1975

answers:

11

I have looked over the Repository pattern and I recognized some ideas that I was using in the past which made me feel well.

However now I would like to write an application that would use this pattern BUT I WOULD LIKE TO HAVE THE ENTITY CLASSES DECOUPLED from the repository provider.

I would create several assemblies :

  1. an "Interfaces" assembly which would host common interfaces including the IRepository interface
  2. an "Entities" assembly which would host the entity classes such as Product, User, Order and so on. This assembly would be referenced by the "Interfaces" assembly since some methods would return such types or arrays of them. Also it would be referenced by the main application assembly (such as the Web Application)
  3. one or more Repository provider assembly/assemblies. Each would include (at least) a class that implements the IRepository interface and it would work with a certain Data Store. Data stores could include an SQL Server, an Oracle server, MySQL, XML files, Web / WCF services and so on.

Studying LINQ to SQL which looks very productive in terms of time taken to implement all seems well until I discover the deep dependency between the generated classes and the CustomDataContext class.

How can I use LINQ to SQL in such a scenario?

+1  A: 

Not exactly the same scenario, but I'm working to create a custom tool that based on a XML file will generate an OO model. My approach is to use LINQ to SQL behind the scene and since I'm generating the code automatically it would be easy to use another mechanism for let's say MySQL data source. Since it's not supported by LINQ to SQL you will have to write the data access code manually, but the client code that will use the OO model will change in any way.

Albert
Ofcourse that if I have to implement a MySQL repository provider I'll write the data access code myself, no problem regarding this. However wouldn't be at least awkward to use the LINQ-generated entities for this?
Andrei Rinea
I have used DataSets before and it was a good solution. It may not be the most elegant way, but certainly it works.
Albert
+1  A: 

Could your Entity classes implement IProduct, IUser, IOrder etc. interfaces that would be declared in your "Interfaces" assembly? This way the IRepository interface references only the business object interfaces (i.e., returns collections of IProduct etc.) and the "Interfaces" assembly is decoupled from your other implementation-specific assemblies.

+5  A: 

I don't know if this is exactly what you want, but you may want to take a look at Rob Conery's MVC Storefront code. He uses a variant of the repository pattern with a linq provider. He maps the LINQ to Sql objects to domain objects and then returns the domain objects from the repository provider to a service layer which wraps the provider allowing him to work some logic on the data returned before it hits the business layer.

MVC Storefront Webcasts
Code

To me it sounds like you want the providers to return DTOs and then you want to map the DTOs to the domain objects in the repository/service layer. If this is the case you could map your LINQ to SQL provider to the DTOs, have it return them, then map the DTOs to domain objects in the repository/service layer. This should work just fine, but it may become tedious as you now would have 2 mapping layers.

In this case you would have: ProductService, which takes an IProductRepository. It evokes methods on the IProductRepository to get back your DTOs. It then maps the DTOs to the real business objects and returns them to the calling code.

Daniel Auger
I just went in and looked into MVC Storefront. They use different domain objects than those auto-generated by LINQ. So I will probably do it too. Thanks for pointing this out :)
Andrei Rinea
The classes generated by the L2S designer are not exactly domain objects. http://martinfowler.com/eaaCatalog/domainModel.html
cottsak
Cottsak, exactly. That's why this solution maps the linq2sql results to real domain objects before returning them.
Daniel Auger
+2  A: 

You don't have to use the LINQ to SQL generated code, you can decorate your own classes with the necessary ColumnAttributes or use an external XML mapping file.

DamienG
+4  A: 

You can create an external XML file mapping the database to any class:

 <?xml version="1.0" encoding="utf-8"?>
 <Database Name="DbName" 
           xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007"&gt;
    <Table Name="DbTableName">
       <Type Name="EntityClassName" >
           <Column Name="ID" Type="System.Int64" Member="Id"
                   DbType="BigInt NOT NULL IDENTITY" IsPrimaryKey="true"
                   CanBeNull="false" />
           <Column Name="ColumnName" Type="System.String" Member="PropertyA"
                   DbType="VarChar(1024)" CanBeNull="true" />
       </Type>
    </Table>
 </Database>

And then pass the XML to a DataContext class:

 using (var cn = GetDbConnection())
  { var mappingSrc = XmlMappingSource.FromReader(xmlReader);

    using (var db = new DataContext(cn, mappingSrc))
     { var q = from entity in db.GetTable<EntityClassName>()
               where entity.PropertyA = "..."
               select entity.ID;
     }
  }
Mark Cidade
I have posted some links about external XML mapping here: http://stackoverflow.com/questions/988872/linq-to-sql-external-mapping/1136039#1136039
alexandrul
+3  A: 

I think you want POCO (Plain Old CLR Objects) support. LINQ to SQL has a adapter called Close2Poco.

But I would advise making the switch to Entity Framework, at the moment they also have a POCO adapter, but in v2 its expected to be supported out of the box.

Davy Landman
+2  A: 

The simplest way would be to decouple your entities from the datacontext: load the needed entity, decouple it from the DataContext, use it however you like, later use Attach() to couple it with a DataContext for saving.

Sadly LINQ has no method to decouple entities from a datacontext, but you can just clone them, that works nicely. Simplest way would be something like this:

public static T CloneEntity<T>(T source)
{
  DataContractSerializer dcs = new DataContractSerializer(typeof(T));
  using (Stream stream = new MemoryStream())
  {
    dcs.WriteObject(stream, source);
    stream.Seek(0, SeekOrigin.Begin);
    return (T)dcs.ReadObject(stream);
  }
}
Sam
You could use external XML mapping.
alexandrul
+2  A: 
+4  A: 

I found a fantastic blog post (with lots of good code) about this here: http://iridescence.no/post/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx

Mladen Mihajlovic
+1  A: 

I've implemented precisely what you are asking for and published the code in my blog:

A Generic Linq To SQL Repository Base Class for disconnected Linq to SQL Data Layers

Adrian Grigore
great post!
Andrei Rinea
+1  A: 

This blog post presents a nice solution.

http://iridescence.no/post/Linq-to-Sql-Programming-Against-an-Interface-and-the-Repository-Pattern.aspx

Riko