实体框架 - 类似属性的表达式

时间:2012-11-14 09:43:38

标签: c# .net entity-framework entity-framework-4.1

我仍然是Entity Framework的新手(目前正在使用v4.1开发),所以我可能会以完全错误的方式解决这个问题,所以我欢迎任何指导。

我目前有两个类:,它只有一些简单的字段, FavouriteItem ,它包含对项目和用户的引用(但用户关系是与讨论有关。该项目有一个可以为空的 DateVisible 字段,我将其用作标记,以指示是否应显示某个项目。

为了获得可见项目列表,我使用的是一个简单的查询:

var items = itemRepository
               .QueryAll()
               .Where(i => i.DateVisible <= DateTime.Now)

显然,QueryAll返回IQueryable。这是一个简单的例子,因为条件稍微复杂一些,但它证明了我的问题。

接下来,如果我想返回可见项目的收藏列表,那么我可以进行类似的查询:

var favourites= favouriteRepository
                   .QueryAll()
                   .Where(f => f.Item.DateVisible <= DateTime.Now)

这很好用。但是,代码是重复的。所以我想做的是以某种方式使这段代码更加重要 - 因为我要添加一个 FollowingItem 类,它又有一个Item属性,所以我必须重复代码再次 - 不好。

我开始为项目列表创建一个表达式,效果很好:

public static Expression<Func<Item, bool>> IsVisibleExpression<Item>()
{
     return item => item.DateVisible != null && 
                    item.DateVisible <= DateTime.Now;
}

public static IQueryable<Item> WhereVisible<Item>(this IQueryable<Item> queryable)
{
     return queryable.Where(Item.IsVisibleExpression());
}

var items = itemRepository
               .QueryAll()
               .WhereVisible()

再次,我可以为每个类创建这些表达式:

public static Expression<Func<FavouriteItem, bool>> IsVisibleExpression<FavouriteItem>()
{
     return favourite => favourite.Item.DateVisible != null && 
                         favourite.Item.DateVisible <= DateTime.Now;
}

但这又重复了代码。有没有办法在它们之间使用相同的代码。我知道我可以加入itemRepository.QueryAll().WhereVisible(),但是没有办法做到这一点,不涉及到处都这样做吗?

更新:我尝试为这些类创建一个界面:

public interface IEntityWithItem
{
    Item Item { get; set; }
}

为它创建一个表达式:

    public static IQueryable<TEntity> WhereItemVisible<TEntity>(this IQueryable<TEntity> queryable) where TEntity : IEntityWithItem
    {
        return queryable.Where(ui => ui.Item.DateVisibleFrom != null &&
                                     ui.Item.DateVisibleFrom.Value <= DateTime.Now);
    }

我打过电话:

// Get the favourites
var favourites = favouriteItemRepository.QueryAll()
                      .WhereItemVisible()
                      .Where(favourite => favourite.UserId == user.Id);

但是它给出了错误:“无法将类型'FavouriteItem'转换为'IEntityWithItem'类型.LINQ to Entities仅支持转换实体数据模型基元类型。

1 个答案:

答案 0 :(得分:0)

据我所知,没有简单的方法来实现这一点,但是看看PredicateBuilder,它解决了其中的一些问题:http://www.albahari.com/nutshell/predicatebuilder.aspx

基本思想是使用公共属性定义接口,这允许您定义约束到该接口的通用表达式。

首先确定这些界面:

public interface IHaveItem
{
   Item Item {get;}
}

public interface IDateVisible
{
   DateTime? DateVisible {get;}
}

这将允许您以通用方式编写查询表达式,至少获得一些重用:

public static Expression<Func<T,bool>> IsVisible<T>(DateTime now) 
    where T:IHaveItem
{
   return x => x.Item.DateVisible != null &&
          x.Item.DateVisible < now;
}

但是,您仍然无法在x.DateVisiblex.Item.DateVisible之类的表达式之间轻松实现重用。

UPDATE:事实证明,在生成的表达式树中添加了“不必要的”强制转换操作,实体框架提供程序在转换为SQL时不支持这些操作。

您可以应用与this question中类似的技巧来手动删除这些不必要的强制转换操作。虽然恕我直言,但变得有点毛茸茸......