【发布时间】:2009-04-09 17:24:04
【问题描述】:
我写了下面的方法。
public T GetByID(int id)
{
var dbcontext = DB;
var table = dbcontext.GetTable<T>();
return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}
基本上它是泛型类中的一个方法,其中T 是DataContext 中的一个类。
该方法从 T 的类型 (GetTable) 中获取表,并检查输入参数的第一个属性(始终是 ID)。
问题是我必须先将元素表转换为列表才能在属性上执行GetType,但这不是很方便,因为表中的所有元素都必须枚举并转换为List。
如何重构此方法以避免整个表出现ToList?
[更新]
我不能直接在桌子上执行Where的原因是因为我收到了这个异常:
方法 'System.Reflection.PropertyInfo[] GetProperties()' 不支持 SQL 转换。
因为GetProperties 无法转换为 SQL。
[更新]
有人建议使用T的接口,但问题是T参数将是一个在[DataContextName].designer.cs,因此我不能让它实现一个接口(并且为 LINQ 的所有这些“数据库类”实现接口是不可行的;而且,一旦我将新表添加到 DataContext,文件将重新生成,从而丢失所有写入的数据)。
所以,必须有更好的方法来做到这一点......
[更新]
我现在已经实现了我的代码,如 Neil Williams' 建议,但我仍然遇到问题。以下是代码摘录:
接口:
public interface IHasID
{
int ID { get; set; }
}
DataContext [查看代码]:
namespace MusicRepo_DataContext
{
partial class Artist : IHasID
{
public int ID
{
get { return ArtistID; }
set { throw new System.NotImplementedException(); }
}
}
}
通用方法:
public class DBAccess<T> where T : class, IHasID,new()
{
public T GetByID(int id)
{
var dbcontext = DB;
var table = dbcontext.GetTable<T>();
return table.SingleOrDefault(e => e.ID.Equals(id));
}
}
在这一行抛出异常:return table.SingleOrDefault(e => e.ID.Equals(id));,异常是:
System.NotSupportedException: The member 'MusicRepo_DataContext.IHasID.ID' has no supported translation to SQL.
[更新]解决方案:
借助Denis Troller 发布的答案和Code Rant blog 的帖子链接,我终于找到了解决方案:
public static PropertyInfo GetPrimaryKey(this Type entityType)
{
foreach (PropertyInfo property in entityType.GetProperties())
{
ColumnAttribute[] attributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), true);
if (attributes.Length == 1)
{
ColumnAttribute columnAttribute = attributes[0];
if (columnAttribute.IsPrimaryKey)
{
if (property.PropertyType != typeof(int))
{
throw new ApplicationException(string.Format("Primary key, '{0}', of type '{1}' is not int",
property.Name, entityType));
}
return property;
}
}
}
throw new ApplicationException(string.Format("No primary key defined for type {0}", entityType.Name));
}
public T GetByID(int id)
{
var dbcontext = DB;
var itemParameter = Expression.Parameter(typeof (T), "item");
var whereExpression = Expression.Lambda<Func<T, bool>>
(
Expression.Equal(
Expression.Property(
itemParameter,
typeof (T).GetPrimaryKey().Name
),
Expression.Constant(id)
),
new[] {itemParameter}
);
return dbcontext.GetTable<T>().Where(whereExpression).Single();
}
【问题讨论】:
-
您无需担心设计器生成的文件或 edmx 设计器过度编写它们。您无需在设计器文件中实现接口。您将为实现接口的实体。
-
但这意味着我应该让每个“db类”都实现这个接口,不是吗?
-
是的,它会,但这是一次性的工作,然后你的代码会更加健壮。
-
GetPrimaryKey 方法有点狡猾,linq to sql 并不总是使用属性来解释映射,您可以完全使用 dbml.. 但是无论您使用什么,在 Mappings 定义示例中都是相同的Denis Troller 给的。
-
嗯谢谢你的建议;会调查的
标签: c# linq-to-sql generics expression-trees