简体   繁体   English

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

[英]Entity Framework - expression on similar properties

I'm still fairly new to Entity Framework (currently developing using v4.1), so I may be going about this in completely the wrong way, so I'd welcome any guidance.我对 Entity Framework(目前正在使用 v4.1 进行开发)还是很陌生,所以我可能会以完全错误的方式来解决这个问题,所以我欢迎任何指导。

I currently have two classes: Item which just has a few simple fields, and FavouriteItem which holds a reference to an item and a user (but the user relationship isn't relevant to the discussion).我目前有两个类:只有几个简单字段的Item和保存对项目和用户的引用的FavouriteItem (但用户关系与讨论无关)。 The item has a nullable DateVisible field which I'm using as a flag to indicate if an item should be displayed or not.该项目有一个可为空的DateVisible字段,我将其用作指示是否应显示某项目的标志。

In order to get a list of visible items, I'm using a simple query:为了获得可见项目的列表,我使用了一个简单的查询:

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

Where obviously QueryAll returns an IQueryable.显然 QueryAll 返回一个 IQueryable。 This is a straightforward example, as the conditions are a little more complex, but it demonstrates my issue.这是一个简单的例子,因为条件有点复杂,但它说明了我的问题。

Next, if I want to return a list of favourites for items which are visible, then I can do a similar query:接下来,如果我想返回可见项目的收藏夹列表,那么我可以执行类似的查询:

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

This works fine.这工作正常。 However, the code is duplicated.但是,代码是重复的。 So what I'd like to do is somehow make this code more central - as I'm about to add a FollowingItem class which again has an Item property, and so I'd have to repeat the code again - not good.所以我想要做的是以某种方式使这段代码更集中 - 因为我将添加一个FollowItem类,该类再次具有 Item 属性,所以我不得不再次重复该代码 - 不好。

I started to create an expression for the list of items, which works well:我开始为项目列表创建一个表达式,效果很好:

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

And again, I can create these expressions for each class:同样,我可以为每个类创建这些表达式:

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

But this again just duplicates the code.但这又只是重复了代码。 Is there any way the same code be used between them.有什么办法可以在它们之间使用相同的代码。 I know I could do a join on the itemRepository.QueryAll().WhereVisible() , but is there not a way to do this that doesn't involve doing this everywhere?我知道我可以在itemRepository.QueryAll().WhereVisible()上做一个连接,但是有没有办法做到这一点而不涉及在任何地方都这样做?

Update : I tried creating an interface for the classes:更新:我尝试为这些类创建一个接口:

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

And creating an expression for it:并为它创建一个表达式:

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

Which I've called from:我打来的电话:

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

But it gives the error: " Unable to cast the type 'FavouriteItem' to type 'IEntityWithItem'. LINQ to Entities only supports casting Entity Data Model primitive types. "但它给出了错误:“无法将类型 'FavouriteItem' 转换为类型 'IEntityWithItem'。LINQ to Entities 仅支持转换实体数据模型原始类型。

There is to my knowledge no easy way to achieve this, however have a look at PredicateBuilder, which does solve some of these issues: http://www.albahari.com/nutshell/predicatebuilder.aspx .据我所知,没有简单的方法可以实现这一点,但是看看 PredicateBuilder,它确实解决了其中一些问题: http : //www.albahari.com/nutshell/predicatebuilder.aspx

The basic idea is that you define interfaces with the common properties, which allows you to define generic expressions constrained to that interface.基本思想是您定义具有公共属性的接口,这允许您定义约束到该接口的通用表达式。

Start by definining these interfaces:首先定义这些接口:

public interface IHaveItem
{
   Item Item {get;}
}

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

This will allow you to write the query expressions in a generic way, obtaining at least some reuse:这将允许您以通用方式编写查询表达式,至少获得一些重用:

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

You will still not be able to easily achieve reuse between expressions like x.DateVisible and x.Item.DateVisible however.但是,您仍然无法轻松实现x.DateVisiblex.Item.DateVisible等表达式之间的重用。

UPDATE: it turns out that 'unnecessary' cast operations are being added in the generated expression tree, which are then not supported by the entity framework provider when translating to SQL.更新:事实证明,在生成的表达式树中添加了“不必要的”转换操作,然后在转换为 SQL 时实体框架提供程序不支持这些操作。

You can apply a similar trick as the one in this question to remove these unnecessary cast operations 'manually'.您可以应用与此问题中的技巧类似的技巧来“手动”删除这些不必要的强制转换操作。 Becomes a bit hairy though IMHO...虽然恕我直言,但变得有点毛茸茸......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM