I have this LINQ query where the "new" sentence creates an anonymous type:
var query = from x in List
select new {x.Field1, x.Field2};
How to build dynamically the LINQ Expression for:
select new {x.Field1, x.Field2};
MORE CONTEXT:
We have a List<Customer> where Customer has properties A, B, C, D, E, F, G.
We need to return only some properties sometimes:
First call returns:
select new {x.A, x.B}
Second call returns:
select new {x.C, x.D}
The return is dynamic! I have the names of the properties in string array.
Thanks!
Assuming a CSV for the properties that you want to retrieve. You should be able to use this.
public static class ObjectExtensions
{
public static Dictionary<string,object> GetProperties(this object obj, string properties)
{
var propertyNames = properties.Split(',');
var result = new Dictionary<string, object>();
var type = obj.GetType();
foreach (var property in propertyNames)
{
var prop = type.GetProperty(property);
var value = prop.GetValue(obj);
result.Add(property, value);
}
return result;
}
}
It stores the key value pair in a dictionary
The usage would be.
var result = list.Select(p => p.GetProperties("Field1,Field2"));
JsonSerializer.CreateDefault().Serialize(Console.Out, result);
Related
I have a Generic function that contains an IQueryable<T> where each row contains an instance of a class with a set of properties.
I have another class (MyClass) that has some of the same properties as the class T above... i.e. same name and datatypes.
I also have a List of Strings containing the Property Names shared between the two classes.
I want to be able to create a new IQueryable<myClass> where the myClass instances properties are populated with the name-sake proprties in the original IQueryable<T>
Does that make sense? Please let me know if I can supply any more info or make anything more clear.
EDIT
I will try an add some code to illustrate better. I know there are countless faults here including Adding to an IQueryable - but this is for illustration:
IQueryable<T> qry = this.GetSomeDataIntoIQueryable();
// Just getting a list of the Shared Property Names between the two classes
List<string> sharedProprtyNames = new List<string>();
foreach (var item in ListofSharedPropertyNames)
{
sharedProprtyNames .Add(item.SharedPropertyName);
}
IQueryable<myClass> myClassIQ;
foreach(var classItem in qry)
{
myClass x = new myClass();
foreach(var sharedProperty in sharedProprtyNames )
{
myClass[sharedProperty] = classItem[sharedProperty];
}
myClassIQ.Add(myClass);
}
Something like:
static IQueryable<TTo> Select<TFrom, TTo>(
this IQueryable<TFrom> source,
params string[] members)
{
var p = Expression.Parameter(typeof(TFrom));
var body = Expression.MemberInit(
Expression.New(typeof(TTo)),
members.Select(member => Expression.Bind(
typeof(TTo).GetMember(member).Single(),
Expression.PropertyOrField(p, member))));
var lambda = Expression.Lambda<Func<TFrom, TTo>>(body, p);
return source.Select(lambda);
}
?
This creates an IQueryable<T>-friendly projection from TFrom to TTo, respecting all the members from members.
In your example, it would be:
IQueryable<myClass> myClassIQ = qry.Select<T, myClass>(ListofSharedPropertyNames);
(adjust between array / list for the members parameter to suit your convenience - because we use Select, it'll work with either)
Using System.Linq.Dynamic.Core you can:
IQueryable<Table1> query1 = ...
var res = query.Select<SubTable1>("new(" + string.Join(",", new[] { "Col1", "Col2" }) + ")").ToArray();
Where query1 is your query, Table1 is the T of your question, SubTable1 is MyClass, "Col1", "Col2" are the columsn that must be selected.
You could do everything without usig the System.Linq.Dynamic.Core library and simply building an expression tree... But it is a pain :-)
My variant of the code of Hans Passant:
public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, IEnumerable<string> columns)
{
// the x in x => ...
var par = Expression.Parameter(typeof(TSource), "x");
// "Bindings" (the Col1 = x.Col1 inside the x => new { Col1 = x.Col1 })
var binds = columns.Select(x => Expression.Bind((MemberInfo)typeof(TResult).GetProperty(x) ?? typeof(TResult).GetField(x), Expression.PropertyOrField(par, x)));
// new TResult
var new1 = Expression.New(typeof(TResult));
// new TResult { Bindings }
var mi = Expression.MemberInit(new1, binds);
// x => new TResult { Bindings }
var lambda = Expression.Lambda<Func<TSource, TResult>>(mi, par);
// Select(x => new TResult { Bindings })
return source.Select(lambda);
}
(nearly totally equivalent... Only difference is that he uses GetMember() while I used GetProperty() + GetField())
I am trying to have an orderByExpression to passed to a function which uses it on the dbContext object in the entity framework
> Dictionary<int, string> dict = new Dictionary<int, string>();
> Expression<Func<DbQuestion, string>> orderByExpression = r => dict[r.Id];
This throws the following error.
Exception=Exceptions.DBConnectionException: LINQ
to Entities does not recognize the method 'System.String
get_Item(Int32)' method, and this method cannot be translated into a
store expression.
I understand that I cannot access the [ ] in Linq to Sql queries. Can you please suggest an alternative or point me in the right direction?
Edit:
Few more details about the problem I am trying to solve:
The dict have 15-20 items and keys remain constant and only values change dynamically
I am trying to order the questions based on the range of r.Id i.e if floor(r.Id)==14 then I return "a" if it's between floor(r.Id)==15 then I return "b" and if it's between floor(r.id)==13 I return "c"(as decided by the values in the dict) this helps in ordering the rows
Here's the actual expression:
List<int> cqh;
List<int> iqh;
Expression<Func<DbQuestion, string>> orderByExpression = r => cqh.Contains(r.QuestionID)? dict[(int)Math.Floor(r.SearchKey1)] +"2"+Guid.NewGuid() :
iqh.Contains(r.QuestionID)? dict[(int)Math.Floor(r.SearchKey1)] + "1"+Guid.NewGuid() :
dict[(int)Math.Floor(r.SearchKey1)] + "0"+Guid.NewGuid();
Thanks,
I see two options, that stay with LINQ to Entities
Put the data in the dictionary in a separate table, save it, and then join the table. This solution can apply even if you need to have per query data.
Example:
public class SortKey
{
[Key]
public int SortKeyId { get; set; }
public long SearchId { get; set; }
public int EntityId { get; set; }
public string SortId { get; set; }
}
using (var db = new Db())
{
Dictionary<int, string> dict = new Dictionary<int, string>();
long searchId = DateTime.Now.Ticks; // Simplfied, either use a guid or a FK to another table
db.Keys.AddRange(dict.Select(kv => new SortKey { SearchId = searchId, EntityId = kv.Key, SortId = kv.Value }));
db.SaveChanges();
var query = from e in db.Entity
join k in db.Keys.Where(k => k.SearchId == searchId) on (int)e.Id equals k.EntityId
orderby k.SortId
select e;
}
// Cleanup the sort key table
Build the condition dynamically. This can be achieved using Expression manipulation
Example:
Expression exp = Expression.Constant(""); //Default order key
var p = Expression.Parameter(typeof(Entity));
foreach (var kv in dict)
{
exp = Expression.Condition(
Expression.Equal(
Expression.Convert(
Expression.MakeMemberAccess(p, p.Type.GetProperty("Id")), typeof(int)
),
Expression.Constant(kv.Key)
),
Expression.Constant(kv.Value),
exp
);
}
var orderByExp = Expression.Lambda<Func<Entity, string>>(exp, p);
var query = db.Entity.OrderBy(orderByExp);
Which option you use depend on the amount of data in the dictionary. The condition built for the OrderBy might get very inefficient for a large amount of data
Edit
Based on the changed question, you can use an expression visitor to replace the dic[...] call with a conditional testing for each value in the dictionary. The advantage of this approach is that you can easily change the expression, the replace will work the same way
The class:
class DictionaryReplaceVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if(node.Object != null && node.Object.Type == typeof(Dictionary<int, string>) && node.Method.Name == "get_Item")
{
Expression exp = Expression.Constant(""); //Default order key
// Compile the tahrget of the index and execute it to get the value
// If you know there is a single dictionary you could replace this with a class property intead and set it from the Visit call site,
// but this is the more general appraoch
var dict = Expression.Lambda<Func<Dictionary<int, string>>>(node.Object).Compile()();
foreach (var kv in dict)
{
exp = Expression.Condition(
Expression.Equal(
node.Arguments.Single(),
Expression.Constant(kv.Key)
),
Expression.Constant(kv.Value),
exp
);
}
return exp;
}
return base.VisitMethodCall(node);
}
}
Usage:
Expression<Func<Entity, string>> orderByExpression = r => cqh.Contains(r.QuestionID) ? dict[(int)Math.Floor(r.SearchKey1)] + "2" + Guid.NewGuid() :
iqh.Contains(r.QuestionID) ? dict[(int)Math.Floor(r.SearchKey1)] + "1" + Guid.NewGuid() :
dict[(int)Math.Floor(r.SearchKey1)] + "0" + Guid.NewGuid();
var replace = (Expression<Func<Entity, string>>)new DictionaryReplaceVisitor().Visit(orderByExpression);
var query = db.Entity.OrderBy(replace).ToString();
The resulting SQL will not be pretty but it should work.
Solution 3:
If the amount of data is not very big, you can do a ToList or AsEnumerable on the query and do the ordering in memory (by calling the OrderBy after one of the above methods). It might actually perform better in this case
I have been looking at many posts here and on the web but none of them seem to be helping.
I have a table with about 2 million records, it has over 200 columns.
A simple web service allow the user to pull a specific number of columns out of the table, the user has the option to choose which column to pull.
The result needs to be string of comma separated values, so my query needs to pull the requested columns and return a concatenate string.
I have done this using ADO.NET and pure SQL queries it works fine however I was asked to do it in Entity Framework.
Here is what I have and have done already.
I get the requested columns list as an array of strings.
The following is my query, not sure if it's the best solution or idea hence I'm asking for help here.
var valueList2 = ctx.mytable.Where(x => x.pcds == comValue).Select(x => new{temp = x.column1 +", "+ x.column2}).Select(x => x.temp).ToList();
The above gives me string of two columns separated by commas, I just need to somehow push my array of column names into the lambda part of it.
I did the following but then realised that it only works with a specific type of a class not anonymous, also I can't figure out how I can use it for a multiple columns and not make it so complex.
var createdType = typeof(mytable);
var Param = Expression.Parameter(typeof(string), "pr");
var obj = Expression.New(createdType);
var ValueProperty = createdType.GetProperty("long");
var ValueAssignment = Expression.Bind(ValueProperty, Param);
var memberInit = Expression.MemberInit(obj, ValueAssignment);
var lm = Expression.Lambda<Func<string, mytable>>(memberInit, Param);
Thank you
I'm using Dynamic Linq (source code). Sadly there is little documentation about how to use it :-) In a fun boomerang effect, there is an "evolved" version. The boomerang effect is because the code for generating the dynamic class is based on one of my responses :-) The remaining code seems to be very beautiful... And there is a full suit of unit tests with code samples!!! Note that this second library is a superset of the first library, so you can probably apply many examples to the first one! :-)
I'm adding some static methods to translate the result of a a Dynamic Linq query to a IEnumerable<object[]>.... Example code:
using (var ctx = new Model1())
{
var result = ctx.MyTable
.Take(100)
.SimpleSelect(new[] { "ID", "Col1", "Col2" })
.ToObjectArray();
foreach (var row in result)
{
Console.WriteLine(string.Join(", ", row));
}
}
More complex example:
var columnsNames = new[] { "SomeNullableInt32", "SomeNonNullableDateTimeColumn" };
// One for each column!
var formatters = new Func<object, string>[]
{
x => x != null ? x.ToString() : null,
x => ((DateTime)x).ToShortDateString()
};
var result = ctx.MyTable.Take(100).SimpleSelect(columnsNames).ToObjectArray();
foreach (var row in result)
{
var stringRow = new string[row.Length];
for (int i = 0; i < row.Length; i++)
{
stringRow[i] = formatters[i](row[i]);
}
Console.WriteLine(string.Join(", ", stringRow));
}
And the classes... One (SimpleSelect) produces the Dynamic SQL Select, and "anonymizes" the field names. I do this because for each type of return the Dynamic Linq will generate at runtime a class. This class won't be unloaded until the program ends. By anonymizing the columns (I rename them to Item1, Item2, Item3...) I increase the possibility that the same class will be reused. Note that different type of columns will generate different classes! (int Item1, string Item2 will be a different class from int Item1, DateTime Item2), the other (ToObjectArray) returns a IEnumerable<object[]>, something easier to parse.
public static class DynamicLinqTools
{
private static ConcurrentDictionary<Type, Func<object, object[]>> Converters = new ConcurrentDictionary<Type, Func<object, object[]>>();
public static IQueryable SimpleSelect(this IQueryable query, string[] fields)
{
// With a little luck, "anonymizing" the field names we should
// reduce the number of types created!
// new (field1 as Item1, field2 as Item2)
return query.Select(string.Format("new ({0})", string.Join(", ", fields.Select((x, ix) => string.Format("{0} as Item{1}", x, ix + 1)))));
}
public static IEnumerable<object[]> ToObjectArray(this IQueryable query)
{
Func<object, object[]> converter;
Converters.TryGetValue(query.ElementType, out converter);
if (converter == null)
{
var row = Expression.Parameter(typeof(object), "row");
// ElementType row2;
var row2 = Expression.Variable(query.ElementType, "row2");
// (ElementType)row;
var cast = Expression.Convert(row, query.ElementType);
// row2 = (ElementType)row;
var assign = Expression.Assign(row2, cast);
var properties = query.ElementType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.CanRead && x.GetIndexParameters().Length == 0)
.ToArray();
// (object)row2.Item1, (object)row2.Item2, ...
var properties2 = Array.ConvertAll(properties, x => Expression.Convert(Expression.Property(row2, x), typeof(object)));
// new object[] { row2.Item1, row2.Item2 }
var array = Expression.NewArrayInit(typeof(object), properties2);
// row2 = (ElementType)row; (return) new object[] { row2.Item1, row2.Item2 }
var body = Expression.Block(typeof(object[]), new[] { row2 }, assign, array);
var exp = Expression.Lambda<Func<object, object[]>>(body, row);
converter = exp.Compile();
Converters.TryAdd(query.ElementType, converter);
}
foreach (var row in query)
{
yield return converter(row);
}
}
}
This is a short and easy answer for whoever needs a different answer, but per our discussion with #xanatos, it's not the best as it also returns all the columns which need to be cut off before adding to a list of strings.
List<string> valueList = new List<string>();
using (var ctx = new DataEntities1())
{
var query = ctx.myTable.Where(x => x.pcds == scode).SingleOrDefault();
foreach (var item in columnsArray)
{
valueList.Add(typeof(myTable).GetProperty(onsColumns[Convert.ToInt32(item)]).GetValue(query).ToString());
}
}
I have two lists I would like to compare them for updated/modified columns.
Compare 2 Lists of the same class and show the different values in a new list
I would like to do this using linq. The only problem is I am dealing with a lot of columns, over excess of 30 columns in each. Any suggestions would be of great help...
//In Dal
List<PartnerAndPartnerPositionEntity> GetAllPartnerAndPartnerPositionOldDB(int modelId);
List<PartnerAndPartnerPositionEntity> GetAllPartnerAndPartnerPosition(int modelId);
//BL
//get from new db
var list1= _partnerDAL.GetAllPartnerAndPartnerPosition(modelId);
//get from old db
var list2= _partnerDAL.GetAllPartnerAndPartnerPositionOldDB(modelId);
Let's assume that:
PartnerAndPartnerPositionEntity class contains a property named Id that represents the unique key of an item
Given the above you can:
Get all properties of your type
var properties = typeof(PartnerAndPartnerPositionEntity).GetProperties();
Join the two lists on the Id property and iterate through the properties to see which one has changed:
var list = list1.Join(list2,
x => x.Id,
y => y.Id,
(x, y) => Tuple.Create(x, y))
.ToList();
list.Foreach(tuple =>
{
foreach(var propertyInfo in properties)
{
var value1 = propertyInfo.GetValue(tuple.Item1, null);
var value2 = propertyInfo.GetValue(tuple.Item2, null);
if(value1 != value2)
Console.WriteLine("Item with id {0} has different values for property {1}.",
tuple.Item1,Id, propertyInfo.Name);
}
});
Well, if you want to avoid doing it the boring and tedious way, you need to use reflection to dynamically get the class members then get their values for each instance.
See C# Reflection - Get field values from a simple class for the code.
There...this will generate new IL!
public static class EqualityHelper
{
private ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>();
public bool AreEqual<T>(T left, T right)
{
var equality = (Func<T,T,bool>)_cache.GetOrAdd(typeof(T), CreateEquality<T>());
return equality(left, right);
}
private Func<T, T, bool> CreateEquality<T>()
{
var left = Expression.Parameter(typeof(T));
var right = Expression.Parameter(typeof(T));
var properties = from x in typeof(T).GetProperties()
where x.GetIndexParameters().Any() == false
select x;
var expressions = from p in properties
select Expression.Equal(
Expression.Property(left, p),
Expression.Property(right, p));
var body = expressions.Aggregate(Expression.AndAlso);
var lambda = Expression.Lambda<Func<T,T,bool>>(body, new [] {left, right});
return lambda.Compile();
}
}
I have a array of string say:
String[] Fields=new String[]{RowField,RowField1}
In which I can use the below query to get the values by specifying the values is query i.e RowField and RowField1:
var Result = (
from x in _dataTable.AsEnumerable()
select new
{
Name = x.Field<object>(RowField),
Name1 = x.Field<object>(RowField1)
})
.Distinct();
But if suppose I have many values in the Array like:
String[] Fields= new String[]
{
RowField,
RowField1,
RowField2,
.......
RowField1000
};
How can I use the query here without specifying each of the rowfield in the query?
How can i iterate through the array items inside the LINQ?
var Result = (
from x in _dataTable.AsEnumerable()
select (
from y in Fields
select new KeyValuePair<string, object>(y, x))
.ToDictionary())
.Distinct(DictionariesComparer);
You'll also need to write your own .ToDictionary() extension method and DictionariesComparer method (as Dictionary doesn't implement IEquatable).
Essentially, you want to retrieve specific fields from a DataTable without hardcoding the field names.
The following code will return a single dictionary object per row with the fields you specify in your array. There is no need to create additional extension methods or comparers:
var result = (from row in _dataTable.AsEnumerable()
let projection = from fieldName in fields
select new {Name = fieldName, Value = row[fieldName]}
select projection.ToDictionary(p=>p.Name,p=>p.Value));
The inner select picks the field values you need from each table row and stores them in the projection variable. The outer select converts this variable in a Dictionary
You can iterate over the result to get specific fields like this:
foreach (var row in result)
{
Console.WriteLine(row["field1"]);
}
EDIT:
The above code doesn't return distinct values. It is possible to return distinct values without writing a special comparer using group by but the code is not very pretty:
var result = (from row in table.AsEnumerable()
let projection = from fieldName in fields
select new { Name = fieldName, Value = row[fieldName] }
group projection by projection.Aggregate((v, p) =>
new {
Name = v.Name + p.Name,
Value = (object)String.Format("{0}{1}", v.Value, p.Value)
}) into g
select g.FirstOrDefault().ToDictionary(p=>p.Name,p=>p.Value));
The Aggregate creates a new projection whose Name and Value properties are the concatenation of all name and value fields. The result of the aggregate is used to group all rows and return the first row of each group. It works but it is definitely ugly.
It would be better to create a simple DictionaryComparer like the following code:
public class DictionaryComparer<TKey,TValue>: EqualityComparer<Dictionary<TKey,TValue>>
{
public override bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
//True if both sequences of KeyValuePair items are equal
var sequenceEqual = x.SequenceEqual(y);
return sequenceEqual;
}
public override int GetHashCode(Dictionary<TKey, TValue> obj)
{
//Quickly detect differences in size, defer to Equals for dictionaries
//with matching sizes
return obj.Count;
}
}
This allows you to write:
var result = (from row in table.AsEnumerable()
let projection = from fieldName in fields
select new {Name = fieldName, Value = row[fieldName]}
select projection.ToDictionary(p=>p.Name,p=>p.Value))
.Distinct(new DictionaryComparer<string, object>());
There is no foreach linq expression. I typically create my own extension method
Something along the lines of:
public static void Foreach<T>(this IEnumerable<T> items, Action<T> action)
{
foreach(T t in items)
{
action(t);
}
}
However beware if you're planning on using this with Linq2SQL as it could create a lot of db hits!