views:

635

answers:

3

I have some e-commerce code that I use often that uses Linq To SQL for saving orders to the database. I want to remove the tightly coupled Linq to SQL bit and pass in an IRepository instead but I am still a little confused on things.

Say I have a GetCustomer() method on my ICustomerRepository that returns a Customer object.

Do I need it to really return an ICustomer object that gets passed back from that method so if I switch from Linq To SQL to say SubSonic it's not a problem?

I believe I do, if that is the case is there a way in Linq To SQL to easily convert my Linq To SQL Customer object to my ICustomer object like SubSonics ExecuteSingle(Of ) method?

A: 

You can have it return a Customer as long as Customer is a plain old .NET object, and not some db-generated entity. Your Customer domain object should have no knowledge about how (or if) it might be persisted to a database, and this is what should be returned from your repository. In your repository you might have some mapping code - this is quite common - that maps from [however you get the data back from its storage location] to your domain object. If you're using Linq-to-sql then this mapping would be from the Linq-To-Sql generated Customer table (and perhaps other tables - your Customer domain object likely won't map 1:1 to a particular table in the database) to your Customer domain object, which would live in a different namespace (and most likely, assembly).

ssmith
+1  A: 

There is no need to make it an ICustomer at all. A repository acts in a way as to make it look as though your persistent instances are in memory.

public interface ICustomerRepository
{
  Customer GetByID(int id);
  IEnumerable<Customer> GetByName(string name);
  Customer GetByVatCode(string vatCode);
}

Some people would additionally include methods such as

void Add(Customer newCustomer);
void Delete(Customer deleted);
void Update(Customer modified);

The latter method implementations would most likely just update a unit of work.

The concept though is that these are just common ways of asking for Customer instances, the repository acts as a way of asking for them without defining how to ask for them.

Peter Morris
so it does matter if Customer on ICustomerRepository is created by Linq To SQL or SubSonic and that there types might be different?
Slee
where is customer defined in your ICustomerRepository?
Slee
Max - Why would their types be different? A customer is a customer, when you ask for a customer that is what you should get.
Peter Morris
The customer class would be defined in your business model (your domain layer)
Peter Morris
+2  A: 

If you want your Customer class to be a plain object with no attachment to LINQ, then you will most likely need to write a mapper method to convert your LINQ-based Customer object to your plain Customer domain object. LINQ to SQL does not have such functionality built-in.

I have begun to wrap my mapping methods in an extension method for readability, and it really helps to keep the Repository code simple. For instance, an example CustomerRepository method my look like:

public Customer GetById(int id)
{
   return dataContext.LINQCustomers.Where(c => c.Id == id)
                                   .Single()
                                   .ToDomainObject();
}

and the ToDomainObject() method is defined in an extension method like:

public static class ObjectMapper
{
    public static Customer ToDomainObject(this Customer linqObject)
    {
        var domainObject = null
        if (linqObject != null)
        {
            domainObject = new Customer
            {
               Id = linqObject.Id,
               FirstName = linqObject.FirstName,
               LastName = linqObject.LastName
            }
        }
        return domainObject;
    }
}

or something similar. You can do the same to convert your domain object back to a LINQ object to pass back into your repository for persistence.

Eric King
I didn't know you had to do that with LINQ, I thought it would just return a Customer.PS: In addition you'd first want to check if the instance is loaded into a local cache. If so return it, if not then fetch + cache + return.
Peter Morris
You *can* return the LINQ-generated 'Customer' object directly, but it would contain all of the LINQ-generated persistence logic, too. If you want your repository to return a plain object without all of the LINQ stuff included, you have to make one yourself and map to it.
Eric King
seems like a PITA, be great if there was an easy way to cast them to each other maybe using reflection and matching properties and generics
Slee
Max, maybe AutoMapper is similar to what you're looking for: http://www.codeplex.com/AutoMapper
Eric King