tags:

views:

283

answers:

3

I'm writing a LINQ to SQL based repository where I want to allow a GetByID with an int parameter. The signature is:

public T GetByID(int id)
{
     // Return
     return _dataContext.GetTable<T>() ....;
}

My tables have different names for the primary key. What I'd like to do is determine dynamically for each T what the primary key is and query it for a value of integer = id. Any ideas how to best pull this off?

+1  A: 

Dennis Troller answered it to the question that Ben linked to in the comments in the question.

p.campbell
A: 

Personally, I think it would be easier to provide a SingleOrDefault<T> method that takes a Func<int,T> selector argument. Then you can provide whatever selector you wish, including the one that selects based on that table's id.

 public abstract class Repository<T> where T : class
 {
     public abstract T GetById( int id );
     public T SingleOrDefault( Func<int,T> selector )
     {
           return _dataContext.GetTable<T>().SingleOrDefault( selector );
     }
 }

Usage:

 var myObj = repos.SingleOrDefault<MyClass>( c => c.MyClassID );

A strongly-typed respository could then use this method to implement GetById()

 public class MyClassRepository : Repository<MyClass>
 {
     public override MyClass GetById( int id )
     {
         return this.SingleOrDefault( c => c.MyClassID );
     }
 }
tvanfosson
+2  A: 

Something like below (which supports other types than just int, but defaults to int). Importantly, don't fall into the trap of looking at Attribute data via reflection; LINQ-to-SQL supports objects without attributes too:

public static TEntity Get<TEntity>(this DataContext dataContext, int id)
        where TEntity : class
{
    return Get<TEntity, int>(dataContext, id);
}
public static TEntity Get<TEntity, TKey>(this DataContext dataContext, TKey id)
    where TEntity : class
{
    // get the row from the database using the meta-model
    MetaType meta = dataContext.Mapping.GetTable(typeof(TEntity)).RowType;
    if (meta.IdentityMembers.Count != 1) throw new InvalidOperationException(
        "Composite identity not supported");
    string idName = meta.IdentityMembers[0].Member.Name;

    var param = Expression.Parameter(typeof(TEntity), "row");
    var lambda = Expression.Lambda<Func<TEntity, bool>>(
        Expression.Equal(
            Expression.PropertyOrField(param, idName),
            Expression.Constant(id, typeof(TKey))), param);

    return dataContext.GetTable<TEntity>().Single(lambda);
}
Marc Gravell
This is definitely the best approach, and a good answer besides, since it points out a less well-known feature (runtime metadata) of LINQ to SQL.
Ben M