'Argument expression is not valid' when creating anonymous types dynamically - linq

I'm creating an expression tree builder to return custom anonymous types. I tried it first with discrete types and it works ok, but using TypeBuilder to build types at runtime and pass that type to the expression tree fail with this error
'Argument expression is not valid'
here is the code I use:
this method I use to create the anonymous type
private Type CreateAnonymousType(Dictionary<string, Type> properties)
{
AssemblyName dynamicAssemblyName = new AssemblyName("MyAssembly");
AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("MyAssembly");
TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("ReturnType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
foreach (var p in properties)
{
dynamicAnonymousType.DefineField(p.Key, p.Value, FieldAttributes.Public);
}
return dynamicAnonymousType.CreateType();
}
and here is how I create the expression tree
var cars = new List<Car>();
for (int i = 0; i < 10; i++)
{
cars.Add(new Car { Id = i, Name = "Car " + i, Age = 2010 + i });
}
IQueryable<Car> allCars = cars.AsQueryable();
var properties = new Dictionary<string, Type>
{
{ "Id", typeof(int) },
{ "Name", typeof(string) }
};
ParameterExpression x = Expression.Parameter(typeof(Car), "x");
var listMembers = properties.Select(p => Expression.Property(x, p.Key));
var returnType = CreateAnonymousType(properties);
object destObject = Activator.CreateInstance(returnType);
var listBind = listMembers.Select(p => Expression.Bind(returnType.GetField(p.Member.Name), p));
var result = Expression.New(returnType);
var initExp = Expression.MemberInit(result, listBind.ToArray());
var call = Expression.Call(typeof(Queryable), "Select",
new Type[] {
typeof(Car),
returnType
}
, Expression.Constant(allCars)
, Expression.Lambda(initExp, x));
var qResult = allCars.Provider.CreateQuery<IdName>(call);
foreach (var car in qResult)
{
Console.WriteLine(car.Id + " - " + car.Name);
}
the error happened while CreateQuery method executes

This is because call returns dynamically created ReturnType not IdName thus the exception. Additionally you cannot put such dynamic types like ReturnType as generic type parameters because compiler knows nothing about them so you should use dynamic instead so the type will be resolved at runtime:
var qResult = allCars.Provider.CreateQuery<dynamic>(call);

Related

Load multipe sharepoint list item fields in one Go using CSOM c#

***ctx.Load(listItemCollection,
eachItem => eachItem.Include(
item => item,
item => item["Column1"],
item => item["Column2"]
));***
i have list of fields in a array of string instead of column1 and column2, how can i pass it through in include linq, not able to create proper lambda on runtime. i tried following ways but couldn't get success. Static befor loops works but thw fields added in loop fails as it doesn't evaluate string value in loop
***Expression<Func<ListItem, object>>[] paramss = new
Expression<Func<ListItem, object>>[length];
paramss[0] = x => x.ContentType;
paramss[1] = x => x["Title"];
count = 2;
foreach (string item in solConnDefModel.Columns)
{ paramss[count] = x => x[item];
count++;
}***
Please take a reference of below code:
List dlist = context.Web.Lists.GetByTitle("listname");
context.Load(dlist);
context.ExecuteQuery();
string[] fieldNames = { "Id", "Title", "num", "mStartDate" };
// Create the expression used to define the fields to be included
List<Expression<Func<ListItemCollection, object>>> fieldsToBeIncluded = new List<Expression<Func<ListItemCollection, object>>>();
foreach (string s in fieldNames)
{
fieldsToBeIncluded.Add(items => items.Include(item => item[s]));
}
// Initialize the collection of list items
var listItems = dlist.GetItems(new CamlQuery());
context.Load(listItems, fieldsToBeIncluded.ToArray());
context.ExecuteQuery();
You can hover on load method to see what type parameter it requires, then generate a corresponding one and pass it.
i have to create lambda expression at runtime. following code i was able to get expected value
Expression<Func<ListItem, object>>[] paramss = new Expression<Func<ListItem, object>>[length];
foreach (string item in Columns)
{
if (item.ToLower() != "contenttype")
{
ParameterExpression parameter = Expression.Parameter(typeof(ListItem), "x");
var propertyInfo = typeof(ListItem).GetMethod("get_Item");
var arguments = new List<Expression> { Expression.Constant(item) };
var expression = Expression.Call(parameter, propertyInfo, arguments);
var lambda = Expression.Lambda<Func<ListItem, object>>(expression, parameter);
paramss[count] = lambda;
}
else
{
paramss[count] = x => x.ContentType;
}
count++;
}

Generic Orderby LINQ

I have a generic repository method to enable server side paging:
public virtual IEnumerable<T> GetList(out int totalPages, Expression<Func<T, bool>> filter = null,
Expression<Func<T, object>> orderby = null,
bool ascending = true,
string includeProperties = "",
int pageSize = 10, int pageNumber = 1)
{
IQueryable<T> query = c.Set<T>();
if (filter != null)
{
query = query.Where(filter);
}
query = includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProp) => current.Include(includeProp));
if (orderby != null)
{
query = #ascending ? query.OrderBy(#orderby) : query.OrderByDescending(#orderby);
}
else
{
query = #ascending ? query.OrderBy(o => o.Id) : query.OrderByDescending(o => o.Id);
}
//totalPages = (int) Math.Ceiling((double)(Queryable.Count(query) / pageSize));
totalPages = 1;
if (pageSize > 0)
{
var skip = 0;
var take = pageSize;
if (pageNumber > 1)
{
skip = (pageNumber - 1) * pageSize;
take = pageSize + skip;
}
query = query.Take(take);
query = query.Skip(skip);
}
return query.ToList();
}
T is a base class inherited by my entities
I call this repository method from my service. and i call my service from my controller.
Now from my controller how do i create my orderby expression so that i can pass any column name?
What i've tried so far (found on stackoverflow):
var _OrderByProperty = typeof(Year).GetProperty("Id");
var _OrderByParameter = Expression.Parameter(typeof(Year), "x");
var _OrderByBody = Expression.Property(_OrderByParameter, _OrderByProperty.Name);
var _OrderByConverted = Expression.Convert(_OrderByBody, typeof(Object));
var _OrderByLambda = Expression.Lambda<Func<Year, object>>
(_OrderByConverted, _OrderByParameter);
var list = s.GetList(orderby: _OrderByLambda, totalPages: out totalPages, pageNumber: pageNumber, pageSize: pageSize, ascending: isAsc);
i'm getting the error
Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.

