热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在对象内的对象上的实体框架LINQ表达式

如何解决《在对象内的对象上的实体框架LINQ表达式》经验,为你挑选了1个好方法。

编辑此问题,以期使其更清楚。

我们有实体框架代码优先设置。为了示例目的,我简化了两个类,实际上,与“记录”类似,大约有十多个类,其中“项”是导航属性/外键。

物品类别:

public class Item
{
    public int Id { get; set; }
    public int AccountId { get; set; }
    public List UserItemMappings { get; set; }
    public List GroupItemMappings { get; set; }
}

记录类:

public class Record 
{
    public int ItemId { get; set; }
    public Item Item { get; set; }
}

this.User是每个存储库中注入的用户对象,包含在存储库库中。我们有一个包含以下代码的Item存储库:

var items = this.GetAll()
    .Where(i => i.AccountId == this.User.AccountId);

我在存储库的基础上创建了follow表达式,以轻松地对其进行过滤(以希望再次使用)。由于LINQ to实体的工作方式,我们无法使用静态扩展方法(System.NotSupportedException“ LINQ to Entities无法识别方法X,并且该方法无法转换为存储表达式。”)。

protected Expression> ItemIsOnAccount()
{
    return item => item.AccountId == this.User.AccountId;
}

通过这样做,我已经解决了上述情况:

var items = this.GetAll().Where(this.ItemIsOnAccount());

我们还基于该帐户内的用户权限进行了其他过滤(同样,在另一种情况下,我不想在我们拥有的每个存储库中重复此代码):

protected Expression> SubUserCanAccessItem()
{
    return item => this.User.AllowAllItems 
        || item.UserItemMappings.Any(d => d.UserId.Value == this.User.Id) 
        || item.GroupItemMappings.Any(vm => 
            vm.Group.GroupUserMappings
                .Any(um => um.UserId == this.User.Id));
}

我可以使用以下方法:

    var items = this.GetAll().Where(this.SubUserCanAccessItem());

但是,我们还需要在Record存储库中解决以下问题:

var records = this.GetAll()
    .Where(i => i.Item.AccountId == this.User.AccountId);

因为Item是单个导航属性,所以我不知道如何将创建的表达式应用于该对象。

我想重用我在所有其他所有存储库中在存储库中创建的表达式,以便我的“基于权限”代码都位于同一位置,但是我不能简单地将其放入,因为在这种情况下,Where子句是表达式>。

使用以下方法创建接口:

Item GetItem();

由于存在实体的LINQ,因此无法将其放到Record类上。

我也不能创建基础抽象类并从其继承,因为除Item之外,可能还有其他对象需要过滤。例如,记录中可能还包含具有权限逻辑的“事物”。并非所有对象都需要按“项”和“事物”进行过滤,某些对象仅一个,另一个,另一个。

var items = this.GetAll()
    .Where(this.ItemIsOnAccount())
    .Where(this.ThingIsOnAccount());

var itemType2s = this.GetAll().Where(this.ThingIsOnAccount());

var itemType3s = this.GetAll().Where(this.ItemIsOnAccount());

因此,只有一个父类是行不通的。

有没有一种方法可以重用已经创建的表达式,或者至少创建一个表达式/修改原始表达式以在OTHER仓库内全面使用,当然可以在GetAll中返回自己的对象,但是所有对象都有一个导航属性到项目?我需要如何修改其他存储库才能使用它们?

谢谢



1> Ivan Stoev..:

表达式可重用性的第一步是将表达式移到公共静态类。由于在您的情况下,它们与绑定在一起User,因此我将它们作为User扩展方法(但请注意,它们将返回表达式):

public static partial class UserFilters
{
    public static Expression> OwnsItem(this User user)
        => item => item.AccountId == user.AccountId;

    public static Expression> CanAccessItem(this User user)
    {
        if (user.AllowAllItems) return item => true;
        return item => item.UserItemMappings.Any(d => d.UserId.Value == user.Id) ||
            item.GroupItemMappings.Any(vm => vm.Group.GroupUserMappings.Any(um => um.UserId == user.Id));
    }
}

