C# linq/lambda表达式的泛型类

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)

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).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()时没有
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)