syntax to return a number of results per page

I have this linq statement that returns result and i wanted to add the specifications of how many items are displayed per page. I know the default is 10 per page how can i change it to 40?
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
});
Get User
public virtual IEnumerable<T> GetUser<T>(int userId, Func<IDataRecord, T> modelBinder, int resultsPerPage = 10, int pageNumber = 1)
{
if (userId < 1)
throw new NullReferenceException("The sectionId cannot be null, when retreiving an element");
if (resultsPerPage < 1)
resultsPerPage = 1; // enforce bare minimum result set
if (pageNumber < 1)
pageNumber = 1; // enforce one-based page numbering
SqlCommand _command = new SqlCommand("dbo.GetUser");
_command.CommandType = CommandType.StoredProcedure;
_command.Parameters.Add(new SqlParameter { ParameterName = "userId", SqlDbType = SqlDbType.Int, Value = userId });
_command.Parameters.Add(new SqlParameter { ParameterName = "resultsPerPage", SqlDbType = SqlDbType.Int, Value = resultsPerPage });
_command.Parameters.Add(new SqlParameter { ParameterName = "pageNumber", SqlDbType = SqlDbType.Int, Value = pageNumber });
return DbInstance.ExecuteAs<T>(_command, modelBinder);
}
Neither Linq nor entity framework have any default number of records 'per page'. But since your GetUser function includes a resultsPerPage parameter, you can just do this:
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
}, 40);
To limit the number of results in Linq use the the Enumerable.Take method:
var _roster = DataCBase.StoredProcedures.GetUser<Users>(userID, r => new Users
{
Name = RosterColumnMap.Name(r),
Email = RosterColumnMap.Email(r)
}).Take(40);

How to use LINQ where clause for filtering the collection having dynamically generated type

