Assignment in .NET 3.5 expression trees - linq

Is it possible to encode an assignment into an expression tree?

No, I don't believe so.
Certainly the C# compiler disallows it when converting a lambda expression:
int x;
Expression<Func<int,int>> foo = (x=y); // Assign to x and return value
This yields the error:
CS0832: An expression tree may not contain an assignment operator

You should able to do it with .NET 4.0 Library. by import Microsoft.Scripting.Core.dll to your .NET 3.5 project.
I am using DLR 0.9 - There might be some change on Expession.Block and Expression.Scope in version 1.0 (You can see reference from http://www.codeplex.com/dlr/Thread/View.aspx?ThreadId=43234)
Following sample is to show you.
using System;
using System.Collections.Generic;
using Microsoft.Scripting.Ast;
using Microsoft.Linq.Expressions;
using System.Reflection;
namespace dlr_sample
{
class Program
{
static void Main(string[] args)
{
List<Expression> statements = new List<Expression>();
ParameterExpression x = Expression.Variable(typeof(int), "r");
ParameterExpression y = Expression.Variable(typeof(int), "y");
statements.Add(
Expression.Assign(
x,
Expression.Constant(1)
)
);
statements.Add(
Expression.Assign(
y,
x
)
);
MethodInfo cw = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
statements.Add(
Expression.Call(
cw,
y
)
);
LambdaExpression lambda = Expression.Lambda(Expression.Scope(Expression.Block(statements), x, y));
lambda.Compile().DynamicInvoke();
Console.ReadLine();
}
}
}

My extension method for doing exactly this:
/// <summary>
/// Provides extensions for converting lambda functions into assignment actions
/// </summary>
public static class ExpressionExtenstions
{
/// <summary>
/// Converts a field/property retrieve expression into a field/property assign expression
/// </summary>
/// <typeparam name="TInstance">The type of the instance.</typeparam>
/// <typeparam name="TProp">The type of the prop.</typeparam>
/// <param name="fieldGetter">The field getter.</param>
/// <returns></returns>
public static Expression<Action<TInstance, TProp>> ToFieldAssignExpression<TInstance, TProp>
(
this Expression<Func<TInstance, TProp>> fieldGetter
)
{
if (fieldGetter == null)
throw new ArgumentNullException("fieldGetter");
if (fieldGetter.Parameters.Count != 1 || !(fieldGetter.Body is MemberExpression))
throw new ArgumentException(
#"Input expression must be a single parameter field getter, e.g. g => g._fieldToSet or function(g) g._fieldToSet");
var parms = new[]
{
fieldGetter.Parameters[0],
Expression.Parameter(typeof (TProp), "value")
};
Expression body = Expression.Call(AssignmentHelper<TProp>.MethodInfoSetValue,
new[] {fieldGetter.Body, parms[1]});
return Expression.Lambda<Action<TInstance, TProp>>(body, parms);
}
public static Action<TInstance, TProp> ToFieldAssignment<TInstance, TProp>
(
this Expression<Func<TInstance, TProp>> fieldGetter
)
{
return fieldGetter.ToFieldAssignExpression().Compile();
}
#region Nested type: AssignmentHelper
private class AssignmentHelper<T>
{
internal static readonly MethodInfo MethodInfoSetValue =
typeof (AssignmentHelper<T>).GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Static);
private static void SetValue(ref T target, T value)
{
target = value;
}
}
#endregion
}

As Jon Skeet and TraumaPony have already said, Expression.Assign isn't available before .NET 4. Here's another concrete example of how to work around this missing bit:
public static class AssignmentExpression
{
public static Expression Create(Expression left, Expression right)
{
return
Expression.Call(
null,
typeof(AssignmentExpression)
.GetMethod("AssignTo", BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(left.Type),
left,
right);
}
private static void AssignTo<T>(ref T left, T right) // note the 'ref', which is
{ // important when assigning
left = right; // to value types!
}
}
Then simply call AssignmentExpression.Create() in place of Expression.Assign().

You could probably work around it by nexting expression trees. Call a lambda function, where an argument is the value of the assignee.

Related

How to search Oracle table with a string key using Entity Framework 5 and the ODP.NET Managed Provider

My table is like this (pseudo code):
CREATE TABLE DOCUMENT_LOCATOR (DOCUMENT_ID VARCHAR2(6) PRIMARY KEY)
When using the latest ODP.NET Managed Driver v121.1.2 downloaded via NuGet, the following Entity Framework commands do NOT use the index:
context.DOCUMENT_LOCATOR.Find("ABC123");
context.DOCUMENT_LOCATOR.Find(EntityFunctions.AsNonUnicode("ABC123"));
context.DOCUMENT_LOCATOR.FirstOrDefault(i => i.DOCUMENT_ID == "ABC123");
The following command does use the index:
context.DOCUMENT_LOCATOR.FirstOrDefault(i => i.DOCUMENT_ID == EntityFunctions.AsNonUnicode("ABC123"));
Unfortunately, UPDATEs and DELETEs do not use the index, so they basically render EF/Oracle useless for large tables. Has anyone figured out how to work around this?
After a fair amount of searching, I found a very nice solution. It sets all the string properties of all the entities to IsUnicode(false). To use it, you simply add one line to the OnModelCreating method of your DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ASSUMED_NAME_FILESMap());
modelBuilder.Configurations.Add(new ASSUMED_NAME_FILENAME_ONLYMap());
modelBuilder.Configurations.Add(new DOCUMENT_LOCATORMap());
modelBuilder.Configurations.Add(new IMS_IMAGE_TABLEMap());
modelBuilder.DisableUnicodeForAllEntityStrings(this);
}
As you can see, I changed the code to be an extension. Here's my adapted code:
public static class DbModelBuilderExtensions
{
/// <summary>
/// Disables unicode for all string properties on entities in the given context.
/// MUST BE CALLED AFTER modelBuilder.Configurations.Add(map) CALLS.
/// </summary>
/// <remarks>
/// Adapted from http://www.johncoder.com/Post/EFCodeFirstDisableUnicodeforallStringProperties
/// </remarks>
public static void DisableUnicodeForAllEntityStrings(this DbModelBuilder builder, DbContext context)
{
// Get all IDbSet<> properties from the context
var entityTypes = from property in context.GetType().GetProperties()
where property.PropertyType.ImplementsInterface(typeof(IDbSet<>))
let entityType = property.PropertyType.GetGenericArguments()[0]
select entityType;
// Disable Unicode support for each table
foreach (var entityType in entityTypes)
DisableUnicodeForEntityStrings(builder, entityType);
}
private static void DisableUnicodeForEntityStrings(DbModelBuilder modelBuilder, Type entityType)
{
// Get all string properties with setters
var stringProperties = from property in entityType.GetProperties()
where property.PropertyType == typeof(string)
&& property.CanWrite
select property;
// Each table field must be varchar for now,
// so take the string property & call IsUnicode(false).
foreach (var property in stringProperties)
{
// Don't remove this line.
// Lambda might not work without it.
PropertyInfo prop = property;
// Create the correct expression type,
// should be Expression<Func<TModel, string>>
var exprType = typeof(Expression<>)
.MakeGenericType(typeof(Func<,>)
.MakeGenericType(prop.ReflectedType, typeof(string)));
// Find and execute the Entity() method,
// using TModel generic parameter
var obj = modelBuilder.GetType()
.GetMethod("Entity")
.MakeGenericMethod(prop.ReflectedType)
.Invoke(modelBuilder, null);
// Runtime Lambda expression to represent
// something like Property(p => p.Suffix)
ParameterExpression pe = Expression.Parameter(prop.ReflectedType, "p");
var expression = Expression.Lambda(Expression.Property(pe, prop.Name), pe);
// Find the Property method that takes an expression as a parameter
// and then invoke it using the expression that was just built
var p = obj.GetType()
.GetMethod("Property", new[] { exprType })
.Invoke(obj, new[] { expression });
// If all goes well, we'll have a StringPropertyConfiguration.
var propertyConfig = p as StringPropertyConfiguration;
if (propertyConfig != null)
propertyConfig.IsUnicode(false);
}
}
public static bool ImplementsInterface(this Type value, Type interfaceType)
{
return value.GetInterface(interfaceType.FullName) != null;
}
}