现在Item存储库将使用

var items = this.GetAll().Where(this.User.OwnsItem());

要么

var items = this.GetAll().Where(this.User.CanAccessItem());

为了可重复用于具有Item引用的实体,您需要一个小的帮助程序实用程序,用于将其他lambda表达式组成的lambda表达式,类似于将Linq表达式“ obj => obj.Prop”转换为“ parent => parent.obj.Prop”。

可以使用来实现它Expression.Invoke,但是由于并非所有查询提供程序都支持调用表达式(不能肯定EF6可以支持EF Core,因此),像往常一样,我们将使用自定义表达式访问器将lambda参数表达式替换为另一个任意表达式:

public static partial class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
        => new ParameterReplacer { Source = source, Target = target }.Visit(expression);

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
            => node == Source ? Target : node;
    }
}

而且,这两个组成功能如下(我不喜欢这个名字Compose,所以有时我用的名字Map,有时SelectBindTransform等,但它们在功能上做同样的,在这种情况下,我使用的是ApplyApplyTo,唯一的区别作为转换方向):

public static partial class ExpressionUtils
{
    public static Expression> Apply(this Expression> outer, Expression> inner)
        => Expression.Lambda>(inner.Body.ReplaceParameter(inner.Parameters[0], outer.Body), outer.Parameters);

    public static Expression> ApplyTo(this Expression> inner, Expression> outer)
        => outer.Apply(inner);
}

(没有什么特别的,提供了完整的代码)

现在,您可以通过将原始过滤器“应用”到Item从另一个实体中选择属性的表达式来重用原始过滤器:

public static partial class UserFilters
{
    public static Expression> Owns(this User user, Expression> item)
        => user.OwnsItem().ApplyTo(item);

    public static Expression> CanAccess(this User user, Expression> item)
        => user.CanAccessItem().ApplyTo(item);
}

并将以下内容添加到实体存储库(在本例中为Record存储库):

static Expression> RecordItem => entity => entity.Item;

这将使您可以在那里使用

var records = this.GetAll().Where(this.User.Owns(RecordItem));

要么

var records = this.GetAll().Where(this.User.CanAccess(RecordItem));

这应该足以满足您的要求。


您可以进一步定义这样的接口

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

并让实体实施

public class Record : IHasItem // <--
{
    // Same as in the example - IHasItem.Item is auto implemented
    // ... 
}

然后添加其他这样的助手

public static partial class UserFilters
{
    public static Expression> GetItem() where T : class, IHasItem
        => entity => entity.Item;

    public static Expression> OwnsItem(this User user) where T : class, IHasItem
        => user.Owns(GetItem());

    public static Expression> CanAccessItem(this User user) where T : class, IHasItem
        => user.CanAccess(GetItem());
}

这将允许您省略RecordItem存储库中的表达式并使用它来代替

var records = this.GetAll().Where(this.User.OwnsItem());

要么

var records = this.GetAll().Where(this.User.CanAccessItem());

不知道它是否可以为您提供更好的可读性,但是它是一个选择,从语法上来说更接近Item方法。

对于Thing等,只需添加类似的UserFilters方法。


作为奖励,您可以更进一步,添加常用PredicateBuilder方法AndOr

public static partial class ExpressionUtils
{
    public static Expression> And(this Expression> left, Expression> right)
        => Expression.Lambda>(Expression.AndAlso(left.Body,
            right.Body.ReplaceParameter(right.Parameters[0], left.Parameters[0])), left.Parameters);

    public static Expression> Or(this Expression> left, Expression> right)
        => Expression.Lambda>(Expression.OrElse(left.Body,
            right.Body.ReplaceParameter(right.Parameters[0], left.Parameters[0])), left.Parameters);
}

所以你可以根据需要使用这样的东西

var items = this.GetAll().Where(this.User.OwnsItem().Or(this.User.CanAccessItem()));

Item存储库中,或

var records = this.GetAll().Where(this.User.OwnsItem().Or(this.User.CanAccessItem()));

Record存储库中。


推荐阅读
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • 标题: ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
author-avatar
T糖糖Tsweet_629
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有