.NET泛型方法使用IQueryable<T>和Expression<Func<T, T>>作为参数

3
我想创建一个通用方法来执行这个函数,并将用户类和where子句作为参数传递。
public void Update(where clause, User user) {}

但我找不到正确的方法。
ctx.Users.Where(x => x.Id == 10)
         .Update(x => new User() { Name = "Jack" });

方法声明如下:
public static int Update<T>(this IQueryable<T> query, Expression<Func<T, T>> updateFactory) where T : class;

是的。我传递了之前创建的 User 实例。 - hanc
User类除了Name属性还有其他的属性吗?如果是的话,Update方法是否应该更新其他属性?例如,假设有一个Age属性,它也应该被更新吗?Update方法如何知道要更新哪些属性? - Yacoub Massad
是的。但它应该更新哪些属性呢?全部吗?请注意,一旦创建了对象,就无法知道您设置了哪些属性和哪些属性没有设置。例如,如果您有一个值为0的int Age属性,这可能意味着它在初始化期间未设置,也可能意味着它被明确设置为0。也许将Age设置为0正是您想要更新的内容。 - Yacoub Massad
属性是从数据库中读取的。因此它们在填充之前都已经被填满了。 - hanc
这意味着您想要更新所有属性/列吗? - Yacoub Massad
显示剩余5条评论
1个回答

0
如果您传递一个已经创建的User实例,Update方法将无法知道要更新哪些属性(例如,它应该更新所有属性吗?还是只更新非默认值的属性?如果某个属性故意设置为默认值会怎样?)。
您需要传递一个包含成员初始化表达式的Expression<Func<T, T>>表达式,例如x => new User() { Name = "Jack" }
表达式是代码数据化。内部框架将解析表达式并查看哪些属性旨在进行更新。在示例中,它将看到仅意图更新Name属性。
至于where表达式,您需要传递Expression<Func<User,bool>>。因此,您的方法将如下所示:
public void Update(
    Expression<Func<User,bool>> condition,
    Expression<Func<User,User>> updateFactory)
{
    ctx.Users
       .Where(condition)
       .Update(updateFactory);
}

你可以像这样使用它:

Update(x => x.Id == 10, x => new User() { Name = "Jack" });

编辑:

如果您有一种方法可以找到要更新的属性,您可以像这样构建表达式:

public static void Update(
    Expression<Func<User, bool>> condition,
    User user,
    PropertyInfo[] propertiesToUpdate)
{
    Expression<Func<User, User>> updateFactory =
        Expression.Lambda<Func<User, User>>(
            Expression.MemberInit(
                Expression.New(typeof (User)),
                propertiesToUpdate
                    .Select(prop =>
                        Expression.Bind(
                            prop.SetMethod,
                            Expression.Constant(prop.GetValue(user))))),
            Expression.Parameter(typeof(User), "u"));

    ctx.Users
        .Where(condition)
        .Update(updateFactory);
}

如果你只有属性名称,你可以使用 Type.GetProperty 方法来获取相应的 PropertyInfo


如果我只想传递User对象并使用一个函数来生成表达式,我该怎么做?我想检查属性是否设置为以下内容:var entry = _Context.Entry(entity); foreach (var property in entry.OriginalValues.PropertyNames) { var original = entry.OriginalValues.GetValue<object>(property); var current = entry.CurrentValues.GetValue<object>(property); if (original != null && !original.Equals(current)) { ---将属性更改附加到表达式 } } - hanc
我已经更新了我的回答。如果你知道要更新哪些属性,你可以自己构建表达式。 - Yacoub Massad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接