Change a LINQ expression predicate from one type to another

I have two unrelated classes. One is exposed as API, and the other is used internally by 3rd party API.
Entity is exposed from our API, while EntityProvider is from the 3rd party assembly.
class Entity
{
public A { get; set; }
}
class EntityProvider
{
public A { get; set; }
}
Consumers of our API will provide predicates of the form Expression <Func<Entity, bool>> and I need to modify it to Expression <Func<EntityProvider, bool>> so that I can pass the same to internal 3rd party assembly.
Please help with this conversion.
Since Expressions in .NET are immutable, the only way to do this is to rebuild the whole expression. To do this usually involves inheriting from the ExpressionVisitor class. Depending on the complexity of the expressions you have to convert this could be quite complicated.
This is a simple example of a visitor that will work with simple expressions( like x=>x.Someproperty == somevalue ). It's just an example to get you started and it's in no way finished or tested(it won't handle method calls in the expression for example)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
//Type from which to convert
public class A
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
//Type to which we want the Expression converted
public class B
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
//the expression we want to convert expresion
Expression<Func<A, bool>> expA = x => x.Property1 == 6 && x.Property2 == 3;
var visitor = new ParameterTypeVisitor<A,B>(expA);
var expB = visitor.Convert();
var b = new B() { Property1 = 6, Property2 = 3 };
//try the converted expression
var result = expB.Compile().Invoke(b);
}
}
public class ParameterTypeVisitor<TFrom,TTo> : ExpressionVisitor
{
private Dictionary<string, ParameterExpression> convertedParameters;
private Expression<Func<TFrom, bool>> expression;
public ParameterTypeVisitor(Expression<Func<TFrom,bool>> expresionToConvert )
{
//for each parameter in the original expression creates a new parameter with the same name but with changed type
convertedParameters = expresionToConvert.Parameters
.ToDictionary(
x => x.Name,
x => Expression.Parameter(typeof (TTo), x.Name)
);
expression = expresionToConvert;
}
public Expression<Func<TTo,bool>> Convert()
{
return (Expression<Func<TTo, bool>>)Visit(expression);
}
//handles Properties and Fields accessors
protected override Expression VisitMember(MemberExpression node)
{
//we want to replace only the nodes of type TFrom
//so we can handle expressions of the form x=> x.Property.SubProperty
//in the expression x=> x.Property1 == 6 && x.Property2 == 3
//this replaces ^^^^^^^^^^^ ^^^^^^^^^^^
if (node.Member.DeclaringType == typeof(TFrom))
{
//gets the memberinfo from type TTo that matches the member of type TFrom
var memeberInfo = typeof (TTo).GetMember(node.Member.Name).First();
//this will actually call the VisitParameter method in this class
var newExp = Visit(node.Expression);
return Expression.MakeMemberAccess(newExp, memeberInfo);
}
else
{
return base.VisitMember(node);
}
}
// this will be called where ever we have a reference to a parameter in the expression
// for ex. in the expression x=> x.Property1 == 6 && x.Property2 == 3
// this will be called twice ^ ^
protected override Expression VisitParameter(ParameterExpression node)
{
var newParameter = convertedParameters[node.Name];
return newParameter;
}
//this will be the first Visit method to be called
//since we're converting LamdaExpressions
protected override Expression VisitLambda<T>(Expression<T> node)
{
//visit the body of the lambda, this will Traverse the ExpressionTree
//and recursively replace parts of the expression we for which we have matching Visit methods
var newExp = Visit(node.Body);
//this will create the new expression
return Expression.Lambda(newExp,convertedParameters.Select(x=>x.Value));
}
}

