views:

73

answers:

4

I hope the title and following text are clear, I'm not very familiar with the correct terms so please correct me if I get anything wrong. I'm using Linq ORM for the first time and am wondering how to address the following.

Say I have two DB tables:

User
----
Id
Name


Phone
-----
Id
UserId
Model

The Linq code generator produces a bunch of entity classes.

I then write my own classes and interfaces which wrap these Linq classes:

class DatabaseUser : IUser
{
    public DatabaseUser(User user)
    {
        _user = user;
    }

    public Guid Id 
    {
        get { return _user.Id; } 
    }

    ... etc
}

so far so good.

Now it's easy enough to find a users phones from Phones.Where(p => p.User = user) but surely comsumers of the API shouldn't need to be writing their own Linq queries to get at data, so I should wrap this query in a function or property somewhere.

So the question is, in this example, would you add a Phones property to IUser or not?

In other words, should my interface specifically be modelling my database objects (in which case Phones doesn't belong in IUser), or are they actually simply providing a set of functions and properties which are conceptually associated with a User (in which case it does)?

There seems drawbacks to both views, but I'm wondering if there is a standard approach to the problem. Or just any general words of wisdom you could share.

My first thought was to use extension methods but in fact that doesn't work in this case.

A: 

Your interface should model how you would like for the objects to be used. Since you are trying to abstract, then the consumer should not have to query the DB. Whether you make it a property, or a separate function call (ie, GetPhones()), is entirely up to you. Since you are completely wrapping things, you'll have to make some choices about how deep/lazily you want to load your objects.

Russell Steen
+2  A: 

I've had some awful experiences trying to abstract LINQtoSQL entities behind interfaces. It was a while ago, but from memory the main problem was that it totally breaks associations. For example, if you have a Customer -> Order relationship, you end up exposing it as an ICustomer, with a collection of IOrders, which means that Customer has to do some awkward mapping to cast it's internal collection of Order objects as IOrders.

Then you have to assume that when an IOrder gets passed back in, that we can cast it to an Order. Otherwise LINQtoSQL can't deal with it, but then that defeats the point of having the interface there in the first place.

I would strongly recommend that you don't try and abstract away the entity classes too much, LINQtoSQL doesn't actually put any real magic in them, the DataContext handles their persistence lifecycle, so they remain testable.

The aspects that I would be looking to hide behind an interface would be the interactions with DataContext, for example using Repository-style classes:

public interface IPhoneRepository 
{
    IEnumerable<Phone> GetPhonesForUser(User user);
}

public class L2SPhoneRepository : IPhoneRepository
{
    private readonly MyDataContext context;

    public L2SPhoneRepository(MyDataContext context)
    {
        this.context = context;
    }

    public IEnumerable<Phone> GetPhonesForUser(User user)
    {
        return context.Phones.Where(p => p.User == user);
    }
}
Jon M
I guess I can expect a letter from Apple's lawyers over `IPhoneRepository`
Jon M
A: 

You should add Phones property to IUser and make it nullable, so for a User who don't have Phone, it will be null.

Since you don't want consumers of the API to write queries, than you should implement functions like GetUser().. etc.

Here is a nice list of article abt n-tier application in Asp.net

http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=416

Sharique
A: 

I tend to consider the Linq2Sql related stuff to be an implementation detail of the data access code and, like the real structure of the database, shouldn't necessarily be exposed to other parts of the system.

If your API is going to be consumed by other people it should be cohesive and easy to use and not cluttered by things the consumer doesn't need to know about. If I'm dealing with users and their phones I don't really want to know about DataContexts or (ugh) DataSets.

Also, by keeping the bulk of your code ignorant of the L2S and database you will have an easier time testing, making schema changes (oh, so now the User table needs to keep a history of every change made) or even changing the ORM completely.

Andrew Kennan