【问题标题】:How to check if property exists in Type in an extension method?如何在扩展方法中检查类型中是否存在属性?
【发布时间】:2013-05-19 19:26:50
【问题描述】:

我经常有这样的代码:

var stRecs = db.<someTable>
             .Where(a => a.DepID == depID)

选择单个记录,但是如果depID == 0 我想取回所有记录。

我正在考虑创建一个扩展方法“WhereDepID_OrAll”,比如

public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source)
        where T: // is what?
{
    if(depID > 0) { return source.Where(a => a.depID == depID); }
    else return source.Where(a => a);
}

现在我的基本问题是: 我有几个带有 depID 的表 - 如何设置 Where T:
该方法将如何确定表是否具有 depID?

解决潜在问题的更好方法?

【问题讨论】:

  • 也许您应该创建一个接口IHaveDepId,并让您的表继承该接口:) 然后T : IHaveDepId。我觉得会更好
  • @JensKloster 没有。您不能在 linq to entity 查询中使用接口属性。
  • @RaphaëlAlthaus 为什么不呢?取决于您的方法-例如代码优先或模型优先,有很多方法可以修改 clr 类。
  • @RaphaëlAlthaus 我明白你的意思。数据库表必须拥有一个名为 exacly DepID 的属性(在这种情况下)

标签: c# linq extension-methods


【解决方案1】:

乍一看,反应是:创建一个界面

public interface ObjectWithDepartmentInterface {
    int depID;
}

使所有使用这个depId的实体都实现这个接口,并使用

where T : ObjectWithDepartmentInterface

但 linq to entity 不接受查询中接口的属性...例如:Expression generated based on interface

因此,唯一的方法是让您的实体具有从具有 depId 属性的公共实体(可能是抽象的)继承的 depId。

并将这个抽象实体用作

where T:

一种更简单(但更丑陋的方法)可能是不对 T 添加约束,在方法中构建谓词,并在坏情况下抛出异常。

if (typeof(T).GetProperty("depId") == null)
   throw InvalidOperationException (string.Format("{0}" doesn't have a depId property, typeof(T).Name))

编辑

但也许不是 depId 作为公共属性的问题 那么

public static IQueryable<T> WhereExistsOrAll<T>(this IQueryable<T> source, string propertyName, int value)
        where T: // is what?
{
    if (value == 0)
    return source;
    var parameter = Expression.Parameter(typeof(T), "m");
    Expression member = parameter;
    member = Expression.Property(member, propertyName);
    member = Expression.Equals(member, Expression.Constant(value));
    var lambda = Expression.Lambda<Func<T, bool>>(member, new[]{parameter});
    return source.Where(lambda);
}

用法

var stRecs = db.<someTable>.WhereExistsOrAll("depId", depId);

编辑 2

另一种方法是解析谓词以获得“常量”值

类似的东西

public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
{
       var expression = predicate.Body as BinaryExpression;

       var rightPart = expression.Right as MemberExpression;
       var value = GetValue(rightPart);
       var test = value.ToString();
       int val;
       if (Int32.TryParse(value.ToString(), out val))
       {
           if (val != 0)
               return queryable.Where(predicate);
       }

       return queryable;
}

private static object GetValue(MemberExpression member)
{
     var objectMember = Expression.Convert(member, typeof(object));
     var getterLambda = Expression.Lambda<Func<object>>(objectMember);
     var getter = getterLambda.Compile();
     return getter();
}

用法

var stRecs = db.<someTable>.GetAllOrRestrict(m => m.depID == depId);

【讨论】:

    【解决方案2】:

    我知道这不是特别时髦,但这不正是the Query Builder methods in Entity Framework 的用途吗?

    var stRecs = db.<someTable>
                 .Where("it.DepID == @depID OR @depID = 0", 
                        new ObjectParameter("depID", depID));
    

    这适用于任何someTable,因此它有一个名为DepID 的列。当然也可以做成扩展方法:

    public static ObjectQuery<T> WhereIdEqualOrAll<T>(this ObjectQuery<T> q, int depID)
        where T : class
    {
        return q.Where("it.DepID = @id OR @id = 0", new ObjectParameter("id", id));
    }
    

    这样调用:

    var stRecs = db.<someTable>.WhereIdEqualOrAll(depID);
    

    【讨论】:

      【解决方案3】:

      使用接口:

      public interface IInterface
      {
         int depId;
      }
      

      这将强制TIInterface 继承并实现depId

      然后你可以将它添加到扩展中:

      public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source) where T: IInterface
      {
      
      }
      

      【讨论】:

      【解决方案4】:

      使用接口,然后手动构建表达式树,引用实际的类。

      【讨论】:

        【解决方案5】:

        Raphaels Edit 2 完成了这项工作!

        小版: 如果存在 DepID,我如何包含 NULL 值?

        我喜欢返回 ID == x OR ID == NULL 的所有部门

        也许还有一个额外的 bool includeNullValues)

        public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable,
         Expression<Func<T, bool>> predicate, 
         bool includeNullValues)
            {
                var expression = predicate.Body as BinaryExpression;
        
                var rightPart = expression.Right as MemberExpression;
                var value = GetValue(rightPart);
                var test = value.ToString();
                int val;
                if (Int32.TryParse(value.ToString(), out val))
                {
                    if (val != 0)
                    {
                        if (includeNullValues)
                        {
                            var parameter = Expression.Parameter(typeof(T),"m");
        
                            return queryable.Where(predicate) <====HOW to " || depID == null) ???
                        }
                        else
                        {
                        return queryable.Where(predicate);
                        }
                    }
                }
        
                return queryable;
            }
        

        【讨论】:

        • 为了清楚起见,您宁愿问另一个问题...无论如何,您的意思是 depId (或使用的任何属性)可能是 Nullable&lt;int&gt; ?或者它是导航属性的一部分,可能为空(类似于(m =&gt; m.MyEntity.DepId == depId)
        • 然后你必须为谓词的所有左侧部分重新创建表达式......(当然可以,但更复杂)。但你也可以只做var stRecs = db.&lt;someTable&gt;.GetAllOrRestrict(m =&gt; (m.depID ?? depId) == depId);(我必须承认,这不太“好”)
        • 我很困惑 - 这行得通 - 我不得不承认,我不明白 (m.depID ?? depId) == depId 部分......哦,好吧......我将不得不挖掘更深入地按位,我猜!非常感谢!!
        • 这不是按位的,它是coalesce 运算符。它的意思是:if m.depId == 0 then take the value of your parameter,这和你想要的一样;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-02-25
        • 1970-01-01
        • 2013-05-12
        • 2012-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多