I would like to pass an expression that represents a variable to used when instantiating an object.
Instead of:
class MyObject : IMyInterface { ... }
var list = db.MyObjects.Where(x => !x.IsDeleted).ToList();
var anotherList = list.Select(x => new AnotherObject() {
Id = x.Id,
Value = x.Value
});
I would like to make this so that a list of objects of IMyInterface can be transformed into another type of list (AnotherObject as example) using defined expressions as so:
var list = db.MyObjects
.Where(x => !x.IsDeleted)
.ToAnotherObjectList(x => x.Id, x => x.Value);
...
public static List<AnotherObject> ToAnotherObjectList<T>(
this IEnumerable<IMyInterface> list,
Expression id,
Expression value)
{
return list.Select(x => new AnotherObject() { Id = id, Value = value }).ToList();
}
I'm not sure how to accomplish this. I know I can use reflection to create objects and set properties by a string but I'm not sure how to pass expressions.
UPDATE
Well, I thought I'd have to do some reflection but it's simpler than what I was thinking. Here's my solution that works in IRL.
public static IEnumerable<AnotherObject> ToAnotherObject<T>(this IEnumerable<T> list, Func<T, int> getId, Func<T, string> getValue, Func<T, bool> getSelected = null) where T : IMyInterface
{
return list.Select(x => new AnotherObject {
Display = getValue(x),
Id = getId(x),
Selected = getSelected != null && getSelected(x),
});
}
You could use a Func<TInput,TReturn> for that. For example:
public static List<AnotherObject> ToAnotherObjectList<T>(
this IEnumerable<T> list,
Func<T, int> getId,
Func<T, object> getValue)
{
return list.Select(x => new AnotherObject() { Id = getId(x), Value = getValue(x) }).ToList();
}
Call:
list.ToAnotherObjectList(i => i.Id, i=> i.Value);
In this example I used Funcs with one parameter (of type T) and return type int/object.
Related
Hello I want to sort an ObservableCollection but I cant get access to its properties.
public static class CommonMethods<T>
{
public static ObservableCollection<T> Sort(ObservableCollection<T> array, string columnName, bool sort)
{
ObservableCollection<T> res = new ObservableCollection<T>();
res = res.OrderBy(r => r[""]) // This gives an error says cannot apply indexing with [] to an expression of type T.
return res;
}
}
The Calling code.
mlRegionDetails = CommonMethods<MLRegion>.Sort(mlRegionDetails, columnName, sort);
Please do tell me where I m going wrong.
Instead of passing the name of the property you want to order by, pass the selector function:
public static ObservableCollection<T> Sort(ObservableCollection<T> array, Func<T, object> columnSelector, bool sort)
{
ObservableCollection<T> res = new ObservableCollection<T>(array.OrderBy(columnSelector));
return res;
}
And call it using this:
mlRegionDetails = CommonMethods<MLRegion>.Sort(mlRegionDetails, x => x.SomeColumn, sort);
If you want to do this using strings, you will need to build the expression manually.
Note: the following code has not been tested as it was written here, it may need some change.
public static class CommonMethods<T>
{
private static readonly MethodInfo orderByMethod =
typeof(Enumerable).GetMethods().Single(method =>
method.Name == nameof(Enumerable.OrderBy) && method.GetParameters().Length == 2);
public static ObservableCollection<T> Sort(ObservableCollection<T> array, string columnName, bool sort)
{
var tType = typeof(T);
var parameter = Expression.Parameter(tType);
Expression member = Expression.Property(parameter, columnName);
var lambda = Expression.Lambda(member, paramter);
var genericMethod = orderByMethod.MakeGenericMethod(tType, member.Type);
var orderedData = genericMethod.Invoke(null, new object[] { array, lambda }) as IEnumerable<T>;
return new ObservableCollection<T>(orderedData);
}
}
At last i found an alternate solution to the problem. I create a temp var then convert that ObservableCollection to AsEnumerable and ordering it by the Key passed to it.
Creating key part credit goes to #Camilo Terevinto
public static class CommonMethods
{
public static ObservableCollection<T> sort<T>(ObservableCollection<T> array, Func<T, object> key)
{
var res = array.AsEnumerable().OrderByDescending(key); ;
ObservableCollection<T> temp = new ObservableCollection<T>(res);
return temp;
}
}
I am trying to make a generic to be used on the Selectors components. It should provide a default pattern result which is based on a type we have made called SelectorViewModel that has an Id, a Description and a Code. Today we have a method that does it using the following query:
var result = Queryable.Where(x => .... )
.OrderBy(x => ... )
.Select(x => SelectorViewModel(x.Id,
x.Name,
x.Code))
.ToList();
It works fine, but we will have a lot of these methods. The question is, how to make the fields defined on the Select method to be possible to pass as parameter to the SelectorViewModel? For sample:
public IEnumerable<SelectorViewModel> Selector<T>(.. idExpression, .. descriptionExpression, .. codeExpression)
{
var result = session.Query<T>
.Where(x => .... )
.OrderBy(x => ... )
.Select(x => SelectorViewModel(idExpression, // id
descriptionExpression, // description
codeExpression /*code*/))
.ToList();
return result;
}
I would like to have something like this on the use of this method:
var result = repository.Selector(x => x.Id, x => x.Name, x => x.Code);
And make these arguments be merged on the expression on the Select method. Is that possible?
Thank you.
you need something like this:
public IEnumerable<SelectorViewModel> Selector<T>(
Func<T, int> idExpression,
Func<T, string> descriptionExpression,
Func<T, string> codeExpression)
{
var result = session.Query<T>
.Where(x => .... )
.OrderBy(x => ... )
.Select(x => new SelectorViewModel(idExpression(x), descriptionExpression(x), codeExpression(x)))
.ToList();
return result;
}
However I prefer something more like a "Factory method", so that you pass a factory that convert T to SelectorViewModel (something like this):
public class SelectorViewModel
{
public static SelectorViewModel GetSelectorViewModel<T>(T input)
{
//create SelectorViewModel how do you prefer
return new SelectorViewModel(input.Id, input.Description, input.Code);
}
public SelectorViewModel(int id, string description, int code)
{
// TODO
}
}
public IEnumerable<SelectorViewModel> Selector<T>(Func<T, SelectorViewModel> factoryMethod)
{
var result = session.Query<T>
.Where(x => .... )
.OrderBy(x => ... )
.Select(x => factoryMethod(x))
.ToList();
return result;
}
this because (personally) I don't like to pass many functions, maybe complex such as:
x => x.property > 2 ? x.name.ToString() : (x.property < 0 ? x.description : x.description + "a")
and, at the end
the code is less readable
no/low support for debugging
long code line (length)
[ps: what I did above can be done with OOP (https://en.wikipedia.org/wiki/Factory_method_pattern)]
From your samples it's not quite clear what query provider are you targeting (EF or NHibernate), so the following is an universal helper method which composes selector expression using prototype expression and small ExpressionVisitor based parameter replacer:
public static class QueryableExtensions
{
public static IQueryable<SelectorViewModel> ToSelectorViewModel<T>(
this IQueryable<T> source,
Expression<Func<T, long>> idSelector,
Expression<Func<T, string>> descriptionSelector,
Expression<Func<T, string>> codeSelector
)
{
Expression<Func<long, string, string, SelectorViewModel>> prototype =
(id, description, code) => new SelectorViewModel { Id = id, Description = description, Code = code };
var parameter = Expression.Parameter(typeof(T), "e");
var body = prototype.Body
.ReplaceParameter(prototype.Parameters[0], idSelector.Body.ReplaceParameter(idSelector.Parameters[0], parameter))
.ReplaceParameter(prototype.Parameters[1], descriptionSelector.Body.ReplaceParameter(descriptionSelector.Parameters[0], parameter))
.ReplaceParameter(prototype.Parameters[2], codeSelector.Body.ReplaceParameter(codeSelector.Parameters[0], parameter));
var selector = Expression.Lambda<Func<T, SelectorViewModel>>(body, parameter);
return source.Select(selector);
}
}
The expression helper used:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
Now assuming your method receives the same idSelector, descriptionSelector and codeSelector arguments as above, the usage would be:
var result = Queryable.Where(x => .... )
.OrderBy(x => ... )
.ToSelectorViewModel(idSelector, descriptionSelector, codeSelector)
.ToList();
I am trying to move some code that I wrote to a more generic method. While the method is longer, the part I am having trouble with is the following :
public static void Test()
{
MyObjectType[] list1 = ListMyObjectTypeMethod1();
MyObjectType[] list2 = ListMyObjectTypeMethod2();
List<MyObjectType> linqAblelist1 = new List<MyObjectType>(list1);
List<MyObjectType> linqAblelist2 = new List<MyObjectType>(list2);
IEnumerable<MyObjectType> toBeAdded = linqAblelist1.Where(x => linqAblelist2.All(y => y.Property1 != x.Property1));
IEnumerable<MyObjectType> toBeDeleted = linqAblelist2.Where(a => linqAblelist1.All(b => b.Property1 != a.Property1));
}
And I am trying to pass in a generic type for MyObjectType, but where I have [How To Set Property Here?] how does one specify that in a parameter for the method?
public static void Test<T>(T[] x, T[] y)
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => y.[How To Set Property Here?] != x.[How To Set Property Here?]));
IEnumerable<T> toBeDeleted = list2.Where(a => list1.All(b => b.[How To Set Property Here?])); != a.[How To Set Property Here?]));));
}
Pass in the selection of the property as a Func<T, TProperty>:
public static void Test<T, TProperty>(T[] x, T[] y, Func<T, TProperty> propertySelector)
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => !propertySelector(y).Equals(propertySelector(x))));
IEnumerable<T> toBeDeleted = list2.Where(a => !list1.All(b => propertySelector(b).Equals(propertySelector(a))));
}
Then you can call it by specifying a lambda expression for propertySelector:
Test(someArray, someOtherArray, t => t.SomeProperty);
The best option is to introduce a generic type constraint that will make sure T either inherits from a specific class or implements an interface. In either case the class or interface have to declare Property1. E.g. like this:
public static void Test<T>(T[] x, T[] y) where T : IHasProperty1
{
…
}
You need to put some constraints on your generic type.
public static void Test<T>(T[] x, T[] y) where T : <SomeInterface>
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => y.PropertyName != x.PropertyName));
IEnumerable<T> toBeDeleted = list2.Where(a => list1.All(b => b.PropertyName)); != a.PropertyName));));
}
You cann add a generic constraint that will ensure that T will have the properties you're expecting. Some thing like:
public static void Test<T>(T[] x, T[] y) where T : MyObjectType
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => y.Property1 != x.Property1 ));
IEnumerable<T> toBeDeleted = list2.Where(a => list1.All(b => b.Property1 )); != a.[How To Set Property Here?]));));
}
I have an IEnumberable> and I want only the list of Keys but cast to the needed type (i.e. perhaps short and not int). This is used in a custom generic multi-select control the binds to but the database needs potientially 'short' to save.
public static IEnumerable<T> GetKeysOnly<T>(this IEnumerable<KeyValuePair<int, string>> values)
{
Dictionary<int, string> valuesDictionary = values.ToDictionary(i => i.Key, i => i.Value);
List<int> keyList = new List<int>(valuesDictionary.Keys);
// Returns 0 records cuz nothing matches
//List<T> results = keyList.OfType<T>().ToList();
// Throws exception cuz unable to cast any items
//List<T> results = keyList.Cast<T>().ToList();
// Doesn't compile - can't convert int to T here: (T)i
//List<T> results = keyList.ConvertAll<T>(delegate(int i) { return (T)i; });
throw new NotImplementedException();
}
public static IEnumerable<short> GetKeysOnly(this IEnumerable<KeyValuePair<int, string>> values)
{
Dictionary<int, string> valuesDictionary = values.ToDictionary(i => i.Key, i => i.Value);
List<int> keyList = new List<int>(valuesDictionary.Keys);
// Works but not flexable and requires extension method for each type
List<short> results = keyList.ConvertAll(i => (short)i);
return results;
}
Any advice how to make my generic extension method work?
Thanks!
You want to get only the keys converted to a short?
var myList = valuesDictionary.Select(x => (short)x.Key).ToList();
// A Dictionary can be enumerated like a List<KeyValuePair<TKey, TValue>>
If you want to go to any type, then you would do something like this:
public static IEnumerable<T> ConvertKeysTo<T>(this IEnumerable<KeyValuePair<int, string>> source)
{
return source.Select(x => (T)Convert.ChangeType(x.Key, typeof(T)));
// Will throw an exception if x.Key cannot be converted to typeof(T)!
}
I dont know how to get rid of this error ?
Error 1 Using the generic type 'System.Collections.Generic.IEnumerable' requires 1 type arguments C:\Users\huzaifa.gain\documents\visual studio 2010\Projects\VendInvoiceImport\VendInvoiceImport\Program.cs 34 24 VendInvoiceImport
private static IEnumerable<string , string > DistinctInvoiceNumber(DataTable VendorInvoiceStagingTable)
{
var InvoiceLinecollection = VendorInvoiceStagingTable.AsEnumerable().Select(t => new { number = t.Field<string>(VendInvoice.Number),LineNumber = t.Field<string>(VendInvoice.LineNumber)}).Distinct();
return InvoiceLinecollection;
}
Your Linq query returns an sequence of anonymous type, but methods can't return anonymous types. You have several options:
return an IEnumerable<Tuple<string, string>>
private static IEnumerable<Tuple<string, string>> DistinctInvoiceNumber(DataTable VendorInvoiceStagingTable)
{
var InvoiceLinecollection = VendorInvoiceStagingTable
.AsEnumerable()
.Select(t => Tuple.Create(t.Field<string>(VendInvoice.Number), t.Field<string>(VendInvoice.LineNumber)))
.Distinct();
return InvoiceLinecollection;
}
return a IDictionary<string, string> as suggested in another answer (assuming your query doesn't return duplicate keys)
private static IDictionary<string, string> DistinctInvoiceNumber(DataTable VendorInvoiceStagingTable)
{
var InvoiceLinecollection = VendorInvoiceStagingTable
.AsEnumerable()
.Select(t => Tuple.Create(t.Field<string>(VendInvoice.Number), t.Field<string>(VendInvoice.LineNumber)))
.Distinct()
.ToDictionary(t => t.Item1, t => t.Item2);
return InvoiceLinecollection;
}
create a class for this purpose with 2 string properties and return a sequence of that class
private static IEnumerable<InvoiceLine> DistinctInvoiceNumber(DataTable VendorInvoiceStagingTable)
{
var InvoiceLinecollection = VendorInvoiceStagingTable
.AsEnumerable()
.Select(t => new InvoiceLine(t.Field<string>(VendInvoice.Number), t.Field<string>(VendInvoice.LineNumber)))
.Distinct();
return InvoiceLinecollection;
}
Why you do not use Dictionary<string, string> instead?
private static Dictionary<string , string > DistinctInvoiceNumber(DataTable VendorInvoiceStagingTable)
{
var InvoiceLinecollection = VendorInvoiceStagingTable.AsEnumerable().Select(t => new { number = t.Field<string>(VendInvoice.Number),LineNumber =
t.Field<string>(VendInvoice.LineNumber)}).ToDictionary();
return InvoiceLinecollection;
}