First
I cannot use Entity Framework for this because this is a Desktop application in which all database calls are first routed through a Web API End Point. A requirement of this application is that all traffic is HTTP traffic.
Setup
Lets say I have an IEnumerable<BaseItem> where the BaseItem is a base model class that all models inherit from.
We also dynamically instantiate and populate our collection with a dynamic class and dynamic properties that match the column names and appropriate data types of the System.DataTable that is returned from the query.
These dynamic classes also inherit from BaseItem which exposes two functions:
void SetValue(string PropertyName, object Value)
object GetValue(string PropertyName).
Issue
I need to be able to write a linq query that dynamically selects properties from the collection based on the unique constraint columns from the table that the query referenced. I have that in a separate List, where the string values match the dynamic property names on the dynamic class.
I know the solution is probably to use an Expression Tree, but all the examples I find use actual property references, and in this case, the property can only be accessed and set through the GetValue, and SetValue functions. I'm having a hard time wrapping my head around how to achieve this. Below is an example of the linq query i want, if I KNEW the fields I wanted to return.
var Result = DefaultCollection.Select(x => new { BUSINESS_UNIT = x.GetValue("BUSINESS_UNIT"), FISCAL_YEAR = x.GetValue("FISCAL_YEAR") }).Distinct();
Again, I can't write this because I don't know which fields i need to include in the select list until runtime when I can reference the UniqueConstraint List.
How can I achieve this dynamically with Linq Expression?
Start To Finish Example
You've populated an IEnumerable<BaseItem> from a System.Data.DataTable and lets say the query that you ran was SELECT FIRST_NAME, LAST_NAME, EMAIL ADDRESS FROM TABLEA.
The IEnumerable is populated from automation in which a dynamic class is generated to represent a row in the table and dynamic properties are added to the class to represent each column in the table.
Accessing the properties is via methods in BaseItem string result = item.GetValue("FIRST_NAME") and item.SetValue("EMAIL_ADDRESS", "MyEmail#Email.com');
You also queried INFORMATION_SCHEMA to get unique constraints on TABLEA and you store it in a public class ConstraintModel object that has a public List<string> Columns representing the columns that make up the unique constraint. In this case, only one column "EMAIL_ADDRESS" is in the constraint.
If the user makes changes to the IEnumerable<BaseItem> and more than one item has the same EMAIL_ADDRESS, we know that the UPDATE statement back to SQL will fail. We want to check that the unique constraint hasn't been violated.
At the point the user saves, we want to run a "Validation Check" on the in memory IEnumerable object. Ideally we would write a linq query that essentailly did a SELECT SUM(1), EMAIL_ADDRESS GROUP BY EMAIL_ADDRESS, and if any of the results had more than 1 value, we know a duplicate exists. Obviously the above syntax is SQL not Linq, but you get the idea. I can't know at compile time what fields on the BaseItem i need to select and group by, so I need to leverage the Constraint.Columns object and use it to write a dynamic linq expression. Additionally, the properties on the Dynamic Class are only exposed through the methods that they inherit from BaseItem ... GetValue(string PropertyName) and SetValue(string PropertyName, object Value);
After reading your full example it sounds very much like you do not need dynamic linq at all to do what you're trying to do, but you do need to know what the Primary Key is, why not try something like:
Update I have replaced a simple pk lookup with a func PK lookup so that composite Primary Keys can be used.
List<string> primaryKeyColumns = new List<string> {"Id"}; // or whatever else
List<string> uniques = /// ConstraintModel.Columns;
List<BaseItem> baseItems = // your baseItems
// Now, wit
Func<BaseItem, BaseItem, bool> equalPrimaryKey = (left, right) => {
foreach(var pk in primaryKeyColumns) {
if(left.GetValue(pk) != right.GetValue(pk)) {
return false;
}
}
return true;
}; // or whatever else
var violators = baseItems.Select(x => {
return baseItems.Any(z => {
foreach(var unique in uniques) {
if (z.GetValue(unique) == x.GetValue(unique)
&& !equalPrimaryKey(x, z)) {
return true;
}
}
return false;
});
}).ToArray();
This will give you an array of base items that are duplicated by any unique column
Using a minified version of my EnumerableExpressionExt class for helping build Expression trees for Enumerable expressions, and my ExpressionExt helper class:
public static class EnumerableExpressionExt {
private static Type TEnumerable = typeof(Enumerable);
private static Type TypeGenArg(this Expression e, int n) => e.Type.GetGenericArguments()[n];
public static MethodCallExpression Distinct(this Expression src) => Expression.Call(TEnumerable, "Distinct", new[] { src.TypeGenArg(0) }, src);
public static MethodCallExpression Select(this Expression src, LambdaExpression resultFne) => Expression.Call(TEnumerable, "Select", new[] { src.TypeGenArg(0), resultFne.ReturnType }, src, resultFne);
}
public static class ExpressionExt {
public static ConstantExpression AsConst<T>(this T obj) => Expression.Constant(obj, typeof(T));
public static LambdaExpression AsLambda(this Expression body) => Expression.Lambda(body);
public static LambdaExpression Lambda(this ParameterExpression p1, Expression body) => Expression.Lambda(body, p1);
public static NewExpression New(this Type t, params Expression[] vals) => Expression.New(t.GetConstructors()[0], vals, t.GetProperties());
public static ParameterExpression Param(this Type t, string pName) => Expression.Parameter(t, pName);
}
You can create custom Expression extensions to translate your specific methods:
public static class MyExpressionExt {
public static MethodCallExpression GetValue(this Expression obj, string propName) =>
Expression.Call(obj, "GetValue", null, propName.AsConst());
}
You can build your Expression tree as follows:
// BaseItem x
var xParm = typeof(BaseItem).Param("x");
// typeof(new { object BUSINESS_UNIT, object FISCAL_YEAR })
var anonType = (new { BUSINESS_UNIT = default(object), FISCAL_YEAR = default(object) }).GetType();
// new { BUSINESS_UNIT = x.GetValue("BUSINESS_UNIT"), FISCAL_YEAR = x.GetValue("FISCAL_YEAR") }
var newExpr = anonType.New(xParm.GetValue("BUSINESS_UNIT"), xParm.GetValue("FISCAL_YEAR"));
// DefaultCollection.Select(x => newExpr)
var selExpr = DefaultCollection.AsConst().Select(xParm.Lambda(newExpr));
// DefaultCollection.Select(x => newExpr).Distinct()
var distExpr = selExpr.Distinct();
And you can test it (using LINQPad's Dump method) like this:
// () => DefaultCollection.Select(x => newExpr).Distinct()
var f = distExpr.AsLambda();
var fc = f.Compile();
fc.DynamicInvoke().Dump();
LINQPad is also very helpful for outputting compiler built Expression trees and seeing how they were built, or IQueryable<T> queries Expression member trees.
NOTE: I like not having to call AsConst() so I have additional helpers to make that easier as well.
Related
Here's my scenario:
There is a collection of objects where each object contains a Dictionary<string, string>. The user can build a set of queries for this collection from another app to obtain a subset by selecting a Key in the Dictionary, an operator such as > or CONTAINS, etc., and a Value. They can also balance parenthesis to create groups of queries and select AND/OR operators to combine the queries.
As an example, let's say I have a collection of Car objects and the Dictionary contains keys for Make, Model, and Year.
My app is getting these queries in the form of a string like so:
"((Make = Honda) AND (Model CONTAINS Civic)) || (Year >= 2015)"
This tells me that from the collection of Car objects that I want cars that have Dictionary keys/values of <Make, Honda> and <Model, anything that contains "Civic"> OR <Year, greater than or equal to 2015>
So, I parse these out and put them into a QueryClass containing three string fields for the Key, the Operator, and the Value. I also keep track of the operator between the queries, and if they are in a group of parentheses or not.
Currently, I have to go through each QueryClass one by one performing the query, checking what the previous operator was, if it's part of a group, etc. and combining collections over and over until it reaches the end. This is tedious and seems like an awful way to do things. If there was a way to build these LINQ queries dynamically or perform SQL statements (what these are essential) on this collection it would be better.
Here's my query class that I'm storing the parsed strings in:
class QueryClass
{
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
public QueryClass(string pInput)
{
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null)
{
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
}
My parsing class is pretty long so I won't post the whole thing but it returns a List<object> where each element is either:
A QueryClass
"AND" or "OR"
Another List which means it's a group of queries that were grouped by parentheses, containing the two choices above.
Here's an example of the List<object> I get after parsing a string:
I then just loop through each element, determine if the value is a double or string, and execute a LINQ statement on my collection. I'm checking if the operator was "AND" or "OR" (or none if it's just one query), if it's part of a group or not, and combining the results appropriately.
Here is my implementation of converting your query into a Func. Since I wasn't sure what type was in your collection, I made an interface to represent objects that had an attributes Dictionary<string, string> and processed that.
Basically I added a method to QueryClass to convert it to an Expression. It uses a helper dictionary string->lambda that builds the appropriate comparison Expression for each operator.
Then I added a class to convert the List<object> into a Func<IItem,bool> suitable for a LINQ Where filter.
public interface IItem {
Dictionary<string, string> attributes { get; set; }
}
class QueryClass {
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
public QueryClass(string pInput) {
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null) {
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
static MethodInfo getItemMI = typeof(Dictionary<string, string>).GetMethod("get_Item");
static Dictionary<string, Func<Expression, Expression, Expression>> opTypes = new Dictionary<string, Func<Expression, Expression, Expression>> {
{ "==", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.Equal, lhs, rhs) },
{ ">=", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, Expression.Call(lhs, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), rhs), Expression.Constant(0)) },
{ "CONTAINS", (Expression lhs, Expression rhs) => Expression.Call(lhs, typeof(String).GetMethod("Contains"), rhs) }
};
static MemberInfo attribMI = typeof(IItem).GetMember("attributes")[0];
public Expression AsExpression(ParameterExpression p) {
var dictField = Expression.MakeMemberAccess(p, attribMI);
var lhs = Expression.Call(dictField, getItemMI, Expression.Constant(FieldName));
var rhs = Expression.Constant(Value);
if (opTypes.TryGetValue(Operator, out var exprMakerFn))
return exprMakerFn(lhs, rhs);
else
throw new InvalidExpressionException($"Unrecognized operator {Operator}");
}
}
public class LinqBuilder {
static Type TItems = typeof(IItem);
static Expression BuildOneLINQ(object term, ParameterExpression parm) {
switch (term) {
case QueryClass qc: // d => d[qc.FieldName] qc.Operator qc.Value
return qc.AsExpression(parm);
case List<object> subQuery:
return BuildLINQ(subQuery, parm);
default:
throw new Exception();
}
}
static Expression BuildLINQ(List<object> query, ParameterExpression parm) {
Expression body = null;
for (int queryIndex = 0; queryIndex < query.Count; ++queryIndex) {
var term = query[queryIndex];
switch (term) {
case string op:
var rhs = BuildOneLINQ(query[++queryIndex], parm);
var eop = (op == "AND") ? ExpressionType.AndAlso : ExpressionType.OrElse;
body = Expression.MakeBinary(eop, body, rhs);
break;
default:
body = BuildOneLINQ(term, parm);
break;
}
}
return body;
}
public static Func<IItem, bool> BuildLINQ(List<object> query) {
var parm = Expression.Parameter(TItems, "i");
return Expression.Lambda<Func<IItem, bool>>(BuildLINQ(query, parm), parm).Compile();
}
}
Once you have this, you can pass in a List<object> expression and then filter your collection. Given a query q and a collection of IItems cs, you can do:
var ans = cs.Where(LinqBuilder.BuildLINQ(q));
I would approach this problem little differently, since what you are already having is List<object>, which internally contain a QueryClass containing all the relevant fields containing information, FieldName,Operator and Value, where you are aware which of the binary expressions have to be bundled in a parentheses. Important point is how can you create a Run-time Expression to take care of all kinds of scenarios.
Following is a sample to mimic your scenario:
Sample Class
public class Car
{
public string Make {get; set;}
public string Model {get; set;}
public int Year {get; set;}
}
Query
((c.Make.Equals("Honda") AndAlso c.Model.Contains("Civic")) Or (c.Year >= 2015))
Linqpad Code
void Main()
{
var cars = new List<Car>();
Expression<Func<Car,bool>> e1 = c => c.Make.Equals("Honda");
Expression<Func<Car,bool>> e2 = c => c.Model.Contains("Civic");
Expression<Func<Car,bool>> e3 = c => c.Year >= 2015;
var expr1 = Expression.AndAlso(e1.Body,e2.Body);
var expr2 = e3;
var finalExpression = Expression.Or(expr1,expr2.Body);
finalExpression.Dump();
}
Purpose
As it can be seen that I have manually constructed the Expressions and finally Dump the final expression, since in Linqpad it provides a graphical representation of how shall the Expression be constructed dynamically, overall image is too big and deep to be pasted here (you may try yourself using LinqPad), but following are the relevant details:
Create a ParameterExpression, this acts as a lambda parameter representing the Car class object (this is independent of the fields of the Query class)
var parameterExpression = Expression.Parameter(typeof(Car),"c");
Create MemberExpression to access each relevant field of the Car class, which is used in the Query (this one needs Field property of the Query class)
var makeMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Make"));
var modelMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Model"));
var yearMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Year"));
Completing the Expression:
a.) c => c.Make.Equals("Honda") we create as follows: (this one needs Value property of the QueryClass)
var makeConstantExpression = Expression.Constant("Honda");
var makeEqualExpression = Expression.Equal(makeMemberAccessExpression, makeConstantExpression);
b.) c.Model.Contains("Civic") can be represented as follows here we need supply the MethodInfo for the string Contains method and create a MethodCallEXpression
var modelConstantExpression = Expression.Constant("Civic");
var stringContainsMethodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var modelContainsMethodExpression = Expression.Call(modelMemberAccessExpression, stringContainsMethodInfo, modelConstantExpression);
c.) c.Year >= 2015 can simply projected as:
var yearConstantExpression = Expression.Constant(2015);
var yearGreaterThanEqualExpression = Expression.GreaterThanOrEqual(yearMemberAccessExpression, yearConstantExpression);
Combining all together to form the Composite Expression:
Expressions a.) and b.) are combined together as follows:
((c.Make.Equals("Honda") AndAlso c.Model.Contains("Civic"))
var firstExpression = Expression.AndAlso(makeEqualExpression,modelContainsMethodExpression);
Expression c.) is independent:
c.Year >= 2015
var secondExpression = yearGreaterThanEqualExpression;
Final Combined Expression and Creation a Func Delegate
// Expressions combined via Or (||)
var finalCombinedExpression = Expression.Or(firstExpression,secondExpression);
// Create Lambda Expression
var lambda = Expression.Lambda<Func<Car,bool>>(finalCombinedExpression, parameterExpression);
// Create Func delegate via Compilation
var func = lambda.Compile();
func delegate thus can be used in any of the where clause in the Linq, which expects Func<Car,bool>
Design Suggestions
Using the explanation above and values from the Query Class placeholders or directly from the Dictionary it is feasible to create any number of Expressions to be used dynamically in the code, to be compiled and used as a Func delegate
Binary expressions like Equal, GreaterThan, LessThan, LessThanOrEqual,GreaterThanOrEqual are all exposed by the Expression trees to be used directly, For method like Contains available by default you need Reflection to get theb MethodInfo, similarly it can be done for the Static methods, just that there's object expressions
All Expressions expect values to be supplied Left to Right in the correct order, it cannot be random or incorrect order, else it will fail at run-time.
In your case, since you have few queries combined in parentheses and few independent, I would recommend creating multiple List<Expression>, where each List is combined in parentheses using AndAlso or OrElse and each list can thus be combined using And / Or
By this approach you shall be able to construct very complex requirements at runtime using Linq Expressions.
You should be able to use Linq Expressions (System.Linq.Expressions) and leverage predicates to handle your filtering.
public IQueryable<Car> GetCars(Expression<Func<Car, bool>> filter)
{
return context.Cars.Where(filter);
}
That said, the challenge will be to build your predicate expressions based off of your custom QueryClass object. To handle the filter on each Dictionary you can create a method to handle each:
public Expression<Func<Car, bool>> GetModelFilter(QueryClass modelQuery)
{
return modelQuery.Operator == "CONTAINS"? car => car.Model.Contains(modelQuery.Value) : car => car.Model == modelQuery.Value;
}
Considering you have a limited amount of filters, the above may be acceptable. However, when dealing with a large set this can also be done more dynamically, using reflection or a dynamic predicate builder but for simplicity you can follow the above.
HTH
I am retrieving some tuples from a database that are mapped to entity classes by means of Entity Framework.
For these entities, I have a key selector function (supplied at runtime by other developers) that I would like to pass to Queryable.OrderBy. The key selector function is provided upon "registration" of the entity type in my system - which happens by means of a method that looks roughly like this:
public void RegisterEntity<TEntity, TKey>(string entityName, TKey defaultKey, Func<TEntity, TKey> keySelectorFunc)
I would like to execute this OrderBy call before materializing the results to entity objects (i.e. in such a way that the OrderBy call still gets translated to SQL under the hood).
The problem is that the entities have composite keys, and thus, the key selector function will return a custom object instantiated in the function. You can imagine it like this:
var keySelectorFunc = e => new CustomKey(e.Value1, e.Value2);
As usual, Entity Framework does not like this (the usual "Only parameterless constructors and initializers are supported in LINQ to Entities" error).
Is there any way to use such a custom key selector function to return a custom key? Do I have to resort to anonymous classes? Or should I move the OrderBy call to a place after I have left the LINQ-to-Entities world?
In this particular case it would be easy to use Sort method of Generic List.
https://msdn.microsoft.com/en-us/library/3da4abas(v=vs.110).aspx
Sort method requires the type of the list to implement IComparable interface and it uses the implementation of CompareTo method from IComparable interface. Otherwise implementation of IComparer also can be passed to this method.
So if your entity class is already implemeting IComparable interface then this should surely work for you. You will have to to .ToList() on the IQueryable result of course before you can call the Sort method on it.
public class Category : IComparable<Category>
{
public int CategoryId { get; internal set; }
public string CategoryName { get; internal set; }
public int CompareTo(Category x)
{
return String.Compare(x.CategoryName, this.CategoryName, StringComparison.InvariantCulture);
}
}
List<Category> categories = new List<Category>();
categories.Add(new Category {CategoryName = "Cate1"});
categories.Add(new Category {CategoryName = "Cate2"});
categories.Sort();
foreach (var cat in categories)
{
Console.WriteLine(cat.CategoryName);
}
This displays me category names in reverse order based on the comparison logic I have written in the CompareTo method of Category Class.
In this case I think the best way is use a custom ExtensionMethod to avoid any overhead of coding or unnecessary complexity to do that.
See if it implementation can help you.
First we create your customkey class that is responsable to create the statement expressions:
class CustomKey
{
public CustomKey(params string[] value)
{
if(!value.Any())
throw new InvalidOperationException("Select at least one Property for this operation");
Values = new List<string>();
Values.AddRange(value);
}
private List<string> Values { get; set; }
// this method run throughout all property configured to create the expressions
public void ForEachProperty<TSource, TKey>(Action<Expression<Func<TSource, TKey>>, bool> method)
{
bool firstItem = true;
Values.ForEach(f =>
{
var expression = CreateExpression<TSource, TKey>(f);
method(expression, firstItem);
firstItem = false;
});
}
// this method is responsable to create each expression
Expression<Func<TSource, TKey>> CreateExpression<TSource, TKey>(string property)
{
var parameter = Expression.Parameter(typeof(TSource), "x");
var member = typeof(TSource).GetMember(property).FirstOrDefault();
Expression body = Expression.MakeMemberAccess(parameter, member);
return Expression.Lambda<Func<TSource, TKey>>(Expression.Convert(body, typeof(object)), parameter);
}
}
After that we create your custom ExtesionMethod, somethink like that:
public static class OrderByExtensionClass
{
// instead of try passing an expression, we pass our CustomKey object with the columns to sort.
// than this method create the apropriate OrderBy Expression statement
public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, CustomKey customKey)
{
// the parameter isFirst is just to control where we are to build the expression
customKey.ForEachProperty<TSource, object>((expression, isFirst) =>
{
if (isFirst)
source = source.OrderBy(expression);
else
source = ((IOrderedQueryable<TSource>)source).ThenBy(expression);
});
return ((IOrderedQueryable<TSource>)source);
}
}
After that we just do:
CustomKey custom = new CustomKey("Name", "Age");
myEntityContext.People.OrderBy(custom).ToList()
I hope it can help you.
Part of the problem, I think, is that OrderBy wouldn't know what to do with a complex type. SQL Server knows how to order by primitive types, but that's about it. You would have to do something like ...OrderBy(x=>x.Field1).ThenBy(x=>x.Field2). You could write an extension method that takes the key, extracts the property names from the key, and builds the .OrderBy().ThenBy() expression, as long as you know what the key will be before executing the query. Otherwise yeah, you may have to materialize the results before ordering.
How can I convert Func<DepartmentViewModel, bool> to
Func<Department, bool>?
I have seen a lot of posts about this problem but none of those could help me.
I call this function :
public DepartmentViewModel GetSingle(Expression<Func<DepartmentViewModel, bool>> whereCondition)
from GUI layer like this :
_departmentService.GetSingle(de => de.Id ==id));
and inside GetSingle function which locate in my business layer I must call
public IEnumerable<Department> GetAll(Func<Department, bool> predicate = null)
but GetAll function accepts a Func<Department, bool> type
This is my object :
class Department {
public string name
}
and
class DepartmentViewModel{
public string name
}
regard , I found best answer :
Func<DepartmentViewModel, bool> some_function = whereCondition.Compile();
Func<Department, bool> converted = d => some_function(
new DepartmentViewModel {
Id=d.Id,
Description=d.Descriptions
}
);
You can't change Func<T1,bool> to Func<T2,bool>, but you can convert expression of the same.
That requires a bit of work, First you have to pass Expression<Func<T,bool>>, then you can easily convert your expression to match incoming parameter.
So you can change your method to,
Expression<Func<DepartmentViewModel,bool>> srcLambda =
x => x.DepartmentName.StartsWith("Admin")
Expression<Func<Department,bool>> destLambda =
ConvertTo<Department,DepartmentViewModel>( srcLambda);
This assumes that DepartmentViewModel (DTO) has same fields as Department. Otherwise, you will have to change the code a bit to fit your needs.
public static Expression<Func<TDest,bool>>
ConvertTo<TSrc,TDest>(Expression<Func<TSrc,bool>> srcExp)
{
ParameterExpression destPE = Expression.Parameter(typeof(TDest));
ExpressionConverter ec = new ExpressionConverter(typeof(TSrc),destPE);
Expression body = ec.Visit(srcExp.Body);
return Expression.Lambda<Func<TDest,bool>>(body,destPE);
}
public class ExpressionConverter: ExpressionVisitor{
private Type srcType;
private ParameterExpression destParameter;
public ExpressionConverter(Type src, ParameterExpression dest){
this.srcType = src;
this.destParameter= dest;
}
protected override Expression
VisitParameter(ParameterExpression node)
{
if(node.Type == srcType)
return this.destParameter;
return base.VisitParameter(node);
}
}
I assume you need to issue these queries to Entity Framework (or some other query provider). In that case, you need to work with expressions, not just functions, since only the former store the query information required to be transformed by the provider (for example, into SQL queries).
Here's a simple example:
Expression<Func<DepartmentViewModel, bool>> filterDVM = dvm => dvm.Name == "abc";
You first need to have some logic that will accept a Department and convert it into a DepartmentViewModel:
Expression<Func<Department, DepartmentViewModel>> getViewModel = dep =>
new DepartmentViewModel
{
Name = dep.Name,
Location = dep.Location,
};
Once you have this, you can apply your transformation onto the IQueryable<Department> sequence, after which, you can apply your filter:
var dvm = context.Departments.Select(getViewModel).Where(filterDVM);
public static MapFrom Map(this IDataReader idr, params string[] columns)
{
return new MapFrom { Columns = columns };
}
public static IEnumerable<TEntity> To<TEntity>(this MapFrom from, Func<TEntity, object> map)
{
// logic here.
}
public IEnumerable<FooEntity> Execute()
{
using (DbCommand cmd = db.GetStoredProcCommand("GetListOfFoo"))
{
using (IDataReader idr = db.ExecuteReader(cmd))
{
return idr.Map("FooID", "FooOfPooAmount", "AnotherFooColumn", "BarID")
.To<FooEntity>(x => new object[]{
x.FooID, x.FooOfPooAmount, x.AnotherFoo, x.BarID
});
}
}
}
I wish to take a data reader and create a simple mapper that is easy to use and is also performant. I haven't used expression trees much, but I feel like they may be needed. What I want to do is inspect the relationship of x to it's array (The lambda expression), and then use this relationship to automatically map values appropriately and create a new List of Foo Entitiies.
MapFrom is just a container to carry information and allow fluent extension methods.
The point of these acrobatics is to have a simple way to specify relationships. I realize I could easily do something like in a different form, but I wanted to be able to list columns "fluently" [1] (as in typing column name as a string followed by comma then next column name) and then list the object properties in sequential order and infer the mapping from this. It it possible to implement the To method above? Are Expression Trees appropriate here? I figure I can easily enough infer type casting for the data reader fields from the FooEntity property types.
[1] In this context I do not mean fluent as in "Fluent API"
I wrote here your MapFrom class, than you have to write the code to copy the value from columns to the property of an instance of FooEntity and use the yield return statement to return a collection of your entity, so add another method to the MapFrom class:
public class MapFrom
{
private readonly IDictionary<string, string> columnMapDictionary =
new Dictionary<string, string>();
public MapFrom(string[] columns)
{
foreach (var column in columns)
{
columnMapDictionary.Add(column, null);
}
}
public void To<T>(params Expression<Func<T, object>>[] properties)
{
var index = 0;
foreach (var columnMap in columnMapDictionary.ToDictionary(k => k.Key))
{
var member = (properties[index++].Body as MemberExpression);
var propertyName = member.Member.Name;
columnMapDictionary[columnMap.Key] = propertyName;
}
}
}
Use it as:
var mapper = new MapFrom("Name", "Surname");
mapper.To<FooEntity>(p => p.FirstName, p => p.LastName);
I've got a few tables that all have the same column domainID which basically just controls what data gets displayed on which website, as they share a database.
So when I go to databind a table to a control I would need to create a large switch to handle the different LINQ queries. I would like to create a utility method which takes the table type as a parameter and then return a where clause based on a column in passed table.
public static IEnumerable<T> ExecuteInContext<T>(
IQueryable<T> src)
{
int domain = 1;//hard coded for example
return src.Where(x => x.DomainID == domain);//Won't work, has to be a way to do this.
}
I'm stuck on the return code. You can't simply construct a where clause like I currently am because it doesn't know what table i'm talking about.
I'm trying to call that first method like this:
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
var q = Utility.ExecuteInContext(db.GetTable<item>());
Repeater1.DataSource = q;
Repeater1.DataBind();
}
I hope this explains what I'm trying to do.
Edit: BrokenGlass's answer solved my problem. I would like to add that you need to open up your .dbml.cs file and extend the table/class with your interface. I also wanted to point out that the project wouldn't build if my column was nullable, it said it wasn't the same return type as my interface.
You have to restrict your T to a class that has a property of DomainID - you can add these interface implementations in partial classes that extend your data model.
public interface IFoo
{
int DomainId { get; set; }
}
..
public static IQueryable<T> ExecuteInContext<T>(IQueryable<T> src) where T: IFoo
{
int domain = 1;//hard coded for example
return src.Where(x => x.DomainID == domain);
}
Expression pe = Expression.Parameter(typeof(T));
Expression prope = Expression.Property(pe, "DomainID");
Expression ce = Expression.Equals(prope,
Expression.Constant((int)1);
Expression<Func<T,bool>> exp =
Expression.Lambda<Func<T,bool>>(
ce, pe);
return query.Where(exp);
You should be able to cast your generic parameter to the intended type...
public static IEnumerable<T> ExecuteInContext<T>(IQueryable<T> src)
{
int domain = 1;//hard coded for example
return src.Where(x => ((T)x).DomainID == domain);
}
But you realize you've created a generic method that assumes its type parameter will always expose a specific property? If you're going to do that, you should apply a generic type constraint such that T is always derived from a type that has that property...
For example:
public static IEnumerable<T> ExecuteInContext<T>(IQueryable<T> src) where T : IMyDomainObject
I'm not sure if I understand what you mean, but maybe you want to add a where clause:
public static IEnumerable<T> ExecuteInContext<T>(IQueryable<T> src)
where T: MyType //MyType exposing your DomainId
{
int domain = 1;//hard coded for example
return src.Where(x => x.DomainID == domain);//Won't work, has to be a way to do this.
}