views:

402

answers:

2

Hey all,

I'm working on an application at the moment in ASP.NET MVC which has a number of look-up tables, all of the form

LookUp {
  Id
  Text
}

As you can see, this just maps the Id to a textual value. These are used for things such as Colours. I now have a number of these, currently 6 and probably soon to be more.

I'm trying to put together an API that can be used via AJAX to allow the user to add/list/remove values from these lookup tables, so for example I could have something like:

http://example.com/Attributes/Colours/%5BList/Add/Delete%5D

My current problem is that clearly, regardless of which lookup table I'm using, everything else happens exactly the same. So really there should be no repetition of code whatsoever.

I currently have a custom route which points to an 'AttributeController', which figures out the attribute/look-up table in question based upon the URL (ie http://example.com/Attributes/Colours/List would want the 'Colours' table). I pass the attribute (Colours - a string) and the operation (List/Add/Delete), as well as any other parameters required (say "Red" if I want to add red to the list) back to my repository where the actual work is performed.

Things start getting messy here, as at the moment I've resorted to doing a switch/case on the attribute string, which can then grab the Linq-to-Sql entity corresponding to the particular lookup table. I find this pretty dirty though as I find myself having to write the same operations on each of the look-up entities, ugh!

What I'd really like to do is have some sort of mapping, which I could simply pass in the attribute name and get out some form of generic lookup object, which I could perform the desired operations on without having to care about type.

Is there some way to do this to my Linq-To-Sql entities? I've tried making them implement a basic interface (IAttribute), which simply specifies the Id/Text properties, however doing things like this fails:

System.Data.Linq.Table<IAttribute> table = GetAttribute("Colours");

As I cannot convert System.Data.Linq.Table<Colour> to System.Data.Linq.Table<IAttribute>.

Is there a way to make these look-up tables 'generic'?

Apologies that this is a bit of a brain-dump. There's surely imformation missing here, so just let me know if you'd like any further details. Cheers!

+1  A: 

You have 2 options.

  1. Use Expression Trees to dynamically create your lambda expression
  2. Use Dynamic LINQ as detailed on Scott Gu's blog

I've looked at both options and have successfully implemented Expression Trees as my preferred approach.

Here's an example function that i created: (NOT TESTED)

private static bool ValueExists<T>(String Value) where T : class
    {
        ParameterExpression pe = Expression.Parameter(typeof(T), "p");
        Expression value = Expression.Equal(Expression.Property(pe, "ColumnName"), Expression.Constant(Value));
        Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(value, pe);
        return MyDataContext.GetTable<T>().Where(predicate).Count() > 0;
    }
Mark
Great, just what I needed. Expression Trees look perfect for the job :)
CapBBeard
+1  A: 

Instead of using a switch statement, you can use a lookup dictionary. This is psuedocode-ish, but this is one way to get your table in question. You'll have to manually maintain the dictionary, but it should be much easier than a switch.

It looks like the DataContext.GetTable() method could be the answer to your problem. You can get a table if you know the type of the linq entity that you want to operate upon.

Dictionary<string, Type> lookupDict = new Dictionary<string, Type>
{
  "Colour", typeof(MatchingLinqEntity)
  ...
}

Type entityType = lookupDict[AttributeFromRouteValue];
YourDataContext db = new YourDataContext();
var entityTable = db.GetTable(entityType);
var entity = entityTable.Single(x => x.Id == IdFromRouteValue);
// or whatever operations you need
db.SubmitChanges()

The Suteki Shop project has some very slick work in it. You could look into their implementation of IRepository<T> and IRepositoryResolver for a generic repository pattern. This really works well with an IoC container, but you could create them manually with reflection if the performance is acceptable. I'd use this route if you have or can add an IoC container to the project. You need to make sure your IoC container supports open generics if you go this route, but I'm pretty sure all the major players do.

Ben Robbins