【问题标题】:How to get returned properties from Expression<Func<T, object[]>> parameter如何从 Expression<Func<T, object[]>> 参数中获取返回的属性
【发布时间】:2021-05-14 15:14:48
【问题描述】:

我正在开发一个以.Net 5 为目标的.Net core 项目。 我有一个方法会接收一个参数,他的类型是Expression&lt;Func&lt;T , object[]&gt;&gt;,在方法内部我会循环从表达式返回的所有属性。

我的尝试:

public virtual void UpdateExcept( TEntity record, params Expression<Func<TEntity , object[]>>[] propertiesToBeExcluded )
{

   //Some logic here

    foreach ( var property in  propertiesToBeExcluded )
    {
        foreach ( var prop in property.GetMemberAccessList() )
        {
          //Here I got the property name (I think)
           var x = prop.Name;
        }
    }
}

在运行时出现此错误:

ArgumentException: 表达式 'x => new [] {x.CreatedBy, Convert(x.CreatedOn, Object)}' 不是有效的成员访问权限 表达。表达式应该代表一个简单的属性或字段 访问:'t => t.MyProperty'。当指定多个属性或 字段,使用匿名类型:'t => new { t.MyProperty, t.MyField }'。 (参数'propertyAccessExpression')

更多解释: 实际上,我在基于Entity framework 的存储库中创建了此方法,此方法应更新TEntity 记录并忽略(不更新)propertiesToBeExcluded 中的某些已发送属性有时我会更新记录并忽略一个属性,然后在另一次我将更新一条记录并忽略许多属性。

我试过的原始方法逻辑:

public virtual void UpdateExcept( TEntity record, params Expression<Func<TEntity , object[]>>[] propertiesToBeExcluded )
{
    var entity = Context.Set<TEntity>();
    entity.Attach( record );
    Context.Entry( record ).State = EntityState.Modified;

    foreach ( var property in  propertiesToBeExcluded )
    {
        foreach ( var prop in property.GetMemberAccessList())
        {
            Context.Entry( record ).Property( prop.Name ).IsModified = false;
        }
    }
}

此方法的使用示例:

_studentRepository.UpdateExcept( record : student , propertiesToBeExcluded : x => new object[] {x.Picture} );

另一个例子:

_studentRepository.UpdateExcept( record : student , propertiesToBeExcluded : x => new object[] {x.CreatedOn, x.CreatedBy} );

这个方法是这样的结构:

public virtual void UpdateExcept( TEntity record, params Expression<Func<TEntity , object>>[] propertiesToBeExcluded )
{
    var entity = Context.Set<TEntity>();
    entity.Attach( record );
    Context.Entry( record ).State = EntityState.Modified;

    foreach ( var property in  propertiesToBeExcluded )
    {
        Context.Entry( record ).Property( property ).IsModified = false;
    }
}

旧结构的使用示例:

_studentRepository.UpdateExcept( record : student , propertiesToBeExcluded : x => x.CreatedOn, x => x.CreatedBy );

为什么我从旧结构更改为新结构: 因为如果您注意到在旧结构中,我必须多次为func 编写x 的参数,而我不知道如何使用它一次并返回多个属性。

property.GetMemberAccessList()

我希望所有这些都可以帮助您理解这个问题,所以请帮助您解决这个问题?

【问题讨论】:

  • 什么是property.GetMemberAccessList()
  • @IvanStoev see here
  • 好的,现在我明白了。但请注意,这不是我们甚至不应该使用的标准(实际上是内部)EF Core 方法,因此至少您可以在问题中提到(或提供链接)。无论如何,除此之外我理解这个问题。
  • 不知道你是否明白,或者你有没有解决这个问题的办法,谢谢

标签: c# reflection entity-framework-core repository-pattern


【解决方案1】:

在 cmets 中澄清后,您尝试使用 GetMemberAccessList,这是 internal EF Core 方法,不应由最终用户代码直接使用。

但是假设您不关心/忽略该警告(这是警告,而不是错误),那么很高兴知道它不支持表达式返回属性访问器数组,但表达式返回 匿名类型,类似于处理复合键、多属性索引等的许多 EF Core fluent API。因此预期的签名是单个可选的Expression&lt;Func&lt;TEntity, object&gt;&gt;,没有params 数组:

public virtual void UpdateExcept(
    TEntity record,
    Expression<Func<TEntity, object>> propertiesToBeExcluded = null)
{
    var entity = Context.Set<TEntity>();
    entity.Attach(record);
    Context.Entry(record).State = EntityState.Modified;

    // The modified code
    if (propertiesToBeExcluded != null)
    {
        foreach (var property in propertiesToBeExcluded.GetMemberAccessList())
        {
            Context.Entry(record).Property(property).IsModified = false;
        }
    }
}

和用法类似

x => x.CreatedOn

x => new { x.CreatedOn }

对于单个属性,并且

x => new { x.CreatedOn, x.CreatedBy }

用于多个属性。

如果您想使用该方法,这一切都需要。


带有表达式返回表达式数组的方法签名(原始问题)不能使用该方法,需要完全不同的方法。

首先Expression&lt;Func&lt;T, object[]&gt;&gt;可以有多种调用方式,比如根本不涉及属性或者不直接创建数组

x => new object[] { 1, "abc", new DateTime() }

x => Enumerable.Range(1, 3).Select(i => (object)i).ToArray()

所有这些都是有效的调用,所以显然你必须对它的调用方式进行一些限制。假设预期的呼叫就像您的示例中一样。所以你唯一需要了解的是

body是什么类型的表达式
x => new object[] {x.CreatedOn, x.CreatedBy}

lambda 表达式。如果您在编译时创建此类表达式并在运行时使用调试器检查其内容,您可以轻松查看。

你会发现这个表达式是NewArrayExpression 类型,它有Expressions 属性。从这里开始应该很简单 - 转换主体并使用Expressions 属性从调用者那里获取包含x.CreatedOnx.CreatedBy 等的列表。

所有这些都使用像这样的自定义扩展方法

public static IEnumerable<MemberInfo> GetMemberAccessList<T>(this Expression<Func<T, object[]>> source) =>
    (source.Body is NewArrayExpression newArr ? newArr.Expressions : throw new InvalidOperationException())
    .Select(e => source.Parameters[0].MatchSimpleMemberAccess<MemberInfo>(e));

它正在使用另一种内部 EF Core 方法,但可以不这样做。使用它来实现有问题的所需方法。

【讨论】:

  • 首先,非常感谢您的回答、时间和精力请您提到propertiesToBeExcluded.Ge我在哪里可以找到Get
  • 糟糕,抱歉,这意味着您使用的方法与 GetMemberAccessList() 相同
  • 我收到了这个错误: ArgumentException: The expression 'x =&gt; new [] {x.CreatedBy, Convert(x.CreatedOn, Object)}' is not a valid member access expression. The expression should represent a simple property or field access: 't =&gt; t.MyProperty'. When specifying multiple properties or fields, use an anonymous type: 't =&gt; new { t.MyProperty, t.MyField }'. (Parameter 'propertyAccessExpression')
  • 您可能错过了这样一个事实,即通过答案第一部分的方法,您没有使用数组调用 - 没有[],只有new { ... }
  • 是的,你说得对,我忘了,现在一切正常,非常感谢兄弟,你帮助了我很多次,再次感谢你
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多