I have the following 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
I am passing an Entity Framework entity, attaching it and Set each property as modified.
我正在传递一个实体框架实体,附加它并将每个属性设置为已修改。
I would like to use it as follows:
我想这样使用它:
_repository.Update<File>(file, new { x => x.Data, x => x.Name });
So I am passing a file and saying the Data and Name properties were modified.
所以我传递了一个文件,说数据和名称属性被修改了。
But I am getting the error:
但我得到了一个错误:
The best overloaded method match for 'Update<File>(File,
IEnumerable<System.Linq.Expressions.Expression<System.Func<File,Object>>>)'
has some invalid arguments
How should I change my method to be able to use it as I mentioned?
我该如何改变我的方法才能像我提到的那样使用它呢?
Or maybe:
或者:
_repository.Update<File>(file, x => x.Data, x => x.Name);
Or even:
甚至:
_repository.Update<File>(file, x => new { x.Data, x.Name });
3 个解决方案
#1
8
It looks like you really want:
看起来你真的想:
public void Update<T>(T entity, params Expression<Func<T, Object>>[] properties)
where T : class
and then call it as:
然后把它叫做
_repository.Update(file, x => x.Data, x => x.Name);
(Note that I'm using type inference here rather than explicitly using _repository.Update<File>(...)
.)
(注意,这里我使用的是类型推断,而不是显式地使用_repository.Update . update
The params
part is how you can specify multiple arguments to be converted into an array. There's no need for an anonymous type at all. if you really wanted an anonymous type, you could access its members by reflection - but that would be pretty ugly and I suspect you'd need to cast each lambda expression too (as otherwise the compiler wouldn't be able to infer its type).
params部分是如何指定要转换为数组的多个参数。根本不需要匿名类型。如果你真的想要匿名类型,你可以通过反射来访问它的成员——但是那样会很难看,我怀疑你也需要对每个lambda表达式进行强制转换(否则编译器将无法推断出它的类型)。
#2
2
Your method's signature is written to accept a sequence of property selectors, not an anonymous type containing two properties, each of which are property selectors or a property selector that is selecting out an anonymous object containing several properties of your object.
方法的签名被编写为接受一个属性选择器序列,而不是包含两个属性的匿名类型,每个属性都是属性选择器或属性选择器,选择一个包含对象的多个属性的匿名对象。
The syntax is similar enough though; create an implicitly typed array rather than an anonymous object:
语法也很相似;创建隐式类型化数组而不是匿名对象:
_repository.Update<File>(file, new[] { x => x.Data, x => x.Name });
If you want to be able to specify each of the lambdas as separate parameters then you'd need to alter your method to use params
for that argument:
如果你想要将每一个lambdas指定为独立的参数,那么你需要改变你的方法来使用参数params:
public void Update<T>(T entity, params Expression<Func<T, Object>>[] properties)
After that change the following call would work:
在此更改之后,下面的调用将会生效:
_repository.Update<File>(file, x => x.Data, x => x.Name);
To allow your solution for a single selector that selects out an anonymous type using all of the members to work we'll have a bit more work ahead of us. To do this we'll need to create an expression visitor that looks through an entire expression for member accesses, pulls out the ones that are accessing members of the parameter (because those are what we care about) and stores all of them together. We can inherit from ExpressionVisitor
to do this reasonably easily, but it's generally worth it to create an extension method to improve the syntax for utilizing it.
为了让您的解决方案选择一个使用所有成员的匿名类型的选择器,我们将在我们前面做更多的工作。为此,我们需要创建一个表达式访问器,该访问器通过一个完整的表达式来查找成员访问,取出访问参数成员的表达式(因为这些是我们关心的),并将它们存储在一起。我们可以从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;
}
We can now call this method on the selectors in your method to pull out the member accesses that we care about. In addition to traversing the entire tree and pulling out all member accesses if there are several, it also doesn't break and throw an exception if someone creates a selector that is anything other than just a member access, as your code currently does (making it somewhat fragile).
现在,我们可以在您的方法中调用该方法来提取我们所关心的成员访问。除了遍历整个树并在有多个成员访问时取出所有成员访问之外,如果有人创建了一个除成员访问之外的任何选择器(如您的代码当前所做的那样),它也不会中断并抛出异常。
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;
}
#3
1
For this one,
为了这一个,
_repository.Update(file, x => new { x.Data, x.Name });
I could think of
我能想到的
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;
}
This should do the trick, but it won't be as fast as an Expression<Func<T, Object>>
solution.
这应该可以实现,但是它不会像表达式
And there is a possibility to "trick" the method with
而且有可能“欺骗”这个方法
_repository.Update(file, x => new { DisplayName = x.Name });
Another one:
另一个:
_repository.Update(file, new { file.Data, file.Name });
the method:
方法:
public void Update<T>(T entity, object modify)
{
foreach (PropertyInfo p in modify.GetType().GetProperties())
_context.Entry<T>(entity).Property(p.Name).IsModified = true;
}
#1
8
It looks like you really want:
看起来你真的想:
public void Update<T>(T entity, params Expression<Func<T, Object>>[] properties)
where T : class
and then call it as:
然后把它叫做
_repository.Update(file, x => x.Data, x => x.Name);
(Note that I'm using type inference here rather than explicitly using _repository.Update<File>(...)
.)
(注意,这里我使用的是类型推断,而不是显式地使用_repository.Update . update
The params
part is how you can specify multiple arguments to be converted into an array. There's no need for an anonymous type at all. if you really wanted an anonymous type, you could access its members by reflection - but that would be pretty ugly and I suspect you'd need to cast each lambda expression too (as otherwise the compiler wouldn't be able to infer its type).
params部分是如何指定要转换为数组的多个参数。根本不需要匿名类型。如果你真的想要匿名类型,你可以通过反射来访问它的成员——但是那样会很难看,我怀疑你也需要对每个lambda表达式进行强制转换(否则编译器将无法推断出它的类型)。
#2
2
Your method's signature is written to accept a sequence of property selectors, not an anonymous type containing two properties, each of which are property selectors or a property selector that is selecting out an anonymous object containing several properties of your object.
方法的签名被编写为接受一个属性选择器序列,而不是包含两个属性的匿名类型,每个属性都是属性选择器或属性选择器,选择一个包含对象的多个属性的匿名对象。
The syntax is similar enough though; create an implicitly typed array rather than an anonymous object:
语法也很相似;创建隐式类型化数组而不是匿名对象:
_repository.Update<File>(file, new[] { x => x.Data, x => x.Name });
If you want to be able to specify each of the lambdas as separate parameters then you'd need to alter your method to use params
for that argument:
如果你想要将每一个lambdas指定为独立的参数,那么你需要改变你的方法来使用参数params:
public void Update<T>(T entity, params Expression<Func<T, Object>>[] properties)
After that change the following call would work:
在此更改之后,下面的调用将会生效:
_repository.Update<File>(file, x => x.Data, x => x.Name);
To allow your solution for a single selector that selects out an anonymous type using all of the members to work we'll have a bit more work ahead of us. To do this we'll need to create an expression visitor that looks through an entire expression for member accesses, pulls out the ones that are accessing members of the parameter (because those are what we care about) and stores all of them together. We can inherit from ExpressionVisitor
to do this reasonably easily, but it's generally worth it to create an extension method to improve the syntax for utilizing it.
为了让您的解决方案选择一个使用所有成员的匿名类型的选择器,我们将在我们前面做更多的工作。为此,我们需要创建一个表达式访问器,该访问器通过一个完整的表达式来查找成员访问,取出访问参数成员的表达式(因为这些是我们关心的),并将它们存储在一起。我们可以从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;
}
We can now call this method on the selectors in your method to pull out the member accesses that we care about. In addition to traversing the entire tree and pulling out all member accesses if there are several, it also doesn't break and throw an exception if someone creates a selector that is anything other than just a member access, as your code currently does (making it somewhat fragile).
现在,我们可以在您的方法中调用该方法来提取我们所关心的成员访问。除了遍历整个树并在有多个成员访问时取出所有成员访问之外,如果有人创建了一个除成员访问之外的任何选择器(如您的代码当前所做的那样),它也不会中断并抛出异常。
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;
}
#3
1
For this one,
为了这一个,
_repository.Update(file, x => new { x.Data, x.Name });
I could think of
我能想到的
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;
}
This should do the trick, but it won't be as fast as an Expression<Func<T, Object>>
solution.
这应该可以实现,但是它不会像表达式
And there is a possibility to "trick" the method with
而且有可能“欺骗”这个方法
_repository.Update(file, x => new { DisplayName = x.Name });
Another one:
另一个:
_repository.Update(file, new { file.Data, file.Name });
the method:
方法:
public void Update<T>(T entity, object modify)
{
foreach (PropertyInfo p in modify.GetType().GetProperties())
_context.Entry<T>(entity).Property(p.Name).IsModified = true;
}