I want to build a Lambda Expression using Linq Expressions that is able to access an item in a 'property bag' style Dictionary using a String index. I am using .Net 4.
static void TestDictionaryAccess()
{
ParameterExpression valueBag = Expression.Parameter(typeof(Dictionary<string, object>), "valueBag");
ParameterExpression key = Expression.Parameter(typeof(string), "key");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
BlockExpression block = Expression.Block(
new[] { result }, //make the result a variable in scope for the block
Expression.Assign(result, key), //How do I assign the Dictionary item to the result ??????
result //last value Expression becomes the return of the block
);
// Lambda Expression taking a Dictionary and a String as parameters and returning an object
Func<Dictionary<string, object>, string, object> myCompiledRule = (Func<Dictionary<string, object>, string, object>)Expression.Lambda(block, valueBag, key).Compile();
//-------------- invoke the Lambda Expression ----------------
Dictionary<string, object> testBag = new Dictionary<string, object>();
testBag.Add("one", 42); //Add one item to the Dictionary
Console.WriteLine(myCompiledRule.DynamicInvoke(testBag, "one")); // I want this to print 42
}
In the above test method, I want to assign the Dictionary item value i.e. testBag["one"] into the result. Note that I have assigned the passed in Key string into the result to demonstrate the Assign call.
You can use the following to access the Item property of the Dictionary
Expression.Property(valueBag, "Item", key)
Here is the code change that should do the trick.
ParameterExpression valueBag = Expression.Parameter(typeof(Dictionary<string, object>), "valueBag");
ParameterExpression key = Expression.Parameter(typeof(string), "key");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
BlockExpression block = Expression.Block(
new[] { result }, //make the result a variable in scope for the block
Expression.Assign(result, Expression.Property(valueBag, "Item", key)),
result //last value Expression becomes the return of the block
);
Related
I need to create an expression that extract a specific item from a dictionary.
The topic is to put the name of the item to extract inside the Expression.
I'll try to explain it better with an example.
I have this dictionary:
Dictionary<string, object> myDictionary = new Dictionary<string, object>
{
{ "Name", "My first name" },
{ "Age", 42 }
};
I wrote this code to compose an Expression that works with IDictionary:
private Expression<Func<IDictionary<string, object>, string, object>> GetDictionaryExpression()
{
var dictionary = Expression.Parameter(typeof(IDictionary<string, object>), "dict");
var keyParam = Expression.Parameter(typeof(string));
var indexer = typeof(IDictionary<string, object>).GetProperty("Item");
var indexerExpr = Expression.Property(dictionary, indexer, keyParam);
return Expression.Lambda<Func<IDictionary<string, object>, string, object>>(indexerExpr, dictionary, keyParam);
}
Finally, I can write this code:
var x = GetDictionaryExpression();
var y = x.Compile().Invoke(myDictionary, "Name"); // My first name
This code works.
What I need is to create an expression that doesn't need the field "Name" at runtime, but put it inside the Expression.
In other words, something that will let me have something like this:
var x = GetNameFromDictionary(); // <== How to write this method that extract only the 'Name' value from the dictionary?
var y = x.Compile().Invoke(visionRecord); // My first name
Thank you in advance.
I am looking to convert the following dictionary to an expandoobject:
var dicToConvert = new dictionary<int, CustomAttribute>()
My CustomAttribute class has the following properties:
int AttributeId
string AttributeValue
int tId
I'd like the expandoobject to contain only AttributeId and AttributeValue. I.e.The Value portion of the ExpandoObject shouldn't contain a whole 'CustomAttibute', just the 'AttributeValue' from it.
From my initial dictionary, the following works to convert a single KeyValuePair to a KeyValuePair:
CreateMap<KeyValuePair<int, CustomAttribute>, KeyValuePair<string, object>>()
.ConvertUsing(source => new KeyValuePair<string, object>(source.Key.ToString(), source.Value.AttributeValue));
The following also works to convert a Dictionary to a Dictionary
CreateMap<Dictionary<int, CustomAttibute>, Dictionary<string, object>>()
.ConvertUsing(source => source.ToDictionary(e => e.Key.ToString(), e => (object) e.Value.AttributeValue));
I can't make the last step in coercing into an expandoobject. I was hoping this might work:
CreateMap<Dictionary<int, CustomAttribute>, ExpandoObject>()
.ConvertUsing(source =>
{
var returnValue = new ExpandoObject();
foreach (var item in source)
{
returnValue.Add(item.Key.ToString(), item.Value.AttributeValue);
}
return returnValue;
});
But I get an error saying "A lambda Expression with a statement body cannot be converted to an expression tree".
Any help would be gratefully received!
I am attempting to write a method that will construct a contains type lambda expression that I can tack on to my queries like qry.Where(lambda).
I am pretty new to expression trees and am running into trouble.
Here is what I have so far:
public static IQueryable<T> ContainsQuery<T>(IQueryable<T> query, IEnumerable<string> propertyNames, IEnumerable<string> propertyValues)
{
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MethodInfo indexOf = typeof(string).GetMethod("IndexOf", new Type[] { typeof(string), typeof(StringComparison) });
foreach (var propertyName in propertyNames)
{
foreach (var propertyValue in propertyValues)
{
}
}
...
return query.Where(lambda);
}
I know that I need the case insensitive indexOf to do the comparison and I know that I need to loop for all the properties and all the values (think searching for one or more values in one or more columns of a table and returning the or'd result of all the combinations.
I am just having trouble getting started constructing this.
I have a large dataset (IEnumerable of [Table]-attributed class objects) from a Linq-To-Sql query and I need to produce a CSV file from it. I loop over the dataset and for each item I convert the value of each property of the item into a string using various formatting options.
Type t = typeof(T);
var properties = t.GetProperties();
foreach (var item in list)
{
foreach (var property in properties)
{
// This is made using delegates, but whatever
object value = property.GetValue(item, null);
// convert to string and feed to StringBuilder
}
}
The problem is that conversion takes even longer that running the query. The dataset contains heavily denormalized data - numerous items have the same properties having the same values and only some properties having different values. Each property value is translated separately for each item in the dataset. So my code converts the same data into the same strings - over and over. And I'd like to somehow speed this up, preferable without changing the SQL query.
Looks like MemoryCache class could work, but I need to craft unique keys for each object. I can't figure out how I could craft such keys reliably and efficiently enough.
How do I make use of MemoryCache so that I can cache translation results for objects of different types?
If you just want to speed it up I would suggest ExpressionTrees more than MemoryCache. This assumes you don't have nested objects to want to read and I can use reflection on the first item and it will be the same for each item in the IEnumerable - which from your example code in the question seems correct.
Also if it's big and you are going to just write it out to a file I would suggest going straight to a FileStream instead of a StringBuilder.
public class CSV
{
public static StringBuilder ToCSV(IEnumerable list)
{
Func<object, object[]> toArray = null;
var sb = new StringBuilder();
// Need to initialize the loop and on the first one grab the properties to setup the columns
foreach (var item in list)
{
if (toArray == null)
{
toArray = ItemToArray(item.GetType());
}
sb.AppendLine(String.Join(",", toArray(item)));
}
return sb;
}
private static Func<object, object[]> ItemToArray(Type type)
{
var props = type.GetProperties().Where(p => p.CanRead);
var arrayBody = new List<Expression>();
// Create a parameter to take the item enumeration
var sourceObject = Expression.Parameter(typeof (object), "source");
// Convert it to the type that is passed in
var sourceParam = Expression.Convert(sourceObject, type);
foreach (var prop in props)
{
var propType = prop.PropertyType;
if (IsValueProperty(propType))
{
// get the value of the property
Expression currentProp = Expression.Property(sourceParam, prop);
// Need to box to an object if value type
if (propType.IsValueType)
{
currentProp = Expression.TypeAs(currentProp, typeof (object));
}
// Add to the collection of expressions so we can build the array off of this collection
arrayBody.Add(currentProp);
}
}
// Create an array based on the properties
var arrayExp = Expression.NewArrayInit(typeof (object), arrayBody);
// set a default return value of null if couldn't match
var defaultValue = Expression.NewArrayInit(typeof (object), Expression.Constant(null));
//Set up so the lambda can have a return value
var returnTarget = Expression.Label(typeof (object[]));
var returnExpress = Expression.Return(returnTarget, arrayExp, typeof (object[]));
var returnLabel = Expression.Label(returnTarget, defaultValue);
//Create the method
var code = Expression.Block(arrayExp, returnExpress, returnLabel);
return Expression.Lambda<Func<object, object[]>>(code, sourceObject).Compile();
}
private static bool IsValueProperty(Type propertyType)
{
var propType = propertyType;
if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof (Nullable<>))
{
propType = new NullableConverter(propType).UnderlyingType;
}
return propType.IsValueType || propType == typeof (string);
}
}
I have a little problem :) I have a list where some arguments are duplicate. I must remove that. I can't use Distinct becouse i must only see at some fields (not all).I think it's great option to use lambda epxressions.
I have a declaration on my object who i working, not the same but the idea is similar.
var keys = new string[] {"column1", "column2"};
var repeatedValues = new object[] {5, 15};
var values = new List<Dictionary<string, object>>();
//MAKE FAKE DOUBLE
values.Add(new Dictionary<string, object> {
{ "column1", 5 }, { "column2", 15 }, { "column3", "test" },
{ "column4", "test1" } });
for (int i = 0; i < 10; i++)
{
values.Add(new Dictionary<string, object> {
{"column1", i}, {"column2", 10 + i}, "column3", "test"},
{"column4", "test1"}});
}
The keys columns always have the same lenght as repeatedValues - but is changed, some the lenghts is 1, other 2,3,5. Not more than 5
The keys is like primaryKeys on database table. It's quite similar. So we looking for duplicates in "primary keys columns" - i think is a good compare.
We see in that example duplicate is has value 5 in "column1" and values 15 in "column2". How i before say i must remove that, but before i must count repeated items.
I try do code like that (i know the func method awalys fail becouse (object)1 == (object)1 always return false, but it's example :
Expression expression = null;
for (int i = 0; i < keys.Length; i++)
{
Expression<Func<Dictionary<string, object>, bool>> exp = x => x[keys[i]] == repeatedValues[i];
if (expression == null)
expression = exp.Body;
else
expression = Expression.AndAlso(expression, exp.Body);
}
var parameterExpression = Expression.Parameter(typeof (Dictionary<string, object>), "x");
var lamba = Expression.Lambda<Func<Dictionary<string, object>, bool>>(expression, parameterExpression);
var res = lamba.Compile();
var counts = queryLis.Count(res);
But compilator give me a exception
variable 'x' of type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]' referenced from scope '', but it is not defined
It's possible to do this at this way ?
(Not about exception) In other step maybe the expression be asking for example repeatedValues[i] (after for) and it will doesn't know what is it ?
You need to pass the same Expression.Parameter that the original expression is referencing.
Making a new Expression.Parameter with the same name isn't good enough.
I don't know why you're messing with Expression and the like. If I understood you correctly, you're essentially duplicating a relational database situation with each entry of values being a row of data where the column represents the field name. If that's the case, then you might as well take a page from the database book and work off of an index. After your first snippet, you can get at a cleaned-up list like so:
// testing for duplicates
List<Dictionary<string, object>> duplicates = new List<Dictionary<string, object>>();
List<string> index = new List<string>();
foreach (var value in values)
{
List<string> keyValues = new List<string>();
foreach (string key in keys)
{
keyValues.Add(value[key].GetHashCode().ToString());
}
string hash = string.Join(",", keyValues.ToArray());
if (index.Contains(hash))
duplicates.Add(value);
else
index.Add(hash);
}
var cleanList = values.Except(duplicates);
EDIT: altered example so that it puts the hash in the same column order each time.