Working with EF6, in a generic method, I am trying to set some property values like so:
protected void MyFunc<T>(T entity, params Expression<Func<T, dynamic>>[] properiesToCopy) where T: class
{
foreach (var propertyEntry in properiesToCopy)
{
context.Entry(_entityCopy).Property(propertyEntry ).CurrentValue = context.Entry(entity).Property(propertyEntry).CurrentValue;
}
}
Now, I would like to use the same function for reference types of the entity, but then I would have to use DbEntityEntry.Reference function.
How can I know whether the lambda expression (named propertyEntry above) referes to a DbPropertyEntry or to a DbReferenceEntry? Or can my function perform the same action in a better way?
I found the answer, I just had to use DbEntityEntry.Member function, which will work for scalar types, reference types and even collections. However, this only works with a string parameter: so I took the method TryParsePath from the entity framework source code that converts a Lambda path to a string path.
Then I could do:
string path;
foreach (var memberEntry in membersToCopy)
{
if (!Utils.TryParsePath(memberEntry .Body, out path))
{
throw new ArgumentException("Include path not valid", "path");
}
context.Entry(_entityCopy).Member(path).CurrentValue = context.Entry(entity).Member(path).CurrentValue;
}
Related
I have a dictionary where the key is a string and the value is an Action. I setup mapping of string keys to functions. I then have a list that I call .ForEach() on and inside this I use a value in the list as the key and then call the function associated with it. The issue will come if the key doesn't exist. How can I check this in the .ForEach function? I'd like to use the ternary operator but it doesn't seem to work.
private Dictionary<string, Func<IniConfig, object>> typeMapping = new Dictionary<string, Func<IniConfig, object>>();
typeMapping["MB"] = ProcessMB;
public object ProcessMB(IniConfig file)
{
return null;
}
object iif(bool expression, object truePart, object falsePart)
{ return expression ? truePart : falsePart; }
FilesToProcess.ForEach(x => iif(typeMapping.ContainsKey(x.ExtractType), typeMapping[x.ExtractType](x), Error(x)));
This highlights the 2nd parameter in the iif() call with error: Argument 2: cannot convert from 'void' to 'object', and the same thing for the Error(x) part.
So it seems like the ternary operator returns an object? So if I change my Action to a Func and have the return type be object and make my functions return object it compiles, but when I run a test with an invalid key it doesn't go into my Error() function it throws "The given key was not present in the dictionary" exception. I would expect it to do the ternary operation and see that typeMapping doesn't have the key and so fail to the Error() function. What am I missing?
Your iif() method is counter-intuitive here, and it seems like an over-use of LINQ. Here is what I would do:
foreach (var file in filesToProcess)
{
Func<IniConfig, object> action;
if (typeMapping.TryGetValue(file.ExtractType, out action))
{
action(file);
}
else
{
Error(file);
}
}
Makes your code easier to read too :)
Edit:
I Have found a generic way to do what you want. First you need to create an extension method for IDictionary<TKey,TVal> that simply returns the default value if a key does not exist:
public static TVal GetValueOrDefault<TKey, TVal>(this IDictionary<TKey, TVal> self, TKey key)
{
TVal ret;
self.TryGetValue(key, out ret);
return ret;
}
Then you can use the null coalescing (??) operator:
FilesToProcess.ForEach((typeMapping.GetValueOrDefault(x.ExtractType) ?? Error)(x));
Make your life easy and just create a little helper function that does what you want:
private void InvokeProcessor(IniConfig iniConfig)
{
Func<IniConfig, object> processor;
if (typeMapping.TryGetValue(x.ExtractType, out processor)
processor(iniConfig);
else
Error(iniConfig);
}
Then use it like so:
FilesToProcess.ForEach(InvokeProcessor);
Easy to read and understand. Also faster because the dictionary will only be searched once for each file.
Edit
Alright, if you really wanna use that weird iif() thing, here's how to fix it:
object iif(bool expression, Func<object> truePart, Func<object> falsePart)
{ return expression ? truePart() : falsePart(); }
...and how to use it:
FilesToProcess.ForEach(x => iif(
typeMapping.ContainsKey(x.ExtractType),
() => typeMapping[x.ExtractType](x),
() => Error(x)));
Basically the evaluation of the 2nd and 3rd argument of iif() is delayed until one of them is actually needed. In your previous version all arguments were evaluated before the execution of iff() resulting in a call to the dictionary indexer and to Error() every time a IniConfig object was processed.
A word of warning though: if you use this in production then harakiri might be your only way out of the devastating shame that will overcome you a couple years later.
I am working on a project to filter a list in a generic way. I am retrieving an IEnumerable<T> at runtime but I don't know what is T. I need to cast the list I am retrieving to IEnumerable<T> and not IEnumerable, because I need extension methods like ToList and Where. Here is my code.
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
// Here I want to get the list property to update
// The List is of List<Model1>, but of course I don't know that at runtime
// casting it to IEnumerable<object> would give Invalid Cast Exception
var listToModify = (IEnumerable<object>)propertyListInfoToUpdate.GetValue(model);
foreach (var condition in conditions)
{
// Filter is an extension method defined below
listToModify = listToModify.Filter(condition .Key, condition .Value);
}
// ToList can only be invoked on IEnumerable<T>
return listToModify.ToList();
}
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, string propertyName, object value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(parameter, propertyName);
var propertyType = ((PropertyInfo)property.Member).PropertyType;
Expression constant = Expression.Constant(value);
if (((ConstantExpression)constant).Type != propertyType)
{ constant = Expression.Convert(constant, propertyType); }
var equality = Expression.Equal(property, constant);
var predicate = Expression.Lambda<Func<T, bool>>(equality, parameter);
var compiled = predicate.Compile();
// Where can only be invoked on IEnumerable<T>
return source.Where(compiled);
}
Also please note that I cannot retrieve the List like this
((IEnumerable)propertyListInfoToUpdate.GetValue(model)).Cast<object>()
since it will generate the below exception in the Filter extention
ParameterExpression of type 'Model1' cannot be used for delegate parameter of type 'System.Object'
Use GetGenericArguments and MakeGenericMethod to interface generic signatures.
private IList<object> UpdateList(KeyValuePair<string, string> conditions)
{
var rawList = (IEnumerable)propertyListInfoToUpdate.GetValue(model);
var listItemType = propertyListInfoToUpdate.PropertyType.GetGenericArguments().First();
var filterMethod = this.GetType().GetMethod("Filter").MakeGenericMethod(genericType);
object listToModify = rawList;
foreach (var condition in conditions)
{
listToModify = filterMethod.Invoke(null, new object[] { listToModify, condition.Key, condition.Value })
}
return ((IEnumerable)listToModify).Cast<object>().ToList();
}
Assuming your propertyListInfoToUpdate is a PropertyInfo and the property type is List<T>.
Why are you using Expression at all? It's hard to understand your question without a good Minimal, Complete, and Verifiable code example. Even if we could solve the casting question, you're still returning IList<object>. It's not like the consumer of the code will benefit from the casting.
And it's not really possible to solve the casting problem, at least not in the way you seem to want. A different approach would be to call the Filter() dynamically. In the olden days, we'd have to do this by using reflection, but the dynamic type gives us runtime support. You could get it to work something like this:
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
dynamic listToModify = propertyListInfoToUpdate.GetValue(model);
foreach (var condition in conditions)
{
// Filter is an extension method defined below
listToModify = Filter(listToModify, condition.Key, condition.Value);
}
// ToList can only be invoked on IEnumerable<T>
return ((IEnumerable<object>)Enumerable.Cast<object>(listToModify)).ToList();
}
NOTE: your original code isn't valid; I made the assumption that conditions is supposed to be an array, but of course if you change it to anything that has a GetEnumerator() method, that would be fine.
All that said, it seems to me that given the lack of a compile-time type parameter, it would be more direct to just change your Filter() method so that it's not generic, and so that you use object.Equals() to compare the property value to the condition value. You seem to be jumping through a lot of hoops to use generics, without gaining any of the compile-time benefit of generics.
Note that if all this was about was executing LINQ query methods, that could be addressed easily simply by using Enumerable.Cast<object>() and using object.Equals() directly. It's the fact that you want to use expressions to access the property value (a reasonable goal) that is complicating the issue. But even there, you can stick with IEnumerable<object> and just build the object.Equals() into your expression.
Creating an expression and compiling it every time is very expensive. You should either use Reflection directly or a library like FastMember (or cache the expressions). In addition, your code uses Expression.Equal which translates into the equality operator (==), which is not a good way of comparing objects. You should be using Object.Equals.
Here is the code using FastMember:
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
var listToModify = ((IEnumerable)propertyListInfoToUpdate.GetValue(model)).Cast<object>();
foreach (var condition in conditions)
{
listToModify = listToModify.Where(o => Equals(ObjectAccessor.Create(o)[condition.Key], condition.Value));
}
return listToModify.ToList();
}
Side note - your Filter method doesn't really need generics. It can be changed to accept and return an IEnumerable<object> by tweaking the expression, which also would've solved your problem.
Is it possible to complete this method? Is it possible in the latest version of C#? Thinking about this as a DSL to configure a system for watching for certain property changes on certain objects.
List<string> list = GetProps<AccountOwner>(x => new object[] {x.AccountOwnerName, x.AccountOwnerNumber});
// would return "AccountOwnerName" and "AccountOwnerNumber"
public List<string> GetProps<T>(Expression<Func<T, object[]>> exp)
{
// code here
}
In C# 6, you'd use:
List<string> list = new List<string>
{
nameof(AccountOwner.AccountOwnerName),
nameof(AccountOwner.AccountOwnerNumber)
};
Before that, you could certainly break the expression tree apart - the easiest way of working out how is probably to either use an expression tree visualizer, or use the code you've got and put a break point in the method (just make it return null for now) and examine the expression tree in the debugger. I'm sure it won't be very complicated - just a bit more than normal due to the array.
You could possibly simplify it using an anonymous type, if you use:
List<string> list = Properties<AccountOwner>.GetNames(x => new {x.AccountOwnerName, x.AccountOwnerNumber});
Then you could have:
public static class Properties<TSource>
{
public static List<string> GetNames<TResult>(Func<TSource, TResult> ignored)
{
// Use normal reflection to get the properties
}
}
If you don't care about the ordering, you could just use
return typeof(TResult).GetProperties().Select(p => p.Name).ToList();
If you do care about the ordering, you'd need to look at the names the C# compiler gives to the constructor parameters instead - it's a bit ugly. Note that we don't need an expression tree though - we only need the property names from the anonymous type. (An expression tree would work just as well, admittedly.)
Without c# 6 and nameof, you could get a property name from a expression tree like:
using System.Linq.Expressions;
//...
static string GetNameOf<T>(Expression<Func<T>> property)
{
return (property.Body as MemberExpression).Member.Name;
}
Using it like:
GetNameOf(() => myObject.Property);
Not directly usable for an array of objects, but you could make an overload to take an array of expressions... something like:
static string[] GetNameOf(IEnumerable<Expression<Func<object>>> properties)
{
return properties.Select(GetNameOf).ToArray();
}
And use it like
GetNameOf(
new Expression<Func<object>>[]
{
() => x.AccountOwnerName,
() => x.AccountOwnerNumber
}
);
Demonstrating fiddle: https://dotnetfiddle.net/GsV96t
Update
If you go this route, the original GetNameOf for a single property won't work for value types (since they get boxed to object in the Expression and now the expression uses Convert internally). This is easily solvable by changing the code to something like:
static string GetNameOf<T>(Expression<Func<T>> property)
{
var unary = property.Body as UnaryExpression;
if (unary != null)
return (unary.Operand as MemberExpression).Member.Name;
return (property.Body as MemberExpression).Member.Name;
}
Updated fiddle: https://dotnetfiddle.net/ToXRuu
Note: in this updated fiddle I've also updated the overloaded method to return a List instead of an array, since that's what was on your original code
The code below is valid:
IEnumerable<SomeThing> things = ...;
// map type SomeThing to a new anonymous type, resulting in a strongly typed
// sequence based on an anon type
var newList = things.Select(item =>
{
return new
{
ID = item.ID,
DateUpdatedOrCreated = ((DateTime)(item.DateUpdated ??
item.DateCreated)).ToShortDateString(),
Total = item.Part1 + item.Part2
};
});
newList now appears in Visual Studio as IEnumerable<'a> and is strongly typed with the anonymous type created in the function. That is so cool.
What I can't seem to do is figure out a way to assign just the lambda expression (and not the enumeration) to an implicitly typed variable. Even though the compiler has no problem with the anonymous type in the context above, if I try (say)
var func = (SomeThing item)=> {
return new { ... };
};
I get the error "Cannot assign lambda expression to implicitly-typed local variable". This seems a strange compiler limitation; unless I am missing something, the types are just as non-ambiguous in the 2nd example as they are in the first first: both type parameters are well defined.
Is there any way to do this? Since it's an anonymous type, of course, I don't have any way to use a type to assign it explicitly, so it seems I'd be stuck with making a class for the output type if not.
Update
Shortly after going about my merry way with Jon Skeet's answer, I found a similar dilemma instantiating classes. In case it's not obvious, the same trick can be used to create strongly typed classes using inferred anonymous types.
class Processor<T,U>
{
public Processor(Func<T,U> func) {
}
}
// func is a delegate with anon return type created using method in answer below
var instance = new Processor(func); // does not compile! Requires type arguments!
cannot be created directly, but can be created in much the same way as the trick below:
public static Processor<T,U> Create<T,U>(Func<T,U> func) {
return new Processor<T,U>(func);
}
var instance = Processor.Create(func); // all good
You can do it via type inference:
var func = BuildFunc((SomeThing item) => {
return new { ... };
});
...
static Func<TSource, TResult> BuildFunc<TSource, TResult>(
Func<TSource, TResult> function) {
return function;
}
Note that BuildFunc doesn't really do anything - it just provides the method call needed to get the compiler to do type inference for the generic type arguments for Func<,> - it adds the information that you're interested in Func<,>, basically - that's information which can't be specified as part of a variable declaration, without also specifying the type arguments.
I have a class that builds a url with query string parameters and so on. The class has a method: Url() which returns the complete url composed from the class properties and another method: UrlNew() which allows passing a predicate as parameter for the replacement of the value of one of the properties and THEN Returns the modified URL. Now, I need to modify this function to use TWO parameters, both predicates. How do I do that? I tried modifying the method's parameters as a List of predicates but I probably am not doing something right:
My OLD UrlNew() method looked like this:
public static string Url() (Action<LGUrlBuilder> predicate)
{
var instance = new LGUrlBuilder();
if (predicate != null) predicate(instance);
return instance.BuildUrl();
}
My NEW UrlNew() method looks like this:
public static string UrlNew(List<Action<LGUrlBuilder>> predicateList)
{
var instance = new LGUrlBuilder();
if (predicateList != null && predicateList.Count > 0)
{
foreach (Action<LGUrlBuilder> predicate in predicateList)
{
if (predicate != null) predicate(instance);
}
}
return instance.BuildUrl();
}
This compiles just fine but when I run it, using it in ASPX gives me this error:
CS1660: Cannot convert lambda expression to type 'System.Collections.Generic.List<System.Action<public_site.Library.LG.LGUrlBuilder>>' because it is not a delegate type
I am a C# beginner and I am sure I am doing something completely wrong. Any advice would help. Thanks!
Don't modify the function itself. Modify the method call like this:
UrlNew(x => { func1(x); func2(x); });
But if you really want to take arbitrary number of delegate instances as arguments, try modifying it like:
public static void UrlNew(params Action<LGUrlBuilder>[] list) {
// ... do what you're already doing in the second snippet ...
}
You can call it like:
UrlNew(x => firstthing(), x => secondthing(x), thirdthing);
Side note: An Action<T> is not called a predicate. A predicate returns a boolean value.
How about using the overloaded Action<T1,T2> delegate which accepts 2 parameters.
If you are looking to use a predicate instead which expects a boolean return value then use Func<T1,T2,bool> instead.