How can I make CRMSvcUtil.exe generate unduplicated, error-free early-bound option sets?

I use Erik Pool's implementation of ICodeWriterFilterService and Manny Grewal's GenerateOption function as a model to filter out unwanted entities in the file that CRMSvcUtil generates. While Erik recommends returning true for the GenerateOptionSet method to generate enums for option sets, doing so duplicates any of the global option sets that are used by any particular entity (as mentioned in one of the comments on that post).
To address this, I check to see if the option set has been already generated, and if so, I return the default option (presumably false for most cases) as in the below.
//list of generated option sets, instantiated in the constructor
private List<string> GeneratedOptionSets;
public bool GenerateOptionSet
(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
{
if (!GeneratedOptionSets.Contains(optionSetMetadata.Name))
{
GeneratedOptionSets.Add(optionSetMetadata.Name);
return true;
}
return _defaultService.GenerateOptionSet(optionSetMetadata, services);
}
But when incorporating the generated file in my CRM projects, the compilation error
Cannot convert type 'Microsoft.Xrm.Sdk.OptionSetValue' to 'int'
is always thrown by every line of code that looks like
this.SetAttributeValue
("address1_shippingmethodcode", new Microsoft.Xrm.Sdk.OptionSetValue(((int)(value))));
.
As a workaround, I use a separate project where I filter the entities I need, run CRMSvcUtil with the arguments Erik suggests, replace the troublesome part of the code (int)(value) (where value is an OptionSetValue) with value.Value after the file is generated, and then resave the file, and all issues go away.
My question is this: do I need to do something differently that will fix this compilation error with the default CRMSvcUtil generated file without doing something so hackish as altering that generated file?
You can use the ICustomizeCodeDomService interface to rewrite the SetAttributeValue method for the optionSets. Snippet below:
namespace The.NameSpace
{
using System;
using System.CodeDom;
using System.Diagnostics;
using System.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
/// <summary>
/// The customize code dom service.
/// </summary>
public sealed class CustomizeCodeDomService : ICustomizeCodeDomService
{
#region Constants and Fields
/// <summary>
/// The metadata.
/// </summary>
private IOrganizationMetadata metadata;
#endregion
#region Properties
#endregion
#region Public Methods
/// <summary>
/// The customize code dom.
/// </summary>
/// <param name="codeCompileUnit">
/// The code compile unit.
/// </param>
/// <param name="services">
/// The services.
/// </param>
public void CustomizeCodeDom(CodeCompileUnit codeCompileUnit, IServiceProvider services)
{
// Locate the namespace to use
CodeNamespace codeNamespace = codeCompileUnit.Namespaces[0];
var metadataProviderService = (IMetadataProviderService)services.GetService(typeof(IMetadataProviderService));
var filterService = (ICodeWriterFilterService)services.GetService(typeof(ICodeWriterFilterService));
this.metadata = metadataProviderService.LoadMetadata();
foreach (EntityMetadata entityMetadata in this.metadata.Entities)
{
if (filterService.GenerateEntity(entityMetadata, services))
{
CodeTypeDeclaration entityClass =
codeNamespace.Types.Cast<CodeTypeDeclaration>().First(codeType => codeType.Name.ToUpper() == entityMetadata.SchemaName.ToUpper());
UpdateEnumSetter(entityClass, entityMetadata);
}
}
}
#endregion
#region Private Methods
private static void UpdateEnumSetter(
CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf)))
{
//Match the respective field Name.
AttributeMetadata metadata1 = attributeMetadata;
foreach (
CodeTypeMember codeMembers in
entityClass.Members.Cast<CodeTypeMember>().Where(
codeMembers => codeMembers.Name == metadata1.SchemaName))
{
var codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet)
{
if (attributeMetadata.AttributeType != null && attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist)
{
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] =
new CodeSnippetStatement
{
Value =
String.Format(
"this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));",
attributeMetadata.LogicalName)
};
Debug.WriteLine(String.Format("{0}.{1}", entity.LogicalName, attributeMetadata.LogicalName));
}
}
}
}
}
#endregion
}
}
Some changes to the UpdateEnumSetter method:
private static void UpdateEnumSetter(CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf)))
{
AttributeMetadata currentMetadata = attributeMetadata;
foreach (CodeTypeMember codeMembers in entityClass.Members.Cast<CodeTypeMember>().Where(codeMembers => codeMembers.Name == currentMetadata.SchemaName))
{
CodeMemberProperty codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet)
{
if (attributeMetadata.AttributeType != null && (attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist || attributeMetadata.AttributeType.Value == AttributeTypeCode.Status))
{
if (codeProperty.SetStatements[1].GetType() == typeof(CodeConditionStatement))
{
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] = new CodeSnippetStatement
{
Value = String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName)
};
}
else
{
codeProperty.SetStatements[1] = new CodeSnippetStatement(String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName));
}
}
}
}
}
}
I'm betting that Guarav's answer is the real way to go, but in the absence of documentation surrounding CRMSvcUtil, I'm forced to use my workaround. (I use a separate project where I filter the entities I need, run CRMSvcUtil with the arguments Erik suggests, replace the troublesome part of the code(int)(value) (where value is an OptionSetValue) with value.Value after the file is generated, and then resave the file.)
Not a perfect solution, but it's been working on the few samples I've worked with so far.
It turns out that this fault is to do with the code attempting to make optionsets that look like the code below when the types are available for use. Note the only difference is the correct type being chose for the return type and the cast.
It should be possible to update the codegen stuff to fix this bug, but it might be better to get microsoft to fix the damn thing properly, I would make a solution but I don't really have time to implement it right now because we have a mostly working solution even if we have to deal with the optionsetvalue class.
public enum entityname_optionsetname
{
Value = 200
}
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("myprefix_fieldname")]
public entityname_optionsetname myprefix_FieldName
{
get
{
Microsoft.Xrm.Sdk.OptionSetValue optionSet = this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("myprefix_fieldname");
if ((optionSet != null))
{
return ((entityname_optionsetname)(System.Enum.ToObject(typeof(Microsoft.Xrm.Sdk.OptionSetValue), optionSet.Value)));
}
else
{
return null;
}
}
set
{
this.OnPropertyChanging("myprefix_FieldName");
if ((value == null))
{
this.SetAttributeValue("myprefix_fieldname", null);
}
else
{
this.SetAttributeValue("myprefix_fieldname", new Microsoft.Xrm.Sdk.OptionSetValue(((int)(value))));
}
this.OnPropertyChanged("myprefix_FieldName");
}
}
I finally am able to generate early bound class with a filtered set of entities and error free option set. I found the bulk of my answer through this thread, so thanks guys. The problem though is it's difficult to compile all of the various suggestions into something that actually... compiles. So I thought I'd post my final solution for the benefit of others, here's what worked for me.
I used Erik Pool's, Manny Grewal's, and Peter Majeed's solution for outputting only distinct enums with proper values, then combined that with Gaurav Dalal's solution (updated by JFK007 to fix the cast error) to re-write the SetAttributeValue that caused the (int)(value) error. And as an added bonus, I used the same solution for filtering distinct option sets to also filter for distinct option set values (which was an issue in my org).
The result is a class library containing CodeWriterFilter and CustomizeCodeDomService, the cmd batch file to run CrmSvcUtil.exe, and the filter.xml to filter the entities.
In your class library add references to CrmSvcUtil.exe, Microsoft.Xrm.Sdk, and System.Runtime.Serialization then compile the dll and copy it to the same the folder as your CrmSvcUtil.exe. Use the command I've included to reference your new assembly and build the early bound class file.
CodeWriterFilter:
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
using System.Text.RegularExpressions;
using Microsoft.Xrm.Sdk;
namespace SvcUtilFilter
{
/// <summary>
/// CodeWriterFilter for CrmSvcUtil that reads list of entities from an xml file to
/// determine whether or not the entity class should be generated.
/// </summary>
public class CodeWriterFilter : ICodeWriterFilterService
{
//list of entity names to generate classes for.
private HashSet<string> _validEntities = new HashSet<string>();
//reference to the default service.
private ICodeWriterFilterService _defaultService = null;
//list of generated option sets, instantiated in the constructor
private List<string> GeneratedOptionSets;
//list of generated options, instantiated in the constructor
private List<string> GeneratedOptions;
/// <summary>
/// constructor
/// </summary>
/// <param name="defaultService">default implementation</param>
public CodeWriterFilter(ICodeWriterFilterService defaultService)
{
this._defaultService = defaultService;
this.GeneratedOptionSets = new List<string>();
this.GeneratedOptions = new List<string>();
LoadFilterData();
}
/// <summary>
/// loads the entity filter data from the filter.xml file
/// </summary>
private void LoadFilterData()
{
XElement xml = XElement.Load("filter.xml");
XElement entitiesElement = xml.Element("entities");
foreach (XElement entityElement in entitiesElement.Elements("entity")) {
_validEntities.Add(entityElement.Value.ToLowerInvariant());
}
}
/// <summary>
/// /Use filter entity list to determine if the entity class should be generated.
/// </summary>
public bool GenerateEntity(EntityMetadata entityMetadata, IServiceProvider services)
{
return (_validEntities.Contains(entityMetadata.LogicalName.ToLowerInvariant()));
}
//All other methods just use default implementation:
public bool GenerateAttribute(AttributeMetadata attributeMetadata, IServiceProvider services)
{
return _defaultService.GenerateAttribute(attributeMetadata, services);
}
public bool GenerateOption(OptionMetadata optionMetadata, IServiceProvider services)
{
//return _defaultService.GenerateOption(optionMetadata, services);
string label = optionMetadata.Label.UserLocalizedLabel.Label;
//remove spaces and special characters
label = Regex.Replace(label, #"[^a-zA-Z0-9]", string.Empty);
if (label.Length > 0 && !char.IsLetter(label, 0)) {
label = "Number_" + label;
}
else if (label.Length == 0) {
label = "empty";
}
if (!GeneratedOptions.Exists(l=>l.Equals(label))) {
GeneratedOptions.Add(label);
optionMetadata.Label = new Label(label, 1033);
return _defaultService.GenerateOption(optionMetadata, services);
}
else { return false; }
}
public bool GenerateOptionSet(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
{
//return _defaultService.GenerateOptionSet(optionSetMetadata, services);
if (!GeneratedOptionSets.Contains(optionSetMetadata.Name)) {
GeneratedOptionSets.Add(optionSetMetadata.Name);
return true;
}
return _defaultService.GenerateOptionSet(optionSetMetadata, services);
}
public bool GenerateRelationship(RelationshipMetadataBase relationshipMetadata, EntityMetadata otherEntityMetadata, IServiceProvider services)
{
return _defaultService.GenerateRelationship(relationshipMetadata, otherEntityMetadata, services);
}
public bool GenerateServiceContext(IServiceProvider services)
{
return _defaultService.GenerateServiceContext(services);
}
}
}
CustomizeCodeDomService:
using System;
using System.CodeDom;
using System.Diagnostics;
using System.Linq;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
namespace SvcUtilFilter
{
/// <summary>
/// The customize code dom service.
/// </summary>
public sealed class CustomizeCodeDomService : ICustomizeCodeDomService
{
#region Constants and Fields
/// <summary>
/// The metadata.
/// </summary>
private IOrganizationMetadata metadata;
#endregion
#region Properties
#endregion
#region Public Methods
/// <summary>
/// The customize code dom.
/// </summary>
/// <param name="codeCompileUnit">
/// The code compile unit.
/// </param>
/// <param name="services">
/// The services.
/// </param>
public void CustomizeCodeDom(CodeCompileUnit codeCompileUnit, IServiceProvider services)
{
// Locate the namespace to use
CodeNamespace codeNamespace = codeCompileUnit.Namespaces[0];
var metadataProviderService = (IMetadataProviderService)services.GetService(typeof(IMetadataProviderService));
var filterService = (ICodeWriterFilterService)services.GetService(typeof(ICodeWriterFilterService));
this.metadata = metadataProviderService.LoadMetadata();
foreach (EntityMetadata entityMetadata in this.metadata.Entities) {
if (filterService.GenerateEntity(entityMetadata, services)) {
CodeTypeDeclaration entityClass =
codeNamespace.Types.Cast<CodeTypeDeclaration>().First(codeType => codeType.Name.ToUpper() == entityMetadata.SchemaName.ToUpper());
UpdateEnumSetter(entityClass, entityMetadata);
}
}
}
#endregion
#region Private Methods
private static void UpdateEnumSetter(CodeTypeDeclaration entityClass, EntityMetadata entity)
{
foreach (var attributeMetadata in entity.Attributes.Where(attributeMetadata => String.IsNullOrWhiteSpace(attributeMetadata.AttributeOf))) {
AttributeMetadata currentMetadata = attributeMetadata;
foreach (CodeTypeMember codeMembers in entityClass.Members.Cast<CodeTypeMember>().Where(codeMembers => codeMembers.Name == currentMetadata.SchemaName)) {
CodeMemberProperty codeProperty = (CodeMemberProperty)codeMembers;
if (codeProperty.HasSet) {
if (attributeMetadata.AttributeType != null && (attributeMetadata.AttributeType.Value == AttributeTypeCode.Picklist || attributeMetadata.AttributeType.Value == AttributeTypeCode.Status)) {
if (codeProperty.SetStatements[1].GetType() == typeof(CodeConditionStatement)) {
((CodeConditionStatement)codeProperty.SetStatements[1]).FalseStatements[0] = new CodeSnippetStatement {
Value = String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName)
};
}
else {
codeProperty.SetStatements[1] = new CodeSnippetStatement(String.Format("this.SetAttributeValue(\"{0}\", new Microsoft.Xrm.Sdk.OptionSetValue(value.Value));", attributeMetadata.LogicalName));
}
}
}
}
}
}
#endregion
}
}
CrmSvcUtil_run.cmd Command Batch File:
#echo off
set url=https://[organization].api.crm.dynamics.com/XRMServices/2011/Organization.svc
echo.
echo Generating CrmSvcUtil Proxy class in output folder
echo.
CrmSvcUtil.exe /metadataproviderservice:"MetadataProvider.IfdMetadataProviderService,
MetadataProvider"
/url:https://[organization].api.crm.dynamics.com/XRMServices/2011/Organization.svc /out:Xrm.cs
/namespace:Xrm /serviceContextName:XrmServiceContext /serviceContextPrefix:Xrm
/u:[username] /p:[password]
/codewriterfilter:SvcUtilFilter.CodeWriterFilter,SvcUtilFilter
/codecustomization:SvcUtilFilter.CustomizeCodeDomService,SvcUtilFilter
echo.
pause
filter.xml
<filter>
<entities>
<entity>systemuser</entity>
<entity>team</entity>
<entity>role</entity>
<entity>businessunit</entity>
<entity>account</entity>
<entity>product</entity>
<entity>transactioncurrency</entity>
</entities>
</filter>
It looks like there was a bug in the crmsrvcutil that has since been fixed. My code for OptionSet properties now looks like this:
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("prioritycode")]
public Microsoft.Xrm.Sdk.OptionSetValue PriorityCode
{
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>("prioritycode");
}
set
{
this.OnPropertyChanging("PriorityCode");
this.SetAttributeValue("prioritycode", value);
this.OnPropertyChanged("PriorityCode");
}
}
And I get no error setting the OptionSetValue...

