3

我对 Entity Framework(目前使用 v4.1 开发)还很陌生,所以我可能会以完全错误的方式来解决这个问题,所以我欢迎任何指导。

我目前有两个类:Item只有几个简单的字段,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)

这工作正常。但是,代码是重复的。所以我想做的是以某种方式使这段代码更加中心化——因为我即将添加一个再次具有 Item 属性的FollowItem类,所以我不得不再次重复代码——不好。

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

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 仅支持转换实体数据模型原始类型。

4

1 回答 1

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.DateVisibleand x.Item.DateVisible

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

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

于 2012-11-14T10:01:01.820 回答