I need to filter the ObservableCollection using LINQ Where clause in my Silverlight application.
The object type is dynamically created using method provided in following url.
http://mironabramson.com/blog/post/2008/06/Create-you-own-new-Type-and-use-it-on-run-time-(C).aspx
Is filtering my collection using Where clause for specific property possible?
How can I achieve it?
Thanks
The only way I know is using reflection, like this:
// using a list of dynamic types
var items = new List<object> { new { A = 0, B = 1 }, new { A = 1, C = 0 } };
// select ao items with A > 0
var filteredItems = items.Where(obj => (int)obj.GetType().GetField("A").GetValue(obj) > 0).ToArray();
// if you have a property instead of field, you should call GetProperty(), like this:
obj.GetType().GetProperty("PropertyName").GetValue(obj, null)
As you know the element type of your collection only at run time it probably is object at compile time. So the argument to the .Where method has to be a Func<object, bool>.
Here's a piece of code that will create such a delegate given a property of the actual element type and a lambda expression on the property (which i suppose you know the type of):
/// <summary>
/// Get a predicate for a property on a parent element.
/// </summary>
/// <param name="property">The property of the parent element to get the value for.</param>
/// <param name="propertyPredicate">The predicate on the property value.</param>
static Func<object, bool> GetPredicate<TProperty>(PropertyInfo property, Expression<Func<TProperty, bool>> propertyPredicate)
{
if (property.PropertyType != typeof(TProperty)) throw new ArgumentException("Bad property type.");
var pObj = Expression.Parameter(typeof(object), "obj");
// ((elementType)obj).property;
var xGetPropertyValue = Expression.Property(Expression.Convert(pObj, property.DeclaringType), property);
var pProperty = propertyPredicate.Parameters[0];
// obj => { var pProperty = xGetPropertyValue; return propertyPredicate.Body; };
var lambda = Expression.Lambda<Func<object, bool>>(Expression.Block(new[] { pProperty }, Expression.Assign(pProperty, xGetPropertyValue), propertyPredicate.Body), pObj);
return lambda.Compile();
}
Sample usage:
var items = new List<object> { new { A = 0, B = "Foo" }, new { A = 1, B = "Bar" }, new { A = 2, B = "FooBar" } };
var elementType = items[0].GetType();
Console.WriteLine("Items where A >= 1:");
foreach (var item in items.Where(GetPredicate<int>(elementType.GetProperty("A"), a => a >= 1)))
Console.WriteLine(item);
Console.WriteLine();
Console.WriteLine("Items where B starts with \"Foo\":");
foreach (var item in items.Where(GetPredicate<string>(elementType.GetProperty("B"), b => b.StartsWith("Foo"))))
Console.WriteLine(item);
Output:
Items where A >= 1:
{ A = 1, B = Bar }
{ A = 2, B = FooBar }
Items where B starts with "Foo":
{ A = 0, B = Foo }
{ A = 2, B = FooBar }

Dynamic Linq to Datatable Derived Field

Is it possible to use Dynamic Linq to run a query similar to:
Select a, b, a + b as c
from MyDataTable
I have an application where the user can enter SQL statements, the results of these statements are then assigned to a DataTable. There is also the option to derive a field based on other fields. (e.g. user can say field C = a + b, or field D = A*B+10 etc).
Ideally I would like to do something similar to:
string myCalc = "Convert.ToDouble(r.ItemArray[14])+Convert.ToDouble(r.ItemArray[45])";
var parameters = from r in dt.AsEnumerable()
select (myCalc);
What I want to do in this example is add the value of column 14 to column 45 and return it. It's up to the user to decide what expression to use so the text in the select needs to be from a string, I cannot hard code the expression. The string myCalc is purely for demonstration purposes.
You could do that using a Dictionary, and a DataReader and Dynamic Queries. Here is an example based in part in Rob Connery's Massive ORM RecordToExpando:
void Main()
{
string connString = "your connection string";
System.Data.SqlClient.SqlConnection conn = new SqlConnection(connString);
string statement = "SUM = EstimatedEffort + OriginalEstimate, Original = OriginalEstimate";
// Note: You should parse the statement so it doesn't have any updates or inserts in it.
string sql = "SELECT " + statement +" FROM Activities";
List<IDictionary<string, object>> results = new List<IDictionary<string, object>>();
conn.Open();
using(conn)
{
var cmd = new SqlCommand(sql, conn);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var dic = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
dic.Add(
reader.GetName(i),
DBNull.Value.Equals(reader[i]) ? null : reader[i]);
}
results.Add(dic);
}
}
foreach (var dicRow in results)
{
foreach (string key in dicRow.Keys)
{
Console.Write("Key: " + key + " Value: " + dicRow[key]);
}
Console.WriteLine();
}
}
Something like this:
void Main()
{
var dataTable = new DataTable();
dataTable.Columns.Add("a", typeof(double));
dataTable.Columns.Add("b", typeof(double));
dataTable.Rows.Add(new object[] { 10, 20 });
dataTable.Rows.Add(new object[] { 30, 40 });
string myCalc = "Convert.ToDouble(ItemArray[0]) + Convert.ToDouble(ItemArray[1])";
var query = dataTable.AsEnumerable().AsQueryable();
var result = query.Select(myCalc);
foreach (Double c in result)
{
System.Console.WriteLine(c);
}
}

Resources