views:

267

answers:

2

Hi

I am wondering if anyone has any good tutorials(or maybe even a library that is already made and well documented) on making a generic repository.

I am using currently linq to sql but it might change so I don't know if you can make a generic repository that would take little to no changes if I would say switch to entity framework.

Thanks

I think I should also add why I want a generic repository. The reason is in my database I have like corporate tables(users who's subscriptions are paid by someone else) and individual tables(people who find my site through google or whatever and pay for their own subscription)

But I will have 2 very similar tables. For instance I have 2 settings tables one for corporate users and one for the individuals.

Now since they are 2 different tables I need 2 different insert methods as I am inserting it into 2 different tables and at this current time only one field is different(that is the PK).

So now I need all these duplicate methods and I don't want that. Maybe what I have in my database is could be considered as a design flaw(and maybe it is) but one of the reasons behind this was if needed I can break up my database into 2 different databases very easy and I am not going to change my design anytime soon.

+2  A: 

Here is my answer to another question of the same type. Hope it helps:

http://stackoverflow.com/questions/1230571/advantage-of-creating-a-generic-repository-vs-specific-repository-for-each-objec/1231473#1231473

Edit:

It sounds like you want to treat two concrete types as one logical type. To do that, first define the logical type:

public interface ISubscription
{
    // ...
}

Then, define the concrete types as part of your data model (interfaces would be implemented in another partial class):

[Table("CorporateSubscription")]
public partial class CorporateSubscription : ISubscription
{

}

[Table("IndividualSubscription")]
public partial class IndividualSubscription : ISubscription
{

}

Next, define the repository which operates on the logical type:

public interface ISubscriptionRepository
{
    CorporateSubscription GetCorporate(string key);

    IndividualSubscription GetIndividual(int userId);

    IEnumerable<ISubscription> ListAll();

    IEnumerable<CorporateSubscription> ListCorporate();

    IEnumerable<IndividualSubscription> ListIndividual();

    void Insert(ISubscription subscription);
}

Finally, implement the interface by using both tables:

public class SubscriptionRepository : ISubscriptionRepository
{
    private readonly YourDataContext _dataContext;

    public SubscriptionRepository(YourDataContext dataContext)
    {
        _dataContext = dataContext;
    }

    #region ISubscriptionRepository

    public CorporateSubscription GetCorporate(string key)
    {
        return _dataContext.CorporateSubscriptions.Where(c => c.Key == key).FirstOrDefault();
    }

    public IndividualSubscription GetIndividual(int userId)
    {
        return _dataContext.IndividualSubscriptions.Where(i => i.UserId == userId).FirstOrDefault();
    }

    public IEnumerable<ISubscription> ListAll()
    {
        return ListCorporate()
            .Cast<ISubscription>()
            .Concat(ListIndividual().Cast<ISubscription>());
    }

    public IEnumerable<CorporateSubscription> ListCorporate()
    {
        return _dataContext.CorporateSubscriptions;
    }

    public IEnumerable<IndividualSubscription> ListIndividual()
    {
        return _dataContext.IndividualSubscriptions;
    }

    public void Insert(ISubscription subscription)
    {
        if(subscription is CorporateSubscription)
        {
            _dataContext.CorporateSubscriptions.InsertOnCommit((CorporateSubscription) subscription);
        }
        else if(subscription is IndividualSubscription)
        {
            _dataContext.IndividualSubscriptions.InsertOnCommit((IndividualSubscription) subscription);
        }
        else
        {
            // Forgive me, Liskov
            throw new ArgumentException(
                "Only corporate and individual subscriptions are supported",
                "subscription");
        }
    }
    #endregion
}

Here is an example of an insert. Don't get too wrapped up in the presenter class; I just needed a situation in which subscriptions would be created based on a flag:

public class CreateSubscriptionPresenter
{
    private readonly ICreateSubscriptionView _view;
    private readonly ISubscriptionRepository _subscriptions;

    public CreateSubscriptionPresenter(
        ICreateSubscriptionView view,
        ISubscriptionRepository subscriptions)
    {
        _view = view;
        _subscriptions = subscriptions;
    }

    public void Submit()
    {
        ISubscription subscription;

        if(_view.IsCorporate)
        {
            subscription = new CorporateSubscription();
        }
        else
        {
            subscription = new IndividualSubscription();
        }

        subscription.Notes = _view.Notes;

        _subscriptions.Insert(subscription);
    }
}
Bryan Watts
Thanks for the link, that is a good way to do it
Andy White
It helps a bit but I really need probably almost a step by step guide. I don't know much about generics and stuff. See my edit in original post
chobo2
Hmm I will have to try this but I don't get a couple things. 1st I am not sure what you mean by concert types. So I am not sure what I would put in there. I am not sure if the interface and the 2 particular classes would be in the same file, I am not sure why you put [Table] on top of it and finally how would I return the table object(as some cases I would have to return corporate and sometimes individual). For instance I want to get a users record from the settings table and I write a where clause to get that users record but depending on what the user is signed up for it might be...
chobo2
I might return the table object CorporateSettings or IndividualSettings. The where clause it self might be like(u => u.userid == userId).FirstOrDefault(); for individuals and for corporates it might be (u => u.key == key).firstOrDefault(); So what would I do return a generic type? Then cast it later or what?
chobo2
The `CorporateSubscription` and `IndividualSubscription` types are tables defined in your LINQ to SQL `DataContext`. You might have dragged and dropped them in the designer, but the code generated by the designer looks like what is in my answer, with the `[Table(...)]` attribute. You can right-click the .dbml file and select View Code to see for yourself. The data context, which I named `YourDataContext` in my example, would have properties named `CorporateSubscriptions` and `IndividualSubscriptions` that represent the tables. (Your table names are probably different.)
Bryan Watts
`ISubscription` would contain the list of properties common between the tables. It is like a base class. You have to add it to the classes generated by the LINQ to SQL designer. Since the code is generated, you can't modify it directly, because it would be overwritten. Luckily, each class is `partial`, which means you can split it across multiple files (see more here: http://msdn.microsoft.com/en-us/library/wa80x488(VS.80).aspx). You would create a file called CorporateSubscription.cs right next to your .dbml file and add this: `public partial class CorporateSubscription : ISubscription {}`
Bryan Watts
Ok I see what your doing now but I am still unclear as you said with properties that are in common. But the search to get a users record changes slightly as the where clause is different(one uses userId and the other uses key). So would I do that one and that is really the biggest problem as they both use different columns and I have to specify it in the search. So how would I get byt hat one?
chobo2
You said that the 2 tables have the same columns except for the primary key. Let's say there are 10 total. That would mean 9 are the same. `ISubscription` contains those 9 properties. It represents everything that is the same between the 2 tables.
Bryan Watts
I edited `ISubscriptionRepository` to include methods that retrieve subscriptions by ID/key.
Bryan Watts
Ya I see what your getting at but what you got seems to be good for stuff that returns void or maybe a primitive type or something. But mystuff mostly returns collection of the tables or just one table object back. So I still will have duplicate code(maybe a bit less what is good) but it would be really good if I could have none since really it is one little thing that changes. But what you updated is the same(GetIndividual,GetCorporate) the only difference is I have it in 2 different repositories. Where you have it in one. So I am going to check how many methods I could reduce into one with..
chobo2
your way and see if it is worth the time switching. One thing I am not sure is many times I just send primary types in like say if I want to create a new record I would have like create(string one,string two) { Table1 one = new Table1() one.Firstcolumn = one; dbcontext.Table1.insertonsubmit(one);} but with your way I am going to have to pass in an interface? Or how would that work(if you could show me what is happening if you wanted to call your Insert method that would be great. Thanks btw for all this help so far.
chobo2
@chobo2: *"I don't know much about generics and stuff."* If you are asking how to make a **generic** repository then probably the first thing you should do is start learning about *generics*. Any solution provided will be difficult to understand if you lack this background.
Aaronaught
@Aaronaught and I think a good way to learn about generics is to use it situations that you normally would use in it. Hence why I was asking for a tutorial.
chobo2
@chobo2: Subscriptions are logically 1 table, but having 2 physical tables makes sense for deployment reasons. `ISubscriptionRepository` represents the logical table, giving your application a single set of methods which cover both physical tables. Everybody who needs corporate or individual subscriptions goes through the same place. The split table setup is just an implementation detail. (I updated my answer with an example of using the `Insert` method. You're welcome for the help. I remember when I was first starting out with this stuff.)
Bryan Watts
So everything in the ISubscription interface would I write methods in both the partial class? Or do I just implement them but don't actually write any code in them?
chobo2
You implement them without writing any code. The properties are in the partial class generated by LINQ to SQL, so they exist, satisfying the interface. `ISubscription` provides a common type between the classes.
Bryan Watts
+1  A: 

Great Linq to Sql resources:

A t4 template that by generates exactly what is created by default, but can be fully customised.

http://l2st4.codeplex.com/

Using Linq to Sql for a multi tier application. It has a GenericObjectDataSource which I have found very handy

http://multitierlinqtosql.codeplex.com

Search all properties of an IQueryable with one single search

http://naspinski.codeplex.com/

SteadyEddi
Hey. I don't really get what the t4 one is for. I downloaded the multi tier application but kinda confused on how to use it as I can't find northwind(I find this .msi file for it but when I install it does not even tell me where it installs it to so i can't find it). The 3rd link looks but does it have some where clause searches? Like from the examples I seen it uses .Contains what I really don't want to use in most cases.
chobo2
Ok I figured out how where they installed northwind too. So now I am testing it. It looks pretty good this multi teir linq even though I am still not 100% sure how to use it. Like I am not sure what the GenericObjectDataSource does and what it responsible for. Or how to integrate with my stuff. Then that of course does not help me with my where clauses and other sort of ones like that.
chobo2
So by default visual studio uses a tool called sql metal to generate the .designer.cs file for your dbml file. L2ST4 replaces this process and allows you to completely customise the class generation process. This will mean you can define your repository within the templates, and the code will be automatically generated for each entity.
SteadyEddi
The key thing about asp.net webforms (which I kind of assume you are using), is that most of the "controller" and data layer logic is actually defined at the DataSource level, rather than in controls. Updates and Inserts are handled within the datasource. In a lot of custom scenarios people use an ObjectDataSource as part of their repository, so their data logic can be hooked up to a GridView or ListView. The GenericObjectDataSource is a replacement for this designed to be used in place of the ObjectDataSource, and provides better functionality for Linq (ie paging and sorting).
SteadyEddi
Ah. and how about thatIQueryable does it have where clause ability?
chobo2
IQueryable is one of the classes used in linq. It extends IEnumerable, and is a way of representing a linq expression, which can at some point be converted to sql. If you want to keep something as an expression and not have a query executed in the database, keep it as an IQueryable. The Napinski tools are used to extend the features of linq for common scenarios, for instance a quick and dirty search mechanism. A where clause can easily be combined in with that.
SteadyEddi
Could you maybe give an example of that using IQueryable?
chobo2
SteadyEddi