繁体   English   中英

使用IEnumerable <Expression<Func<T, Object> &gt;&gt;方法

[英]Use IEnumerable<Expression<Func<T, Object>>> in method

我有以下方法:

public void Update<T>(T entity, IEnumerable<Expression<Func<T, Object>>> properties)  
    where T : class 
{
    _context.Set<T>().Attach(entity);

    foreach (var property in properties)
        _context.Entry<T>(entity)
            .Property(((MemberExpression)property.Body).Member.Name)
            .IsModified = true;

} // Update

我正在传递一个Entity Framework实体,附加它并将每个属性设置为已修改。

我想用它如下:

_repository.Update<File>(file, new { x => x.Data, x => x.Name });

所以我传递一个文件并说数据和名称属性被修改。

但是我收到了错误:

The best overloaded method match for 'Update<File>(File,
IEnumerable<System.Linq.Expressions.Expression<System.Func<File,Object>>>)' 
has some invalid arguments

如上所述,我应该如何更改我的方法才能使用它?

或者可能:

_repository.Update<File>(file, x => x.Data, x => x.Name);

甚至:

_repository.Update<File>(file, x => new { x.Data, x.Name });

看起来你真的想要:

public void Update<T>(T entity, params Expression<Func<T, Object>>[] properties)
    where T : class

然后将其称为:

_repository.Update(file, x => x.Data, x => x.Name);

(注意我在这里使用类型推断而不是显式使用_repository.Update<File>(...) 。)

params部分是如何指定要转换为数组的多个参数。 根本不需要匿名类型。 如果你真的想要一个匿名类型,你可以通过反射访问它的成员 - 但这将是非常难看的,我怀疑你也需要转换每个lambda表达式(否则编译器将无法推断其类型) 。

编写方法的签名是为了接受一系列属性选择器,而不是包含两个属性的匿名类型,每个属性都是属性选择器,或者是选择包含对象的多个属性的匿名对象的属性选择器。

但语法相似; 创建一个隐式类型数组而不是一个匿名对象:

_repository.Update<File>(file, new[] { x => x.Data, x => x.Name });

如果您希望能够将每个lambda指定为单独的参数,那么您需要更改您的方法以使用params作为该参数:

public void Update<T>(T entity, params Expression<Func<T, Object>>[] properties)

在更改之后,以下调用将起作用:

_repository.Update<File>(file, x => x.Data, x => x.Name);

为了让你的解决方案能够选择一个使用所有成员工作的匿名类型的选择器,我们将会有更多的工作要做。 要做到这一点,我们需要创建一个表达式访问者,它通过整个表达式查看成员访问,拉出正在访问参数成员的那些(因为这些是我们关心的)并将所有这些存储在一起。 我们可以从ExpressionVisitor继承来轻松地完成这项工作,但是创建一个扩展方法以改进使用它的语法通常是值得的。

internal class MemberAccesses : ExpressionVisitor
{
    private ParameterExpression parameter;
    public HashSet<MemberExpression> Members { get; private set; }
    public MemberAccesses(ParameterExpression parameter)
    {
        this.parameter = parameter;
        Members = new HashSet<MemberExpression>();
    }
    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression == parameter)
        {
            Members.Add(node);
        }
        return base.VisitMember(node);
    }
}

public static IEnumerable<MemberExpression> GetPropertyAccesses<T, TResult>(
    this Expression<Func<T, TResult>> expression)
{
    var visitor = new MemberAccesses(expression.Parameters[0]);
    visitor.Visit(expression);
    return visitor.Members;
}

我们现在可以在方法中的选择器上调用此方法来提取我们关心的成员访问。 除了遍历整个树并拔出所有成员访问(如果有多个)之外,如果有人创建的选择器不仅仅是成员访问权限,那么它也不会中断并抛出异常,就像您的代码当前所做的那样(制作它有点脆弱)。

public void Update<T>(
    T entity, params Expression<Func<T, Object>>[] selectors)
    where T : class
{
    _context.Set<T>().Attach(entity);

    var members = selectors.SelectMany(
        selector => selector.GetPropertyAccesses());
    foreach (var member in members)
        _context.Entry<T>(entity)
            .Property(member.Member.Name)
            .IsModified = true;
}

对于这个,

_repository.Update(file, x => new { x.Data, x.Name });

我能想到

public void Update<T>(T entity, Func<T, object> modify)
{
    foreach (PropertyInfo p in modify(entity).GetType().GetProperties())
        _context.Entry<T>(entity).Property(p.Name).IsModified = true;
}

这应该可以解决问题,但它不会像Expression<Func<T, Object>>解决方案那样快。

并且有可能“欺骗”该方法

_repository.Update(file, x => new { DisplayName = x.Name });

另一个:

_repository.Update(file, new { file.Data, file.Name });

方法:

public void Update<T>(T entity, object modify)
{
    foreach (PropertyInfo p in modify.GetType().GetProperties())
        _context.Entry<T>(entity).Property(p.Name).IsModified = true;
}

暂无
暂无

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

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