Generic function for building Dynamic Linq query of string.contains against tokenized string

I am using Expression.And and Expression.Or to build dynamic linq queries. When the property/field being queried is a string, and the string contains spaces I would like to tokenize the string on the spaces and create an "And'd" sub query on the tokens.
Here is what I mean in a non generic fashion
var tokens = Code.Split(new []{" "}, StringSplitOptions.RemoveEmptyEntries);
var index = 0;
var firstToken = tokens[index ++];
Expression<Func<Entity, bool>> subQuery =
entity => entity.Code.Contains(firstToken);
for (; index < tokens.Length; index ++)
{
var tempToken = tokens[index];
subQuery = subQuery.And(entity => entity.Code.Contains(tempToken));
}
query = query.Or(subQuery);
What I'd like to do is find a way of writing a method which is generic enough to just call for example:
PredicateBuilder.BuildTokenizedStringQuery<Entity>(
tokens, entity => entity.Code);
and I end up with the same result. The following is where I'm at but I can't use the Func stringProp accessor in and Expression. I have to somehow combine an accessor expression (of the string property) with an invocation expression (that invokes string.Contains)
private Expression<Func<T, bool>> BuildTokenizedStringQuery<T>(string[] tokens,
Func<T, string> stringProp)
{
var index = 0;
var firstToken = tokens[index++];
Expression<Func<T, bool>> subQuery = entity =>
stringProp(entity).Contains(firstToken);
for (; index < tokens.Length; index++)
{
var tempToken = tokens[index];
subQuery = subQuery.And(
entity => stringProp(entity).Contains(tempToken));
}
return subQuery;
}
I'd also be interested to hear if this all looks like a bad idea.
Here's what I use to do this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
namespace MyLibrary.Extensions
{
/// <summary>Defines extension methods for building and working with Expressions.</summary>
public static class ExpressionExtensions
{
/// <summary>Ands the Expressions.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expressions">The Expression(s) to and.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> And<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
{
if (expressions.IsNullOrEmpty())
return null;
Expression<Func<T, bool>> finalExpression = expressions.First();
foreach (Expression<Func<T, bool>> e in expressions.Skip(1))
finalExpression = finalExpression.And(e);
return finalExpression;
}
/// <summary>Ors the Expressions.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expressions">The Expression(s) to or.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> Or<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
{
if (expressions.IsNullOrEmpty())
return null;
Expression<Func<T, bool>> finalExpression = expressions.First();
foreach (Expression<Func<T, bool>> e in expressions.Skip(1))
finalExpression = finalExpression.Or(e);
return finalExpression;
}
/// <summary>Ands the Expression with the provided Expression.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expression1">The left Expression to and.</param>
/// <param name="expression2">The right Expression to and.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
//Reuse the first expression's parameter
ParameterExpression param = expression1.Parameters.Single();
Expression left = expression1.Body;
Expression right = RebindParameter(expression2.Body, expression2.Parameters.Single(), param);
BinaryExpression body = Expression.AndAlso(left, right);
return Expression.Lambda<Func<T, bool>>(body, param);
}
/// <summary>Ors the Expression with the provided Expression.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expression1">The left Expression to or.</param>
/// <param name="expression2">The right Expression to or.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
//Reuse the first expression's parameter
ParameterExpression param = expression1.Parameters.Single();
Expression left = expression1.Body;
Expression right = RebindParameter(expression2.Body, expression2.Parameters.Single(), param);
BinaryExpression body = Expression.OrElse(left, right);
return Expression.Lambda<Func<T, bool>>(body, param);
}
/// <summary>Updates the supplied expression using the appropriate parameter.</summary>
/// <param name="expression">The expression to update.</param>
/// <param name="oldParameter">The original parameter of the expression.</param>
/// <param name="newParameter">The target parameter of the expression.</param>
/// <returns>The updated expression.</returns>
private static Expression RebindParameter(Expression expression, ParameterExpression oldParameter, ParameterExpression newParameter)
{
if (expression == null)
return null;
switch (expression.NodeType)
{
case ExpressionType.Parameter:
{
ParameterExpression parameterExpression = (ParameterExpression)expression;
return (parameterExpression.Name == oldParameter.Name ? newParameter : parameterExpression);
}
case ExpressionType.MemberAccess:
{
MemberExpression memberExpression = (MemberExpression)expression;
return memberExpression.Update(RebindParameter(memberExpression.Expression, oldParameter, newParameter));
}
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
{
BinaryExpression binaryExpression = (BinaryExpression)expression;
return binaryExpression.Update(RebindParameter(binaryExpression.Left, oldParameter, newParameter), binaryExpression.Conversion, RebindParameter(binaryExpression.Right, oldParameter, newParameter));
}
case ExpressionType.Call:
{
MethodCallExpression methodCallExpression = (MethodCallExpression)expression;
return methodCallExpression.Update(RebindParameter(methodCallExpression.Object, oldParameter, newParameter), methodCallExpression.Arguments.Select(arg => RebindParameter(arg, oldParameter, newParameter)));
}
case ExpressionType.Invoke:
{
InvocationExpression invocationExpression = (InvocationExpression)expression;
return invocationExpression.Update(RebindParameter(invocationExpression.Expression, oldParameter, newParameter), invocationExpression.Arguments.Select(arg => RebindParameter(arg, oldParameter, newParameter)));
}
default:
{
return expression;
}
}
}
public static Expression<Func<T, bool>> BuildContainsExpression<T, R>(Expression<Func<T, R>> valueSelector, IEnumerable<R> values)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
if (null == values)
throw new ArgumentNullException("values");
ParameterExpression parameterExpression = valueSelector.Parameters.Single();
IEnumerable<BinaryExpression> equalExpressions = null;
Expression aggregationExpression = null;
if (!values.IsNullOrEmpty())
return (e => false);
equalExpressions = values.Select(v => Expression.Equal(valueSelector.Body, Expression.Constant(v, typeof(R))));
aggregationExpression = equalExpressions.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<T, bool>>(aggregationExpression, parameterExpression);
}
public static Expression<Func<T, bool>> BuildDoesNotContainExpression<T, R>(Expression<Func<T, R>> valueSelector, IEnumerable<R> values)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
ParameterExpression parameterExpression = valueSelector.Parameters.Single();
IEnumerable<BinaryExpression> notEqualExpressions = null;
Expression aggregationExpression = null;
if (!values.IsNullOrEmpty())
return (e => false);
notEqualExpressions = values.Select(v => Expression.NotEqual(valueSelector.Body, Expression.Constant(v, typeof(R))));
aggregationExpression = notEqualExpressions.Aggregate<Expression>((accumulate, equal) => Expression.And(accumulate, equal));
return Expression.Lambda<Func<T, bool>>(aggregationExpression, parameterExpression);
}
}
}
Usage
string query = "kill mockingbird";
string[] tokens = query.Split(' ');
Expression<Func<Book, string>> inClause = BuildContainsExpression<Book, string>(o => o.Title, tokens);
using (LibraryDataContext dataContext = new LibraryDataContext())
{
List<Book> matchingBooks = dataContext.Books.Where(inClause).ToList();
}
Results
This will find all books whose title contains the words "kill" or "mockingbird."
The answer Josh provided is awesome and helped me to get exactly what I want. It however tests for equality of each token (it is also more generic as equality can be tested against any type) as opposed to a string.Contains test. Here is a solution that gives a string.Contains result:
public static Expression<Func<T, bool>>
BuildTokenizedStringQuery<T>(string[] tokens,
Expression<Func<T, string>> stringPropertyAccessor)
{
ParameterExpression parameterExpression = stringPropertyAccessor.Parameters
.Single();
var index = 0;
var firstToken = tokens[index ++];
Expression<Func<string, bool>> contains =
aString => aString.Contains(firstToken);
var invocation = Expression.Invoke(contains, stringPropertyAccessor.Body);
Expression<Func<T, bool>> expression = Expression
.Lambda<Func<T, bool>>(invocation, parameterExpression);
for (; index < tokens.Length; index++)
{
var tempToken = tokens[index];
contains = aString => aString.Contains(tempToken);
invocation = Expression.Invoke(contains, stringPropertyAccessor.Body);
expression = expression.And(Expression
.Lambda<Func<T, bool>>(invocation, parameterExpression));
}
return expression;
}

