使用反射来解决Linqed属性
我想写一个通用的方法,将加载特定类型的记录,具有特定的ID。这里有一个工作方式:使用反射来解决Linqed属性
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; }
}
可以忽略调用LinqUtils.GetDataContext<T>()
;这是我必须处理的事实,即我的程序中有多个数据上下文。问题是,现在我可以将我的任何类声明为LinqableTable
的子类,并且只需调用LinqedTable<MyType>.Get(ID)
即可轻松实例化该表的一条记录。
但是这有一些限制。首先,它强制我的所有表格都有一个long
类型的标识字段ID
。其次,因为我使用抽象方法,所以我不得不去O/R设计器,并将系统中每个ID字段的继承属性更改为“覆盖”。
我想要更多的灵活性。所以很自然的,我试过了反思,并用以下就出来了:
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"; }
}
}
从理论上讲,这可以让我重写ID列名,投给long
应该与任何整型数据类型确定,我不我需要去定义我所有的ID列为overrides
。
但
的LINQ不喜欢这一点。在电话会议中q.Single<T>();
我得到一个运行时错误:
The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.
OK,今天笔者了解到的LINQ做某种在后端魔术;它没有实例化obj
,只是阅读IDValue
属性。所以必须有一些属性需要在IDValue
属性上设置,让Linq能够做到这一点。
但是什么?
Linq to SQL尝试将您的linq查询转换为SQL,但它不知道如何将您的属性转换为数据库中的列名称。
一个很好的解释可以在这里找到在SO: simple linq to sql has no supported translation to SQL
但怎么解决呢,则是另一回事。我有更迭从这个线程使用的之路探寻:
或者你可以使用这里由Scott Guthrie的提到的动态查询:
由于引用LINQ-to-SQL IQueryable的LINQ语句被转换为SQL查询,所以必须使用AsEnumerable扩展(这会导致读取数据库中的所有项),并执行反射 - 该IEnumerable相关的东西。
编辑
如这里所需要的澄清
作为注释规定,我的意思是这样的:
(from obj in context.GetTable<T>() select obj).AsEnumerable().Where(x => x.IDValue == ID)
与上一个IQueryable执行的查询,可以完美地转换为SQL如
context.GetTable().Where(x => x.Text == "Hello")
,其被转换到类似的东西,以
SELECT * FROM TABLE_MAPPED_TO_TYPE_T WHERE Text = 'Hello'
针对IEnumerable执行的查询 - 在您的情况下 - 将通过获取表的所有条目然后应用代码方式应用指定的过滤器来执行。
看了这些帖子:Generic Data Access using LINQ to SQL and C#, LINQ-to-SQL: Generic Primary Key function和 Calling a generic method with Type
我和我的同事想出了以下摘要:
我们在datacontext中添加了以下方法(在部分类中)。
public T GetInstanceByPrimaryKey<T>(object primaryKeyValue) where T : class
{
var table = this.GetTable<T>();
var mapping = this.Mapping.GetTable(typeof(T));
var pkfield = mapping.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);
if (pkfield == null)
throw new Exception(String.Format("Table {0} does not contain a Primary Key field", mapping.TableName));
var param = Expression.Parameter(typeof(T), "e");
var predicate =
Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(param, pkfield.Name), Expression.Constant(primaryKeyValue)), param);
return table.SingleOrDefault(predicate);
}
然后,我们需要从类型名称和主键值来实例化:
string name = "LinqObjectName";
int primaryKey = 123;
var dc = new YourDataContext();
Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));
MethodInfo methodInfoOfMethodToExcute = dc.GetType().GetMethod("GetInstanceByPrimaryKey");
MethodInfo methodInfoOfTypeToGet = methodInfoOfMethodToExcute.MakeGenericMethod(name);
var instance = methodInfoOfTypeToGet.Invoke(dc, new object[] { primaryKey });
return instance;
希望这有助于!
请澄清 - 可能是以您想到的语法为例? – 2009-04-26 17:06:25
一个认为emaster的意思是:(从obj中获取context.GetTable()select obj).AsEnumerable()。其中(x => x.IDValue == ID)...但这样效率很低。 –
asgerhallas
2009-04-26 17:12:48