使用reflection来解决Linqed属性

我正在尝试编写一个通用方法,它将加载具有特定ID的特定类型的记录。 这是一种有效的方法:

public abstract class LinqedTable where T : LinqableTable { public static T Get(long ID) { DataContext context = LinqUtils.GetDataContext(); var q = from obj in context.GetTable() where obj.ID == ID select obj; return q.Single(); } } public abstract class LinqableTable { public abstract long ID { get; set; } } 

您可以忽略对LinqUtils.GetDataContext()的调用; 这是一个实用函数,我必须处理我的程序中有多个数据上下文的事实。 关键是现在我可以将我的任何类声明为LinqableTable子类,并且我可以通过调用LinqedTable.Get(ID)轻松地实例化该表的记录。

然而,这有一些局限性。 首先,它强制我的所有表都有一个类型为long的I标识字段,名为ID 。 其次,因为我使用的是抽象方法,所以我不得不去O / R设计器并将系统中每个ID字段的inheritance属性更改为“覆盖”。

我想要更多的灵活性。 很自然地,我尝试了反思,并提出以下内容:

  public abstract class LinqedTable where T : LinqableTable { public static T Get(long ID) { DataContext context = LinqUtils.GetDataContext(); var q = from obj in context.GetTable() where obj.IDValue == ID select obj; return q.Single(); } } 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(); 我收到运行时错误:

The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.

好的,今天我了解到Linq在后端做了某种魔术; 它不实例化obj并只读取IDValue属性。 所以必须要有一些属性需要在IDValue属性上设置,让Linq做它的事情。

但是什么?

Linq to SQL尝试将您的linq查询转换为SQL,但它不知道如何将您的属性转换为DB中的列名。

可以在SO上找到一个很好的解释: 简单的linq到sql没有支持的SQL转换

但如何解决它是另一回事。 我有成功使用了这个线程的apporoach:

http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/df9dba6e-4615-478d-9d8a-9fd80c941ea2/

或者您可以使用scott guthrie提到的动态查询:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

阅读这些post: 使用LINQ to SQL和C# , LINQ-to-SQL的 通用数据访问 :通用主键function和使用类型调用generics方法

我的同事和我想出了以下摘要:

我们在datacontext中添加了以下方法(在部分类中)。

 public T GetInstanceByPrimaryKey(object primaryKeyValue) where T : class { var table = this.GetTable(); 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>(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; 

希望这可以帮助!

由于引用LINQ-to-SQL IQueryable的LINQ语句被转换为SQL查询,因此您必须使用AsEnumerable扩展(这将导致读取数据库中的所有项)并在其上执行与reflection相关的内容。 IEnumerable的。
编辑
根据需要,这是一个澄清
正如评论中所指出的,我的意思是:

  (from obj in context.GetTable() 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执行的查询(在您的情况下)将通过获取表的所有条目然后按代码方式应用指定的filter来执行。