Pass a lambda expression in place of IComparer or IEqualityComparer or any single-method interface?

I happened to have seen some code where this guy passed a lambda expression to a ArrayList.Sort(IComparer here) or a IEnumerable.SequenceEqual(IEnumerable list, IEqualityComparer here) where an IComparer or an IEqualityComparer was expected.
I can't be sure if I saw it though, or I am just dreaming. And I can't seem to find an extension on any of these collections that accepts a Func<> or a delegate in their method signatures.
Is there such an overload/extension method? Or, if not, is it possible to muck around like this and pass an algorithm (read delegate) where a single-method interface is expected?
Update
Thanks, everyone. That's what I thought. I must've been dreaming. I know how to write a conversion. I just wasn't sure if I'd seen something like that or just thought I'd seen it.
Yet another update
Look, here, I found one such instance. I wasn't dreaming after all. Look at what this guy is doing here. What gives?
And here's another update:
Ok, I get it. The guy's using the Comparison<T> overload. Nice. Nice, but totally prone to mislead you. Nice, though. Thanks.
I'm not much sure what useful it really is, as I think for most cases in the Base Library expecting an IComparer there's an overload that expects a Comparison... but just for the record:
in .Net 4.5 they've added a method to obtain an IComparer from a Comparison:
Comparer.Create
so you can pass your lambda to it and obtain an IComparer.
I was also googling the web for a solution, but i didn't found any satisfying one. So i've created a generic EqualityComparerFactory:
using System;
using System.Collections.Generic;
/// <summary>
/// Utility class for creating <see cref="IEqualityComparer{T}"/> instances
/// from Lambda expressions.
/// </summary>
public static class EqualityComparerFactory
{
/// <summary>Creates the specified <see cref="IEqualityComparer{T}" />.</summary>
/// <typeparam name="T">The type to compare.</typeparam>
/// <param name="getHashCode">The get hash code delegate.</param>
/// <param name="equals">The equals delegate.</param>
/// <returns>An instance of <see cref="IEqualityComparer{T}" />.</returns>
public static IEqualityComparer<T> Create<T>(
Func<T, int> getHashCode,
Func<T, T, bool> equals)
{
if (getHashCode == null)
{
throw new ArgumentNullException(nameof(getHashCode));
}
if (equals == null)
{
throw new ArgumentNullException(nameof(equals));
}
return new Comparer<T>(getHashCode, equals);
}
private class Comparer<T> : IEqualityComparer<T>
{
private readonly Func<T, int> _getHashCode;
private readonly Func<T, T, bool> _equals;
public Comparer(Func<T, int> getHashCode, Func<T, T, bool> equals)
{
_getHashCode = getHashCode;
_equals = equals;
}
public bool Equals(T x, T y) => _equals(x, y);
public int GetHashCode(T obj) => _getHashCode(obj);
}
}
The idea is, that the CreateComparer method takes two arguments: a delegate to GetHashCode(T) and a delegate to Equals(T,T)
Example:
class Person
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
class Program
{
static void Main(string[] args)
{
var list1 = new List<Person>(new[]{
new Person { Id = 1, FirstName = "Walter", LastName = "White" },
new Person { Id = 2, FirstName = "Jesse", LastName = "Pinkman" },
new Person { Id = 3, FirstName = "Skyler", LastName = "White" },
new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" },
});
var list2 = new List<Person>(new[]{
new Person { Id = 1, FirstName = "Walter", LastName = "White" },
new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" },
});
// We're comparing based on the Id property
var comparer = EqualityComparerFactory.Create<Person>(
a => a.Id.GetHashCode(),
(a, b) => a.Id==b.Id);
var intersection = list1.Intersect(list2, comparer).ToList();
}
}
You can provide a lambda for a Array.Sort method, as it requires a method that accepts two objects of type T and returns an integer. As such, you could provide a lambda of the following definition (a, b) => a.CompareTo(b). An example to do a descending sort of an integer array:
int[] array = { 1, 8, 19, 4 };
// descending sort
Array.Sort(array, (a, b) => -1 * a.CompareTo(b));
public class Comparer2<T, TKey> : IComparer<T>, IEqualityComparer<T>
{
private readonly Expression<Func<T, TKey>> _KeyExpr;
private readonly Func<T, TKey> _CompiledFunc
// Constructor
public Comparer2(Expression<Func<T, TKey>> getKey)
{
_KeyExpr = getKey;
_CompiledFunc = _KeyExpr.Compile();
}
public int Compare(T obj1, T obj2)
{
return Comparer<TKey>.Default.Compare(_CompiledFunc(obj1), _CompiledFunc(obj2));
}
public bool Equals(T obj1, T obj2)
{
return EqualityComparer<TKey>.Default.Equals(_CompiledFunc(obj1), _CompiledFunc(obj2));
}
public int GetHashCode(T obj)
{
return EqualityComparer<TKey>.Default.GetHashCode(_CompiledFunc(obj));
}
}
use it like this
ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
You can't pass it directly however you could do so by defining a LambdaComparer class that excepts a Func<T,T,int> and then uses that in it's CompareTo.
It is not quite as concise but you could make it shorter through some creative extension methods on Func.
These methods don't have overloads that accept a delegate instead of an interface, but:
You can normally return a simpler sort key through the delegate you pass to Enumerable.OrderBy
Likewise, you could call Enumerable.Select before calling Enumerable.SequenceEqual
It should be straightforward to write a wrapper that implements IEqualityComparer<T> in terms of Func<T, T, bool>
F# lets you implement this sort of interface in terms of a lambda :)
I vote for the dreaming theory.
You can't pass a function where an object is expected: derivatives of System.Delegate (which is what lambdas are) don't implement those interfaces.
What you probably saw is a use of the of the Converter<TInput, TOutput> delegate, which can be modeled by a lambda. Array.ConvertAll uses an instance of this delegate.
In case if you need this function for use with lambda and possibly two different element types:
static class IEnumerableExtensions
{
public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> comparer)
{
if (first == null)
throw new NullReferenceException("first");
if (second == null)
throw new NullReferenceException("second");
using (IEnumerator<T1> e1 = first.GetEnumerator())
using (IEnumerator<T2> e2 = second.GetEnumerator())
{
while (e1.MoveNext())
{
if (!(e2.MoveNext() && comparer(e1.Current, e2.Current)))
return false;
}
if (e2.MoveNext())
return false;
}
return true;
}
}

Resources