linq / lambda 表达式的泛型类

本文关键字:泛型类 表达式 lambda linq | 更新日期: 2023-09-27 18:36:50

C# 实体框架 4.0

我有一个包含 10 个表格的数据库,其中包含 2 个公共列"id"和"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"是任何表的地方使用?

linq / lambda 表达式的泛型类

首先

,您需要让所有实体实现一个接口或包含IDModStamp属性的抽象类,我们称之为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();
    }
}

如果我理解正确,你需要一个DbContext类的属性Set<T>

首先,创建具有idmodstamp属性的所有实体类的基类。然后:

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();
    }
}

但是,必须对此方法使用代码优先范例。

另一种选择是通过 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 创建如下代码的 Part.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;
    }
}

然后你可以像这样称呼它:

GetModStamp<Entity2, int, string>(1)

如果您愿意只返回找到的实体,则可以消除 TSelector字段,然后在检索到项目后从项目中获取 ModStamp。 这将删除一个表达式树方法和一个主方法上的泛型输入。

正如其他人建议的那样,您可以走接口路线并使用该示例,它会简单得多。