I'm trying to writing a generic method that will load a record of a specific type, with a specific ID. Here's one way that works:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.ID == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
public abstract long ID { get; set; }
}
You can ignore the call to LinqUtils.GetDataContext<T>()
; that's a utility function I've got to deal with the fact that I have multiple data contexts in my program. The point is that now I can declare any of my classes as subclasses of LinqableTable
, and I can easily instantiate a record of that table just by calling LinqedTable<MyType>.Get(ID)
.
This has some limitations, however. Firstly, it forces all of my tables to have an I
identity field of type long
, named ID
. Secondly, because I'm using an abstract method, I am forced to go to the O/R designer and change the inheritance property of every ID field in my system to "override".
I want more flexibility than that. So naturally, I tried reflection, and came out with the following:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.IDValue == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
internal long IDValue {
get { return (long)IDProperty.GetValue(this, null); }
set { IDProperty.SetValue(this, value, null); }
}
internal PropertyInfo IDProperty {
get { return this.GetType().GetProperty(IDPropertyName); }
}
internal protected virtual string IDPropertyName {
get { return "ID"; }
}
}
Theoretically, this allows me to override the ID column name, the cast to long
should be OK with any integral data type, and I don't need to go defining all my ID columns as overrides
.
BUT
Linq doesn't like this. On the call to q.Single<T>();
I get a runtime error:
The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.
OK, today I learned that Linq does some kind of magic on the back end; it doesn't instantiate obj
and just read the IDValue
property. So must be there's some attribute that needs to be set on the IDValue
property that lets Linq do its thing.
But what?