C# linq/lambda表达式的泛型类
C#实体框架4.0 我有一个数据库,有10个表,有两个公共列“id”和“modstamp” 要访问表中的modstamp,我有一个函数C# linq/lambda表达式的泛型类,c#,linq,generics,lambda,C#,Linq,Generics,Lambda,C#实体框架4.0 我有一个数据库,有10个表,有两个公共列“id”和“modstamp” 要访问表中的modstamp,我有一个函数 protected internal override string GetModStampinChild(int sid) { DBContext sq = new DBContext(); return sq.xxxx.Where(s => s.id == sid) .Select(s => s.modstamp)
protected internal override string GetModStampinChild(int sid)
{
DBContext sq = new DBContext();
return sq.xxxx.Where(s => s.id == sid)
.Select(s => s.modstamp).SingleOrDefault().ToModStampString();
}
其中xxxx为每个表更改。
我目前正在为每个表重写此函数
有没有一种方法可以使用某种我可以在“xxxx”是任何表时使用的通用“类”?如果我理解正确,您需要一个
DbContext
类的属性Set
:
首先,使用id
和modstamp
属性创建所有实体类的基类。然后:
protected internal override string GetModStampInChild<T>(int sid) where T : BaseEntity
{
using (var sq = new DbContext())
{
return sq.Set<T>.Where(s => s.id == sid)
.Select(s => s.modstamp)
.SingleOrDefault()
.ToModStampString();
}
}
受保护的内部重写字符串GetModStampInChild(int sid),其中T:BaseEntity
{
使用(var sq=new DbContext())
{
返回sq.Set.Where(s=>s.id==sid)
.选择(s=>s.modstamp)
.SingleOrDefault()
.tomodestampstring();
}
}
但是您必须使用代码优先范式来实现此方法。首先,您需要让所有实体实现一个接口或一个抽象类,其中包含
ID
和ModStamp
属性,我们称之为Stampable
:
public abstract class Stampable
{
[Key]
public int ID { get; set; }
[Required]
public string ModStamp { get; set; }
}
在这一点上,您只需让方法实现泛型类型:
protected internal override string GetModStampInChild<T>(int sid) where T : Stampable
{
using (var sq = new DbContext())
{
return sq.Set<T>.Where(s => s.id == sid)
.Select(s => s.modstamp)
.SingleOrDefault()
.ToModStampString();
}
}
受保护的内部重写字符串GetModStampInChild(int sid),其中T:Stampable
{
使用(var sq=new DbContext())
{
返回sq.Set.Where(s=>s.id==sid)
.选择(s=>s.modstamp)
.SingleOrDefault()
.tomodestampstring();
}
}
另一个选项是通过c#的部分类功能向实体类添加新属性
因此生成的实体定义可能如下所示,注意我不知道ModStamp列的实际数据类型是什么:
public partial class Company
{
public int Id { get; set; }
public byte[] ModStamp { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
}
注意要转换的ModStamp列
然后将EF创建的代码添加到Partial.cs文件中,注意,我不知道您实际想对ModStamp值做什么:
public static class ModConverter
{
public static string ToModStampString(byte[] modStamp)
{
return BitConverter.ToString(modStamp);
}
}
public partial class Company
{
public string ModStampString
{
get
{
return ModConverter.ToModStampString(this.ModStamp);
}
}
}
然后,您必须为每个具有ModStamp列的实体手动添加一个新的ModStampString Get属性,就像我为公司实体所做的那样。下面是一个解决方案,它使用DbContext和表达式树上的Set方法来动态查询该对象
private Expression<Func<TArg, bool>> CreatePredicate<TArg, TPredicateField>(string fieldName, TPredicateField value)
{
ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
MemberExpression memberExpression = Expression.Property(parameter, fieldName);
var condition = Expression.Equal(memberExpression, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TArg, bool>>(condition, parameter);
return lambda;
}
private Expression<Func<TArg, TPredicateField>> CreateSelector<TArg, TPredicateField>(string fieldName)
{
ParameterExpression parameter = Expression.Parameter(typeof(TArg), "o");
Expression propertyExpr = Expression.Property(parameter, fieldName);
var lambda = Expression.Lambda<Func<TArg, TPredicateField>>(propertyExpr, parameter);
return lambda;
}
public TSelectorField GetModStamp<TEntity, TPredicateField, TSelectorField>(TPredicateField id) where TEntity : class
{
using (var ctx = new OnTheFlyEntities("Data Source=(local);Initial Catalog=AscensionBO;Integrated Security=True;MultipleActiveResultSets=True"))
{
var predicate = CreatePredicate<TEntity, TPredicateField>("Id", id);
var selector = CreateSelector<TEntity, TSelectorField>("ModStamp");
TSelectorField item = ctx.Set<TEntity>().Where(predicate).Select(selector).SingleOrDefault();
return item;
}
}
private Expression CreatePredicate(字符串字段名,TPredicateField值)
{
ParameterExpression参数=表达式参数(typeof(target),“o”);
MemberExpression MemberExpression=Expression.Property(参数、字段名);
变量条件=表达式.Equal(memberExpression,表达式.常量(值));
var lambda=表达式.lambda(条件、参数);
返回lambda;
}
专用表达式CreateSelector(字符串字段名)
{
ParameterExpression参数=表达式参数(typeof(target),“o”);
Expression propertyExpr=Expression.Property(参数、字段名);
var lambda=Expression.lambda(propertyExpr,参数);
返回lambda;
}
公共TSelectorField GetModStamp(TPredicateField id),其中tenty:class
{
使用(var ctx=new on thefly entities(“数据源=(本地);初始目录=AscensionBO;集成安全性=True;MultipleActiveResultSets=True”))
{
var谓词=CreatePredicate(“Id”,Id);
var选择器=CreateSelector(“ModStamp”);
TSelectorField item=ctx.Set().Where(谓词).Select(选择器).SingleOrDefault();
退货项目;
}
}
你可以这样称呼它:
GetModStamp<Entity2, int, string>(1)
GetModStamp(1)
如果您愿意只返回找到的实体,那么可以删除TSelectorField,然后在检索该项后从该项中获取ModStamp。这将在主方法上删除一个表达式树方法和一个泛型输入
正如其他人所建议的,您可以按照接口路线使用该示例,这会简单得多。我相信您可以通过让所有表实体实现一个公开
id
和modstamp
属性的接口来实现这一点。当DBContext sq=new DBContext()使用code>时没有try{}finally{}
或using(){}
块。您能否澄清为什么要独立于实体中的其他列检索ModStamp?请将新的DBContext()调用包装在using语句中,否则将泄漏该连接。add using everywhere太糟糕了,它不在Microsoft模板中。从性能角度看,当您可能已经在其他DBContext()中检索到该值时,是否真的要进行另一次数据库往返以获取ModStamp。这是一个很好的观点,但我将此示例视为一个一般概念,而不是解决问题。modstamp是一个时间戳,用于检测乐观并发问题。但在大多数情况下,我都需要将整行放入。这是Sql Server时间戳数据类型吗?如果是这样,您不应该做任何事情,因为EF会将其映射到byte[]数据类型。我首先使用DB,这就是为什么我会出现编译错误:“System.Data.Entity.DbContext.Set()”是一个“方法”,在给定上下文中无效。如果我添加()在我得到另一个编译错误后:类型“T”必须是引用类型才能在泛型类型或方法中将其用作参数“tenty”。是的,我忘记了,Set
是方法,而不是属性,但这并不重要。不幸的是,此方法不能首先用于DB,因为在这种情况下不能自动生成实体。如上所述,Set()会给出一个编译错误。SET()没有“where”扩展名。EF 6.0(如果组件版本为4.0)