(See below solution I created using the answer I accepted)
I'm trying to improve the maintainability of some code involving reflection. The app has a .NET Remoting interface exposing (among other things) a method called Execute for accessing parts of the app not included in its published remote interface.
Here is how the app designates properties (a static one in this example) which are meant to be accessible via Execute:
RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");
So a remote user could call:
string response = remoteObject.Execute("SomeSecret");
and the app would use reflection to find SomeClass.SomeProperty and return its value as a string.
Unfortunately, if someone renames SomeProperty and forgets to change the 3rd parm of ExposeProperty(), it breaks this mechanism.
I need to the equivalent of:
SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()
to use as the 3rd parm in ExposeProperty so refactoring tools would take care of renames.
Is there a way to do this?
Okay, here's what I ended up creating (based upon the answer I selected and the question he referenced):
// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
return me.Member.Name;
}
Usage:
// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);
// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);
Now with this cool capability, it's time to simplify the ExposeProperty method. Polishing doorknobs is dangerous work...
With C# 6.0, this is now a non-issue as you can do:
nameof(SomeProperty)
This expression is resolved at compile-time to "SomeProperty".
MSDN documentation of nameof.
Using GetMemberInfo from here: Retrieving Property name from lambda expression you can do something like this:
RemoteMgr.ExposeProperty(() => SomeClass.SomeProperty)
public class SomeClass
{
public static string SomeProperty
{
get { return "Foo"; }
}
}
public class RemoteMgr
{
public static void ExposeProperty<T>(Expression<Func<T>> property)
{
var expression = GetMemberInfo(property);
string path = string.Concat(expression.Member.DeclaringType.FullName,
".", expression.Member.Name);
// Do ExposeProperty work here...
}
}
public class Program
{
public static void Main()
{
RemoteMgr.ExposeProperty("SomeSecret", () => SomeClass.SomeProperty);
}
}
Okay, here's what I ended up creating (based upon the answer I selected and the question he referenced):
// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
return me.Member.Name;
}
Usage:
// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);
// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);
There's a well-known hack to extract it from lambda expression (this is from the PropertyObserver class, by Josh Smith, in his MVVM foundation):
private static string GetPropertyName<TPropertySource>
(Expression<Func<TPropertySource, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
Debug.Assert(memberExpression != null,
"Please provide a lambda expression like 'n => n.PropertyName'");
if (memberExpression != null)
{
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
return null;
}
Sorry, this was missing some context. This was part of a larger class where TPropertySource is the class containing the property. You could make the function generic in TPropertySource to extract it from the class. I recommend taking a look at the full code from the MVVM Foundation.
The PropertyInfo class should help you achieve this, if I understand correctly.
Type.GetProperties() method
PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
propInfos.ToList().ForEach(p =>
Console.WriteLine(string.Format("Property name: {0}", p.Name));
Is this what you need?
You can use Reflection to obtain the actual names of the properties.
http://www.csharp-examples.net/reflection-property-names/
If you need a way to assign a "String Name" to a property, why don't you write an attribute that you can reflect over to get the string name?
[StringName("MyStringName")]
private string MyProperty
{
get { ... }
}
I modified your solution to chain over multiple properties:
public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
MemberExpression me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
string result = string.Empty;
do
{
result = me.Member.Name + "." + result;
me = me.Expression as MemberExpression;
} while (me != null);
result = result.Remove(result.Length - 1); // remove the trailing "."
return result;
}
Usage:
string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"
Based on the answer which is already in the question and on this article: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ I am presenting my solution to this problem:
public static class PropertyNameHelper
{
/// <summary>
/// A static method to get the Propertyname String of a Property
/// It eliminates the need for "Magic Strings" and assures type safety when renaming properties.
/// See: http://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string
/// </summary>
/// <example>
/// // Static Property
/// string name = PropertyNameHelper.GetPropertyName(() => SomeClass.SomeProperty);
/// // Instance Property
/// string name = PropertyNameHelper.GetPropertyName(() => someObject.SomeProperty);
/// </example>
/// <typeparam name="T"></typeparam>
/// <param name="propertyLambda"></param>
/// <returns></returns>
public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{
throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
}
return me.Member.Name;
}
/// <summary>
/// Another way to get Instance Property names as strings.
/// With this method you don't need to create a instance first.
/// See the example.
/// See: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
/// </summary>
/// <example>
/// string name = PropertyNameHelper((Firma f) => f.Firmenumsatz_Waehrung);
/// </example>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TReturn"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
public static string GetPropertyName<T, TReturn>(Expression<Func<T, TReturn>> expression)
{
MemberExpression body = (MemberExpression)expression.Body;
return body.Member.Name;
}
}
And a Test which also shows the usage for instance and static properties:
[TestClass]
public class PropertyNameHelperTest
{
private class TestClass
{
public static string StaticString { get; set; }
public string InstanceString { get; set; }
}
[TestMethod]
public void TestGetPropertyName()
{
Assert.AreEqual("StaticString", PropertyNameHelper.GetPropertyName(() => TestClass.StaticString));
Assert.AreEqual("InstanceString", PropertyNameHelper.GetPropertyName((TestClass t) => t.InstanceString));
}
}
Old question, but another answer to this question is to create a static function in a helper class that uses the CallerMemberNameAttribute.
public static string GetPropertyName([CallerMemberName] String propertyName = null) {
return propertyName;
}
And then use it like:
public string MyProperty {
get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
You can use the StackTrace class to get the name of the current function, (or if you put the code in a function, then step down a level and get the calling function).
See http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx
I've been using this answer to great effect: Get the property, as a string, from an Expression<Func<TModel,TProperty>>
I realize I already answered this question a while back. The only advantage my other answer has is that it works for static properties. I find the syntax in this answer much more useful because you don't have to create a variable of the type you want to reflect.
I had some difficulty using the solutions already suggested for my specific use case, but figured it out eventually. I don't think my specific case is worthy of a new question, so I am posting my solution here for reference. (This is very closely related to the question and provides a solution for anyone else with a similar case to mine).
The code I ended up with looks like this:
public class HideableControl<T>: Control where T: class
{
private string _propertyName;
private PropertyInfo _propertyInfo;
public string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
_propertyInfo = typeof(T).GetProperty(value);
}
}
protected override bool GetIsVisible(IRenderContext context)
{
if (_propertyInfo == null)
return false;
var model = context.Get<T>();
if (model == null)
return false;
return (bool)_propertyInfo.GetValue(model, null);
}
protected void SetIsVisibleProperty(Expression<Func<T, bool>> propertyLambda)
{
var expression = propertyLambda.Body as MemberExpression;
if (expression == null)
throw new ArgumentException("You must pass a lambda of the form: 'vm => vm.Property'");
PropertyName = expression.Member.Name;
}
}
public interface ICompanyViewModel
{
string CompanyName { get; }
bool IsVisible { get; }
}
public class CompanyControl: HideableControl<ICompanyViewModel>
{
public CompanyControl()
{
SetIsVisibleProperty(vm => vm.IsVisible);
}
}
The important part for me is that in the CompanyControl class the compiler will only allow me to choose a boolean property of ICompanyViewModel which makes it easier for other developers to get it right.
The main difference between my solution and the accepted answer is that my class is generic and I only want to match properties from the generic type that are boolean.
it's how I implemented it , the reason behind is if the class that you want to get the name from it's member is not static then you need to create an instanse of that and then get the member's name. so generic here comes to help
public static string GetName<TClass>(Expression<Func<TClass, object>> exp)
{
MemberExpression body = exp.Body as MemberExpression;
if (body == null)
{
UnaryExpression ubody = (UnaryExpression)exp.Body;
body = ubody.Operand as MemberExpression;
}
return body.Member.Name;
}
the usage is like this
var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
Related
Is there any way to automatically get notified of property changes in a class without having to write OnPropertyChanged in every setter? (I have hundreds of properties that I want to know if they have changed).
Anton suggests dynamic proxies. I've actually used the "Castle" library for something similar in the past, and while it does reduce the amount of code I've had to write, it added around 30 seconds to my program startup time (ymmv) - because it's a runtime solution.
I'm wondering if there is a compile time solution, maybe using compile-time attributes...
Slashene and TcKs give suggestions which generates repetitive code - unfortunately, not all my properties are a simple case of m_Value = value - lots of them have custom code in the setters, so cookie-cutter code from snippets and xml aren't really feasible for my project either.
EDIT: The author of NotifyPropertyWeaver has deprecated the tool in favor of the more general Fody. (A migration guide for people moving from weaver to fody is available.)
A very convenient tool I've used for my projects is Notify Property Weaver Fody.
It installs itself as a build step in your projects and during compilation injects code that raises the PropertyChanged event.
Making properties raise PropertyChanged is done by putting special attributes on them:
[ImplementPropertyChanged]
public string MyProperty { get; set; }
As a bonus, you can also specify relationships for properties that depend on other properties
[ImplementPropertyChanged]
public double Radius { get; set; }
[DependsOn("Radius")]
public double Area
{
get { return Radius * Radius * Math.PI; }
}
The nameof operator was implemented in C# 6.0 with .NET 4.6 and VS2015 in July 2015. The following is still valid for C# < 6.0
We use the code below (From http://www.ingebrigtsen.info/post/2008/12/11/INotifyPropertyChanged-revisited.aspx). Works great :)
public static class NotificationExtensions
{
#region Delegates
/// <summary>
/// A property changed handler without the property name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sender">The object that raised the event.</param>
public delegate void PropertyChangedHandler<TSender>(TSender sender);
#endregion
/// <summary>
/// Notifies listeners about a change.
/// </summary>
/// <param name="EventHandler">The event to raise.</param>
/// <param name="Property">The property that changed.</param>
public static void Notify(this PropertyChangedEventHandler EventHandler, Expression<Func<object>> Property)
{
// Check for null
if (EventHandler == null)
return;
// Get property name
var lambda = Property as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
ConstantExpression constantExpression;
if (memberExpression.Expression is UnaryExpression)
{
var unaryExpression = memberExpression.Expression as UnaryExpression;
constantExpression = unaryExpression.Operand as ConstantExpression;
}
else
{
constantExpression = memberExpression.Expression as ConstantExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
// Invoke event
foreach (Delegate del in EventHandler.GetInvocationList())
{
del.DynamicInvoke(new[]
{
constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)
});
}
}
/// <summary>
/// Subscribe to changes in an object implementing INotifiyPropertyChanged.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ObjectThatNotifies">The object you are interested in.</param>
/// <param name="Property">The property you are interested in.</param>
/// <param name="Handler">The delegate that will handle the event.</param>
public static void SubscribeToChange<T>(this T ObjectThatNotifies, Expression<Func<object>> Property, PropertyChangedHandler<T> Handler) where T : INotifyPropertyChanged
{
// Add a new PropertyChangedEventHandler
ObjectThatNotifies.PropertyChanged += (s, e) =>
{
// Get name of Property
var lambda = Property as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
// Notify handler if PropertyName is the one we were interested in
if (e.PropertyName.Equals(propertyInfo.Name))
{
Handler(ObjectThatNotifies);
}
};
}
}
Used for example this way:
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
public string FirstName
{
get { return this._firstName; }
set
{
this._firstName = value;
this.PropertyChanged.Notify(()=>this.FirstName);
}
}
}
private void firstName_PropertyChanged(Employee sender)
{
Console.WriteLine(sender.FirstName);
}
employee = new Employee();
employee.SubscribeToChange(() => employee.FirstName, firstName_PropertyChanged);
Some syntax errors in the example may exist. Didn't test it. But you should have the concept there at least :)
EDIT: I see now that you may have wanted even less work, but yeah... the stuff above at least makes it a lot easier. And you prevent all the scary problems with refering to properties using strings.
The Framework 4.5 provides us with the CallerMemberNameAttribute, which makes passing the property name as a string unnecessary:
private string m_myProperty;
public string MyProperty
{
get { return m_myProperty; }
set
{
m_myProperty = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
// ... do stuff here ...
}
Similar to Svish's solution, just replacing lambda awesomeness with boring framework functionality ;-)
If you're working on Framework 4.0 with KB2468871 installed, you can install the Microsoft BCL Compatibility Pack via nuget, which also provides this attribute.
You can have extension method on your PropertyChanged delegate and use it like this:
public string Name
{
get { return name; }
set
{
name = value;
PropertyChanged.Raise(() => Name);
}
}
Subscription to a specific property change:
var obj = new Employee();
var handler = obj.SubscribeToPropertyChanged(
o => o.FirstName,
o => Console.WriteLine("FirstName is now '{0}'", o.FirstName));
obj.FirstName = "abc";
// Unsubscribe when required
obj.PropertyChanged -= handler;
extension method is able to determine sender and property name just by inspecting lambda expression tree and without major performance impact:
public static class PropertyChangedExtensions
{
public static void Raise<TProperty>(
this PropertyChangedEventHandler handler, Expression<Func<TProperty>> property)
{
if (handler == null)
return;
var memberExpr = (MemberExpression)property.Body;
var propertyName = memberExpr.Member.Name;
var sender = ((ConstantExpression)memberExpr.Expression).Value;
handler.Invoke(sender, new PropertyChangedEventArgs(propertyName));
}
public static PropertyChangedEventHandler SubscribeToPropertyChanged<T, TProperty>(
this T obj, Expression<Func<T, TProperty>> property, Action<T> handler)
where T : INotifyPropertyChanged
{
if (handler == null)
return null;
var memberExpr = (MemberExpression)property.Body;
var propertyName = memberExpr.Member.Name;
PropertyChangedEventHandler subscription = (sender, eventArgs) =>
{
if (propertyName == eventArgs.PropertyName)
handler(obj);
};
obj.PropertyChanged += subscription;
return subscription;
}
}
If PropertyChanged event is declared in a base type then it won't be visible as a delegate field in the derived classes. In this case a workaround is to declare a protected field of type PropertyChangedEventHandler and explicitly implement event's add and remove accessors:
public class Base : INotifyPropertyChanged
{
protected PropertyChangedEventHandler propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { propertyChanged += value; }
remove { propertyChanged -= value; }
}
}
public class Derived : Base
{
string name;
public string Name
{
get { return name; }
set
{
name = value;
propertyChanged.Raise(() => Name);
}
}
}
Implement a type safe INotifyPropertyChanged : See here
Then make your own code snippet :
private $Type$ _$PropertyName$;
public $Type$ $PropertyName$
{
get
{
return _$PropertyName$;
}
set
{
if(value != _$PropertyName$)
{
_$PropertyName$ = value;
OnPropertyChanged(o => o.$PropertyName$);
}
}
}
With Code snippet designer and you have done ! Easy, secure way to build your INotifyPropertyChanged.
I don't know no standard way, but I know two workarounds:
1) PostSharp can do it for you after the compilation. It is very usefull, but it take some time on every build.
2) Custom tool i Visual Studio. You can combine it with "partial class". Then you can create custom tool for your XML and you can generate source code from the xml.
For example this xml:
<type scope="public" type="class" name="MyClass">
<property scope="public" type="string" modifier="virtual" name="Text" notify="true" />
</type>
can be source for this code:
public partial class MyClass {
private string _text;
public virtual string Text {
get { return this._Text; }
set {
this.OnPropertyChanging( "Text" );
this._Text = value;
this.OnPropertyChanged( "Text" );
}
}
}
you might want to look into Aspect-Oriented Programming as a whole
Frameworks => you could look at linfu
You could look at Castle or Spring.NET and implementing interceptor functionality?
I have just found ActiveSharp - Automatic INotifyPropertyChanged, I have yet to use it, but it looks good.
To quote from it's web site...
Send property change notifications
without specifying property name as a
string.
Instead, write properties like this:
public int Foo
{
get { return _foo; }
set { SetValue(ref _foo, value); } // <-- no property name here
}
Note that there is no need to include the name of the property as a string. ActiveSharp reliably and correctly figures that out for itself. It works based on the fact that your property implementation passes the backing field (_foo) by ref. (ActiveSharp uses that "by ref" call to identify which backing field was passed, and from the field it identifies the property).
Amelioration to call event in children classes:
Called thanks to:
this.NotifyPropertyChange(() => PageIndex);
Add this in the NotificationExtensions class:
/// <summary>
/// <para>Lève l'évènement de changement de valeur sur l'objet <paramref name="sender"/>
/// pour la propriété utilisée dans la lambda <paramref name="property"/>.</para>
/// </summary>
/// <param name="sender">L'objet portant la propriété et l'évènement.</param>
/// <param name="property">Une expression lambda utilisant la propriété subissant la modification.</param>
public static void NotifyPropertyChange(this INotifyPropertyChanged sender, Expression<Func<Object>> property)
{
if (sender == null)
return;
// Récupère le nom de la propriété utilisée dans la lambda en argument
LambdaExpression lambda = property as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
UnaryExpression unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;
PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
// il faut remonter la hierarchie, car meme public, un event n est pas visible dans les enfants
FieldInfo eventField;
Type baseType = sender.GetType();
do
{
eventField = baseType.GetField(INotifyPropertyChangedEventFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
baseType = baseType.BaseType;
} while (eventField == null);
// on a trouvé l'event, on peut invoquer tt les delegates liés
MulticastDelegate eventDelegate = eventField.GetValue(sender) as MulticastDelegate;
if (eventDelegate == null) return; // l'event n'est bindé à aucun delegate
foreach (Delegate handler in eventDelegate.GetInvocationList())
{
handler.Method.Invoke(handler.Target, new Object[] { sender, new PropertyChangedEventArgs(propertyInfo.Name) });
}
}
Just to make the implementation quicker you can use a snippet
From http://aaron-hoffman.blogspot.it/2010/09/visual-studio-code-snippet-for-notify.html
the ViewModel classes of projects following the M-V-VM pattern it is often necessary to raise a "PropertyChanged" event (to assist with INotifyPropertyChanged interface implementation) from within a property's setter. This is a tedious task that will hopefully someday be solved by using the Compiler as a Service...
Snippet core (for which full credit goes to the author, who is not me) is the following
<Code Language= "csharp ">
<![CDATA[public $type$ $property$
{
get { return _$property$; }
set
{
if (_$property$ != value)
{
_$property$ = value;
OnPropertyChanged($property$PropertyName);
}
}
}
private $type$ _$property$;
public const string $property$PropertyName = "$property$";$end$]]>
</Code>
There is no Single implementation of Property Changed that can handle every way that people want to use it. best bet is to generate a helper class to do the work for you
here is an example of one i use
/// <summary>
/// Helper Class that automates most of the actions required to implement INotifyPropertyChanged
/// </summary>
public static class HPropertyChanged
{
private static Dictionary<string, PropertyChangedEventArgs> argslookup = new Dictionary<string, PropertyChangedEventArgs>();
public static string ThisPropertyName([CallerMemberName]string name = "")
{
return name;
}
public static string GetPropertyName<T>(Expression<Func<T>> exp)
{
string rtn = "";
MemberExpression mex = exp.Body as MemberExpression;
if(mex!=null)
rtn = mex.Member.Name;
return rtn;
}
public static void SetValue<T>(ref T target, T newVal, object sender, PropertyChangedEventHandler handler, params string[] changed)
{
if (!target.Equals(newVal))
{
target = newVal;
PropertyChanged(sender, handler, changed);
}
}
public static void SetValue<T>(ref T target, T newVal, Action<PropertyChangedEventArgs> handler, params string[] changed)
{
if (!target.Equals(newVal))
{
target = newVal;
foreach (var item in changed)
{
handler(GetArg(item));
}
}
}
public static void PropertyChanged(object sender,PropertyChangedEventHandler handler,params string[] changed)
{
if (handler!=null)
{
foreach (var prop in changed)
{
handler(sender, GetArg(prop));
}
}
}
public static PropertyChangedEventArgs GetArg(string name)
{
if (!argslookup.ContainsKey(name)) argslookup.Add(name, new PropertyChangedEventArgs(name));
return argslookup[name];
}
}
edit:
it was suggested that i shift from a helper class to a value wrapper and i've since been using this one and i find it works quite well
public class NotifyValue<T>
{
public static implicit operator T(NotifyValue<T> item)
{
return item.Value;
}
public NotifyValue(object parent, T value = default(T), PropertyChangingEventHandler changing = null, PropertyChangedEventHandler changed = null, params object[] dependenies)
{
_parent = parent;
_propertyChanged = changed;
_propertyChanging = changing;
if (_propertyChanged != null)
{
_propertyChangedArg =
dependenies.OfType<PropertyChangedEventArgs>()
.Union(
from d in dependenies.OfType<string>()
select new PropertyChangedEventArgs(d)
);
}
if (_propertyChanging != null)
{
_propertyChangingArg =
dependenies.OfType<PropertyChangingEventArgs>()
.Union(
from d in dependenies.OfType<string>()
select new PropertyChangingEventArgs(d)
);
}
_PostChangeActions = dependenies.OfType<Action>();
}
private T _Value;
public T Value
{
get { return _Value; }
set
{
SetValue(value);
}
}
public bool SetValue(T value)
{
if (!EqualityComparer<T>.Default.Equals(_Value, value))
{
OnPropertyChnaging();
_Value = value;
OnPropertyChnaged();
foreach (var action in _PostChangeActions)
{
action();
}
return true;
}
else
return false;
}
private void OnPropertyChnaged()
{
var handler = _propertyChanged;
if (handler != null)
{
foreach (var arg in _propertyChangedArg)
{
handler(_parent, arg);
}
}
}
private void OnPropertyChnaging()
{
var handler = _propertyChanging;
if(handler!=null)
{
foreach (var arg in _propertyChangingArg)
{
handler(_parent, arg);
}
}
}
private object _parent;
private PropertyChangedEventHandler _propertyChanged;
private PropertyChangingEventHandler _propertyChanging;
private IEnumerable<PropertyChangedEventArgs> _propertyChangedArg;
private IEnumerable<PropertyChangingEventArgs> _propertyChangingArg;
private IEnumerable<Action> _PostChangeActions;
}
example of use
private NotifyValue<int> _val;
public const string ValueProperty = "Value";
public int Value
{
get { return _val.Value; }
set { _val.Value = value; }
}
then in constructor you do
_val = new NotifyValue<int>(this,0,PropertyChanged,PropertyChanging,ValueProperty );
Just Use this attribute above your automatic property declaration
[NotifyParentProperty(true)]
public object YourProperty { get; set; }
I have a set of extension methods that allow for using magic strings in the LINQ OrderBy() methods. I know the first question will be why, but it's part of a generic repository and is there for flexibility so that strings can be sent from the UI and used directly.
I have it working if you pass in a magic string that represents a property on the main entity you are querying, but I'm having trouble making it more generic so it can handle multiple levels deep magic string. For example:
IQueryable<Contact> contacts = GetContacts();
contacts.OrderByProperty("Name"); // works great
// can't figure out how to handle this
contacts.OrderByProperty("ContactType.Name");
Here is the code that I have so far:
public static class LinqHelpers
{
private static readonly MethodInfo OrderByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderBy" && method.GetParameters().Length == 2);
private static readonly MethodInfo OrderByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2);
private static readonly MethodInfo ThenByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "ThenBy" && method.GetParameters().Length == 2);
private static readonly MethodInfo ThenByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "ThenByDescending" && method.GetParameters().Length == 2);
public static IOrderedQueryable<TSource> ApplyOrdering<TSource>(IQueryable<TSource> source, string propertyName, MethodInfo orderingMethod)
{
var parameter = Expression.Parameter(typeof(TSource), "x");
var orderByProperty = Expression.Property(parameter, propertyName);
var lambda = Expression.Lambda(orderByProperty, new[] { parameter });
var genericMethod = orderingMethod.MakeGenericMethod(new[] { typeof(TSource), orderByProperty.Type });
return (IOrderedQueryable<TSource>)genericMethod.Invoke(null, new object[] { source, lambda });
}
public static IOrderedQueryable<TSource> OrderByProperty<TSource>(this IQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, OrderByMethod);
}
public static IOrderedQueryable<TSource> OrderByDescendingProperty<TSource>(this IQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, OrderByDescendingMethod);
}
public static IOrderedQueryable<TSource> ThenByProperty<TSource>(this IOrderedQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, ThenByMethod);
}
public static IOrderedQueryable<TSource> ThenByDescendingProperty<TSource>(this IOrderedQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, ThenByDescendingMethod);
}
}
I'm pretty sure I need to split the propertyName on the period and then use those parts to build up a more complicated Expression that involves a MemberExpression and then a Property but I'm struggling. Any help or pointing in the right direction would be appreciated.
I wrote my own predicate builder type thing a while back. I attempted to adapt the code for posting here. This returns an expression to access a property, and can be used to build up more complicated expressions - just make sure that all the components of the expression use the exact same param object instance.
This won't work as a drop in for your code. It'll will require some slight adaptations to make it work for your use I think.
This outputs param => (param.Child.IntProperty == 42).
You could use the predicate variable in a where clause. Let's say you had a List<Parent> called parents, you could call parents.Where(predicate).
public class Parent {
public string StringProperty { get; set; }
public Child Child { get; set; }
}
public class Child {
public int IntProperty { get; set; }
}
internal class Program {
private static void Main(string[] args) {
var param = Expression.Parameter(typeof(Parent), "param");
var accessExpression = GetAccessExpression(param, "Child.IntProperty", typeof(Parent));
var constantExpression = Expression.Constant(42);
var condition = Expression.Equal(accessExpression, constantExpression);
var predicate = Expression.Lambda<Func<Parent, bool>>(condition, param);
Console.WriteLine(predicate.ToString());
}
/// <summary>
/// Returns an Expression that represents member access for the specified property on the specified type. Uses recursion
/// to find the full expression.
/// </summary>
/// <param name="property">The property path.</param>
/// <param name="type">The type that contains the first part of the property path.</param>
/// <returns></returns>
private static Expression GetAccessExpression(Expression param, string property, Type type) {
if (property == null)
throw new ArgumentNullException("property");
if (type == null)
throw new ArgumentNullException("type");
string[] propPath = property.Split('.');
var propInfo = type.GetProperty(propPath[0]);
if (propInfo == null)
throw new Exception(String.Format("Could not find property '{0}' on type {1}.", propPath[0], type.FullName));
var propAccess = Expression.MakeMemberAccess(param, type.GetProperty(propPath[0]));
if (propPath.Length > 1)
return GetAccessExpression(propAccess, string.Join(".", propPath, 1, propPath.Length - 1), type.GetProperty(propPath[0]).PropertyType);
else
return propAccess;
}
}
I'll start with some classes...
The Domain Entity:
public class Account
{
public int Id { get; set; }
public double Balance { get; set; }
public string CustomerName { get; set; }
}
The View Model:
public class AccountModel
{
public int Id { get; set; }
public double Bal { get; set; }
public string Name { get; set; }
}
The Repository:
My repository has a method on it that takes an expression and returns a list, like this:
public interface IAccountRepository
{
IEnumerable<Account> Query(Expression<Func<Account, bool>> expression);
}
The Problem
My application generates an Expression<Func<AccountModel, bool>> in the UI. I need to somehow convert or map the EXPRESSION from AccountModel to Account so that I can use it in my Query method. I say "map" because, if you notice, my model and domain objects are similar, but don't necessarily have the same property names.
How can this be done?
This sounds like a job for AutoMapper. Automapper allows you to map one class to another at one point in time and use this mapping configuration later on.
See the Projection page on the wiki for the kind of thing you are after.
Update As you are using Entity Framework, here is an update for remapping your expression from using AccountModel to Account.
In the CompositionRoot of your application, set up AutoMapper like so (ignore Code Contract statements if you do not use Code Contracts):
var accountModelMap = Mapper.CreateMap<AccountModel, Account>();
Contract.Assume(accountModelMap != null);
accountModelMap.ForMember(account => account.Id, expression => expression.MapFrom(model => model.Id));
accountModelMap.ForMember(account => account.Balance, expression => expression.MapFrom(model => model.Bal));
accountModelMap.ForMember(account => account.CustomerName, expression => expression.MapFrom(model => model.Name));
This configures how the two data types relate to eachother.
Implement an ExpressionVisitor to use AutoMapper to rebind member access from one type to another.
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly ParameterExpression _newParameter;
private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
Contract.Requires(newParameter != null);
_newParameter = newParameter;
Contract.Assume(_typeMap != null);
}
[ContractInvariantMethod]
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
private void ObjectInvariant()
{
Contract.Invariant(_typeMap != null);
Contract.Invariant(_newParameter != null);
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var propertyMaps = _typeMap.GetPropertyMaps();
Contract.Assume(propertyMaps != null);
// Find any mapping for this member
var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member);
if (propertyMap == null)
return base.VisitMember(node);
var destinationProperty = propertyMap.DestinationProperty;
Contract.Assume(destinationProperty != null);
var destinationMember = destinationProperty.MemberInfo;
Contract.Assume(destinationMember != null);
// Check the new member is a property too
var property = destinationMember as PropertyInfo;
if (property == null)
return base.VisitMember(node);
// Access the new property
var newPropertyAccess = Expression.Property(_newParameter, property);
return base.VisitMember(newPropertyAccess);
}
}
Then implement an extension method to make this easier to use:
/// <summary>
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
/// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam>
/// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns>
public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(this Expression<Func<TSource, TResult>> expression)
{
Contract.Requires(expression != null);
Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null);
var newParameter = Expression.Parameter(typeof (TDestination));
Contract.Assume(newParameter != null);
var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter);
var remappedBody = visitor.Visit(expression.Body);
if (remappedBody == null)
throw new InvalidOperationException("Unable to remap expression");
return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter);
}
}
This can subsequently be used like so (in an NUnit test):
[TestFixture]
public class RemappingTests
{
#region Setup/Teardown
/// <summary>
/// Sets up the variables before each test.
/// </summary>
[SetUp]
public void Setup()
{
var accountModelMap = Mapper.CreateMap<AccountModel, Account>();
Contract.Assume(accountModelMap != null);
accountModelMap.ForMember(account => account.Id, expression => expression.MapFrom(model => model.Id));
accountModelMap.ForMember(account => account.Balance, expression => expression.MapFrom(model => model.Bal));
accountModelMap.ForMember(account => account.CustomerName, expression => expression.MapFrom(model => model.Name));
}
[TearDown]
public void Teardown()
{
Mapper.Reset();
}
#endregion
/// <summary>
/// Checks that <see cref="ExpressionExtensions.RemapForType{TSource, TDestination, TResult}(Expression{Func{TSource, TResult}})"/> correctly remaps all property access for the new type.
/// </summary>
/// <param name="balance">The balance to use as the value for <see cref="Account.Balance"/>.</param>
/// <returns>Whether the <see cref="Account.Balance"/> was greater than 50.</returns>
[TestCase(0, Result = false)]
[TestCase(80, Result = true)]
public bool RemapperUsesPropertiesOfNewDataType(double balance)
{
Expression<Func<AccountModel, bool>> modelExpr = model => model.Bal > 50;
var accountExpr = modelExpr.RemapForType<AccountModel, Account, bool>();
var compiled = accountExpr.Compile();
Contract.Assume(compiled != null);
var hasBalance = compiled(new Account {Balance = balance});
return hasBalance;
}
}
In case that is too much code to find the exact call, here it is:
Expression<Func<AccountModel, bool>> modelExpr = model => model.Bal > 50;
var accountExpr = modelExpr.RemapForType<AccountModel, Account, bool>();
You can use an ExpressionVisitor to rewrite the Expression:
public class AccountModelRewriter : ExpressionVisitor
{
private Stack<ParameterExpression[]> _LambdaStack = new Stack<ParameterExpression[]>();
protected override Expression VisitLambda<T>(Expression<T> node)
{
var lambda = (LambdaExpression)node;
_LambdaStack.Push(
lambda.Parameters.Select(parameter => typeof(AccountModel) == parameter.Type ? Expression.Parameter(typeof(Account)) : parameter)
.ToArray()
);
lambda = Expression.Lambda(
this.Visit(lambda.Body),
_LambdaStack.Pop()
);
return lambda;
}
protected override Expression VisitMember(MemberExpression node)
{
var memberExpression = (MemberExpression)node;
var declaringType = memberExpression.Member.DeclaringType;
var propertyName = memberExpression.Member.Name;
if (typeof(AccountModel) == declaringType)
{
switch (propertyName)
{
case "Bal" :
propertyName = "Balance";
break;
case "Name" :
propertyName = "CustomerName";
break;
}
memberExpression = Expression.Property(
this.Visit(memberExpression.Expression),
typeof(Account).GetProperty(propertyName)
);
}
return memberExpression;
}
protected override Expression VisitParameter(ParameterExpression node)
{
node = (ParameterExpression)base.VisitParameter(node);
if (typeof(AccountModel) == node.Type)
{
node = this._LambdaStack.Peek().Single(parameter => parameter.Type == typeof(Account));
}
return node;
}
}
This visitor switches the input parameters from type AccountModel to Account (that's the VisitLambda and VisitParameter methods), and also changes all property accessors to use this new parameter as well as switching the property names if appropriate (that's the VisitMember part).
Usage is as follows:
Expression<Func<AccountModel, bool>> accountModelQuery = a => a.Bal == 0 && a.Name != null && a.Id != 7;
var accountQuery = (Expression<Func<Account, bool>>)new AccountModelRewriter().Visit(accountModelQuery);
I have a db table that stores the following:
RuleID objectProperty ComparisonOperator TargetValue
1 age 'greater_than' 15
2 username 'equal' 'some_name'
3 tags 'hasAtLeastOne' 'some_tag some_tag2'
Now say I have a collection of these rules:
List<Rule> rules = db.GetRules();
Now I have an instance of a user also:
User user = db.GetUser(....);
How would I loop through these rules, and apply the logic and perform the comparisons etc?
if(user.age > 15)
if(user.username == "some_name")
Since the object's property like 'age' or 'user_name' is stored in the table, along with the comparison operater 'great_than' and 'equal', how could I possible do this?
C# is a statically typed language, so not sure how to go forward.
This snippet compiles the Rules into fast executable code (using Expression trees) and does not need any complicated switch statements:
(Edit : full working example with generic method)
public Func<User, bool> CompileRule(Rule r)
{
var paramUser = Expression.Parameter(typeof(User));
Expression expr = BuildExpr(r, paramUser);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<User, bool>>(expr, paramUser).Compile();
}
You can then write:
List<Rule> rules = new List<Rule> {
new Rule ("Age", "GreaterThan", "21"),
new Rule ( "Name", "Equal", "John"),
new Rule ( "Tags", "Contains", "C#" )
};
// compile the rules once
var compiledRules = rules.Select(r => CompileRule(r)).ToList();
public bool MatchesAllRules(User user)
{
return compiledRules.All(rule => rule(user));
}
Here is the implementation of BuildExpr:
Expression BuildExpr(Rule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.MemberName);
var tProp = typeof(User).GetProperty(r.MemberName).PropertyType;
ExpressionType tBinary;
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary)) {
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 21'
return Expression.MakeBinary(tBinary, left, right);
} else {
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
Note that I used 'GreaterThan' instead of 'greater_than' etc. - this is because 'GreaterThan' is the .NET name for the operator, therefore we don't need any extra mapping.
If you need custom names you can build a very simple dictionary and just translate all operators before compiling the rules:
var nameMap = new Dictionary<string, string> {
{ "greater_than", "GreaterThan" },
{ "hasAtLeastOne", "Contains" }
};
The code uses the type User for simplicity. You can replace User with a generic type T to have a generic Rule compiler for any types of objects. Also, the code should handle errors, like unknown operator name.
Note that generating code on the fly was possible even before the Expression trees API was introduced, using Reflection.Emit. The method LambdaExpression.Compile() uses Reflection.Emit under the covers (you can see this using ILSpy).
Here is some code that compiles as is and does the job.
Basically use two dictionaries, one containing a mapping from operator names to boolean functions, and another containing a map from the property names of the User type to PropertyInfos used to invoke the property getter (if public).
You pass the User instance, and the three values from your table to the static Apply method.
class User
{
public int Age { get; set; }
public string UserName { get; set; }
}
class Operator
{
private static Dictionary<string, Func<object, object, bool>> s_operators;
private static Dictionary<string, PropertyInfo> s_properties;
static Operator()
{
s_operators = new Dictionary<string, Func<object, object, bool>>();
s_operators["greater_than"] = new Func<object, object, bool>(s_opGreaterThan);
s_operators["equal"] = new Func<object, object, bool>(s_opEqual);
s_properties = typeof(User).GetProperties().ToDictionary(propInfo => propInfo.Name);
}
public static bool Apply(User user, string op, string prop, object target)
{
return s_operators[op](GetPropValue(user, prop), target);
}
private static object GetPropValue(User user, string prop)
{
PropertyInfo propInfo = s_properties[prop];
return propInfo.GetGetMethod(false).Invoke(user, null);
}
#region Operators
static bool s_opGreaterThan(object o1, object o2)
{
if (o1 == null || o2 == null || o1.GetType() != o2.GetType() || !(o1 is IComparable))
return false;
return (o1 as IComparable).CompareTo(o2) > 0;
}
static bool s_opEqual(object o1, object o2)
{
return o1 == o2;
}
//etc.
#endregion
public static void Main(string[] args)
{
User user = new User() { Age = 16, UserName = "John" };
Console.WriteLine(Operator.Apply(user, "greater_than", "Age", 15));
Console.WriteLine(Operator.Apply(user, "greater_than", "Age", 17));
Console.WriteLine(Operator.Apply(user, "equal", "UserName", "John"));
Console.WriteLine(Operator.Apply(user, "equal", "UserName", "Bob"));
}
}
I built a rule engine that takes a different approach than you outlined in your question, but I think you will find it to be much more flexible than your current approach.
Your current approach seems to be focused on a single entity, "User", and your persistent rules identify "propertyname", "operator" and "value". My pattern, instead stores the C# code for a predicate (Func<T, bool>) in an "Expression" column in my database. In the current design, using code generation I am querying the "rules" from my database and compiling an assembly with "Rule" types, each with a "Test" method. Here is the signature for the interface that is implemented each Rule:
public interface IDataRule<TEntity>
{
/// <summary>
/// Evaluates the validity of a rule given an instance of an entity
/// </summary>
/// <param name="entity">Entity to evaluate</param>
/// <returns>result of the evaluation</returns>
bool Test(TEntity entity);
/// <summary>
/// The unique indentifier for a rule.
/// </summary>
int RuleId { get; set; }
/// <summary>
/// Common name of the rule, not unique
/// </summary>
string RuleName { get; set; }
/// <summary>
/// Indicates the message used to notify the user if the rule fails
/// </summary>
string ValidationMessage { get; set; }
/// <summary>
/// indicator of whether the rule is enabled or not
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// Represents the order in which a rule should be executed relative to other rules
/// </summary>
int SortOrder { get; set; }
}
The "Expression" is compiled as the body of the "Test" method when the application first executes. As you can see the other columns in the table are also surfaced as first-class properties on the rule so that a developer has flexibility to create an experience for how the user gets notified of failure or success.
Generating an in-memory assembly is a 1-time occurrence during your application and you get a performance gain by not having to use reflection when evaluating your rules. Your expressions are checked at runtime as the assembly will not generate correctly if a property name is misspelled, etc.
The mechanics of creating an in-memory assembly are as follows:
Load your rules from the DB
iterate over the rules and for-each, using a StringBuilder and some string concatenation write the Text representing a class that inherits from IDataRule
compile using CodeDOM -- more info
This is actually quite simple because for the majority this code is property implementations and value initialization in the constructor. Besides that, the only other code is the Expression.
NOTE: there is a limitation that your expression must be .NET 2.0 (no lambdas or other C# 3.0 features) due to a limitation in CodeDOM.
Here is some sample code for that.
sb.AppendLine(string.Format("\tpublic class {0} : SomeCompany.ComponentModel.IDataRule<{1}>", className, typeName));
sb.AppendLine("\t{");
sb.AppendLine("\t\tprivate int _ruleId = -1;");
sb.AppendLine("\t\tprivate string _ruleName = \"\";");
sb.AppendLine("\t\tprivate string _ruleType = \"\";");
sb.AppendLine("\t\tprivate string _validationMessage = \"\";");
/// ...
sb.AppendLine("\t\tprivate bool _isenabled= false;");
// constructor
sb.AppendLine(string.Format("\t\tpublic {0}()", className));
sb.AppendLine("\t\t{");
sb.AppendLine(string.Format("\t\t\tRuleId = {0};", ruleId));
sb.AppendLine(string.Format("\t\t\tRuleName = \"{0}\";", ruleName.TrimEnd()));
sb.AppendLine(string.Format("\t\t\tRuleType = \"{0}\";", ruleType.TrimEnd()));
sb.AppendLine(string.Format("\t\t\tValidationMessage = \"{0}\";", validationMessage.TrimEnd()));
// ...
sb.AppendLine(string.Format("\t\t\tSortOrder = {0};", sortOrder));
sb.AppendLine("\t\t}");
// properties
sb.AppendLine("\t\tpublic int RuleId { get { return _ruleId; } set { _ruleId = value; } }");
sb.AppendLine("\t\tpublic string RuleName { get { return _ruleName; } set { _ruleName = value; } }");
sb.AppendLine("\t\tpublic string RuleType { get { return _ruleType; } set { _ruleType = value; } }");
/// ... more properties -- omitted
sb.AppendLine(string.Format("\t\tpublic bool Test({0} entity) ", typeName));
sb.AppendLine("\t\t{");
// #############################################################
// NOTE: This is where the expression from the DB Column becomes
// the body of the Test Method, such as: return "entity.Prop1 < 5"
// #############################################################
sb.AppendLine(string.Format("\t\t\treturn {0};", expressionText.TrimEnd()));
sb.AppendLine("\t\t}"); // close method
sb.AppendLine("\t}"); // close Class
Beyond this I did make a class I called "DataRuleCollection", which implemented ICollection>. This enabled me to create a "TestAll" capability and an indexer for executing a specific rule by name. Here are the implementations for those two methods.
/// <summary>
/// Indexer which enables accessing rules in the collection by name
/// </summary>
/// <param name="ruleName">a rule name</param>
/// <returns>an instance of a data rule or null if the rule was not found.</returns>
public IDataRule<TEntity, bool> this[string ruleName]
{
get { return Contains(ruleName) ? list[ruleName] : null; }
}
// in this case the implementation of the Rules Collection is:
// DataRulesCollection<IDataRule<User>> and that generic flows through to the rule.
// there are also some supporting concepts here not otherwise outlined, such as a "FailedRules" IList
public bool TestAllRules(User target)
{
rules.FailedRules.Clear();
var result = true;
foreach (var rule in rules.Where(x => x.IsEnabled))
{
result = rule.Test(target);
if (!result)
{
rules.FailedRules.Add(rule);
}
}
return (rules.FailedRules.Count == 0);
}
MORE CODE: There was a request for the code related to the Code Generation. I encapsulated the functionality in a class called 'RulesAssemblyGenerator' which I have included below.
namespace Xxx.Services.Utils
{
public static class RulesAssemblyGenerator
{
static List<string> EntityTypesLoaded = new List<string>();
public static void Execute(string typeName, string scriptCode)
{
if (EntityTypesLoaded.Contains(typeName)) { return; }
// only allow the assembly to load once per entityType per execution session
Compile(new CSharpCodeProvider(), scriptCode);
EntityTypesLoaded.Add(typeName);
}
private static void Compile(CodeDom.CodeDomProvider provider, string source)
{
var param = new CodeDom.CompilerParameters()
{
GenerateExecutable = false,
IncludeDebugInformation = false,
GenerateInMemory = true
};
var path = System.Reflection.Assembly.GetExecutingAssembly().Location;
var root_Dir = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Bin");
param.ReferencedAssemblies.Add(path);
// Note: This dependencies list are included as assembly reference and they should list out all dependencies
// That you may reference in your Rules or that your entity depends on.
// some assembly names were changed... clearly.
var dependencies = new string[] { "yyyyyy.dll", "xxxxxx.dll", "NHibernate.dll", "ABC.Helper.Rules.dll" };
foreach (var dependency in dependencies)
{
var assemblypath = System.IO.Path.Combine(root_Dir, dependency);
param.ReferencedAssemblies.Add(assemblypath);
}
// reference .NET basics for C# 2.0 and C#3.0
param.ReferencedAssemblies.Add(#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll");
param.ReferencedAssemblies.Add(#"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll");
var compileResults = provider.CompileAssemblyFromSource(param, source);
var output = compileResults.Output;
if (compileResults.Errors.Count != 0)
{
CodeDom.CompilerErrorCollection es = compileResults.Errors;
var edList = new List<DataRuleLoadExceptionDetails>();
foreach (CodeDom.CompilerError s in es)
edList.Add(new DataRuleLoadExceptionDetails() { Message = s.ErrorText, LineNumber = s.Line });
var rde = new RuleDefinitionException(source, edList.ToArray());
throw rde;
}
}
}
}
If there are any other questions or comments or requests for further code samples, let me know.
Reflection is your most versatile answer. You have three columns of data, and they need to be treated in different ways:
Your field name. Reflection is the way to get the value from a coded field name.
Your comparison operator. There should be a limited number of these, so a case statement should handle them most easily. Especially as some of them ( has one or more of ) is slightly more complex.
Your comparison value. If these are all straight values then this is easy, although you will have divide the multiple entries up. However, you could also use reflection if they are field names too.
I would take an approach more like:
var value = user.GetType().GetProperty("age").GetValue(user, null);
//Thank you Rick! Saves me remembering it;
switch(rule.ComparisonOperator)
case "equals":
return EqualComparison(value, rule.CompareTo)
case "is_one_or_more_of"
return IsInComparison(value, rule.CompareTo)
etc. etc.
It gives you flexibility for adding more options for comparison. It also means that you can code within the Comparison methods any type validation that you might want, and make them as complex as you want. There is also the option here for the CompareTo to be evaluated as a recursive call back to another line, or as a field value, which could be done like:
return IsInComparison(value, EvaluateComparison(rule.CompareTo))
It all depends on the possibilities for the future....
If you only have a handful of properties and operators, the path of least of resistance is to just code up all the checks as special cases like this:
public bool ApplyRules(List<Rule> rules, User user)
{
foreach (var rule in rules)
{
IComparable value = null;
object limit = null;
if (rule.objectProperty == "age")
{
value = user.age;
limit = Convert.ToInt32(rule.TargetValue);
}
else if (rule.objectProperty == "username")
{
value = user.username;
limit = rule.TargetValue;
}
else
throw new InvalidOperationException("invalid property");
int result = value.CompareTo(limit);
if (rule.ComparisonOperator == "equal")
{
if (!(result == 0)) return false;
}
else if (rule.ComparisonOperator == "greater_than")
{
if (!(result > 0)) return false;
}
else
throw new InvalidOperationException("invalid operator");
}
return true;
}
If you have a lot of properties, you may find a table-driven approach more palatable. In that case you would create a static Dictionary that maps property names to delegates matching, say, Func<User, object>.
If you don't know the names of the properties at compile time, or you want to avoid special-cases for each property and don't want to use the table approach, you can use reflection to get properties. For example:
var value = user.GetType().GetProperty("age").GetValue(user, null);
But since TargetValue is probably a string, you'll need to take care to do type conversion from the rules table if necessary.
What about a data type orientated approach with an extention method:
public static class RoleExtension
{
public static bool Match(this Role role, object obj )
{
var property = obj.GetType().GetProperty(role.objectProperty);
if (property.PropertyType == typeof(int))
{
return ApplyIntOperation(role, (int)property.GetValue(obj, null));
}
if (property.PropertyType == typeof(string))
{
return ApplyStringOperation(role, (string)property.GetValue(obj, null));
}
if (property.PropertyType.GetInterface("IEnumerable<string>",false) != null)
{
return ApplyListOperation(role, (IEnumerable<string>)property.GetValue(obj, null));
}
throw new InvalidOperationException("Unknown PropertyType");
}
private static bool ApplyIntOperation(Role role, int value)
{
var targetValue = Convert.ToInt32(role.TargetValue);
switch (role.ComparisonOperator)
{
case "greater_than":
return value > targetValue;
case "equal":
return value == targetValue;
//...
default:
throw new InvalidOperationException("Unknown ComparisonOperator");
}
}
private static bool ApplyStringOperation(Role role, string value)
{
//...
throw new InvalidOperationException("Unknown ComparisonOperator");
}
private static bool ApplyListOperation(Role role, IEnumerable<string> value)
{
var targetValues = role.TargetValue.Split(' ');
switch (role.ComparisonOperator)
{
case "hasAtLeastOne":
return value.Any(v => targetValues.Contains(v));
//...
}
throw new InvalidOperationException("Unknown ComparisonOperator");
}
}
Than you can evaulate like this:
var myResults = users.Where(u => roles.All(r => r.Match(u)));
Although the most obvious way to answer the "How to implement a rule engine? (in C#)" question is to execute a given set of rules in sequence, this is in general considered as a naïve implementation (does not mean it does not work :-)
It seems it's "good enough" in your case because your problem seems more to be "how to run a set of rules in sequence", and the lambda/expression tree (Martin's answer) is certainly the most elegant way in that matter if you are equiped with recent C# versions.
However for more advanced scenarios, here is a link to the Rete Algorithm that is in fact implemented in many commercial rule engine systems, and another link to NRuler, an implementation of that algorithm in C#.
Martin's answer was quite good. I actually made a rules engine that has the same idea as his. And I was surprised that it's almost the same. I've included some of his code to somewhat improve it. Although I've made it to handle more complex rules.
You can look at Yare.NET
Or download it in Nuget
How about using the workflow rules engine?
You can execute Windows Workflow Rules without Workflow
see Guy Burstein's Blog: http://blogs.microsoft.co.il/blogs/bursteg/archive/2006/10/11/RuleExecutionWithoutWorkflow.aspx
and to programatically create your rules, see Stephen Kaufman's WebLog
http://blogs.msdn.com/b/skaufman/archive/2006/05/15/programmatically-create-windows-workflow-rules.aspx
I added implementation for and,or between rules
i added class RuleExpression that represent the root of a tree that can be leaf the is simple rule or can be and,or binary expressions there for they dont have rule and have expressions:
public class RuleExpression
{
public NodeOperator NodeOperator { get; set; }
public List<RuleExpression> Expressions { get; set; }
public Rule Rule { get; set; }
public RuleExpression()
{
}
public RuleExpression(Rule rule)
{
NodeOperator = NodeOperator.Leaf;
Rule = rule;
}
public RuleExpression(NodeOperator nodeOperator, List<RuleExpression> expressions, Rule rule)
{
this.NodeOperator = nodeOperator;
this.Expressions = expressions;
this.Rule = rule;
}
}
public enum NodeOperator
{
And,
Or,
Leaf
}
I have another class that compile the ruleExpression to one Func<T, bool>:
public static Func<T, bool> CompileRuleExpression<T>(RuleExpression ruleExpression)
{
//Input parameter
var genericType = Expression.Parameter(typeof(T));
var binaryExpression = RuleExpressionToOneExpression<T>(ruleExpression, genericType);
var lambdaFunc = Expression.Lambda<Func<T, bool>>(binaryExpression, genericType);
return lambdaFunc.Compile();
}
private static Expression RuleExpressionToOneExpression<T>(RuleExpression ruleExpression, ParameterExpression genericType)
{
if (ruleExpression == null)
{
throw new ArgumentNullException();
}
Expression finalExpression;
//check if node is leaf
if (ruleExpression.NodeOperator == NodeOperator.Leaf)
{
return RuleToExpression<T>(ruleExpression.Rule, genericType);
}
//check if node is NodeOperator.And
if (ruleExpression.NodeOperator.Equals(NodeOperator.And))
{
finalExpression = Expression.Constant(true);
ruleExpression.Expressions.ForEach(expression =>
{
finalExpression = Expression.AndAlso(finalExpression, expression.NodeOperator.Equals(NodeOperator.Leaf) ?
RuleToExpression<T>(expression.Rule, genericType) :
RuleExpressionToOneExpression<T>(expression, genericType));
});
return finalExpression;
}
//check if node is NodeOperator.Or
else
{
finalExpression = Expression.Constant(false);
ruleExpression.Expressions.ForEach(expression =>
{
finalExpression = Expression.Or(finalExpression, expression.NodeOperator.Equals(NodeOperator.Leaf) ?
RuleToExpression<T>(expression.Rule, genericType) :
RuleExpressionToOneExpression<T>(expression, genericType));
});
return finalExpression;
}
}
public static BinaryExpression RuleToExpression<T>(Rule rule, ParameterExpression genericType)
{
try
{
Expression value = null;
//Get Comparison property
var key = Expression.Property(genericType, rule.ComparisonPredicate);
Type propertyType = typeof(T).GetProperty(rule.ComparisonPredicate).PropertyType;
//convert case is it DateTimeOffset property
if (propertyType == typeof(DateTimeOffset))
{
var converter = TypeDescriptor.GetConverter(propertyType);
value = Expression.Constant((DateTimeOffset)converter.ConvertFromString(rule.ComparisonValue));
}
else
{
value = Expression.Constant(Convert.ChangeType(rule.ComparisonValue, propertyType));
}
BinaryExpression binaryExpression = Expression.MakeBinary(rule.ComparisonOperator, key, value);
return binaryExpression;
}
catch (FormatException)
{
throw new Exception("Exception in RuleToExpression trying to convert rule Comparison Value");
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
I have created a package for a rich and high performance rule engine written in dotnet, check out this repo for more info.
Once installed, you can use it as simple as:
var engine = new RulesService<TestModel>(new RulesCompiler(), new LazyCache.Mocks.MockCachingService());
var matchingRules = engine.GetMatchingRules(
new TestModel { NumericField = 5 },
new[] {
new RulesConfig {
Id = Guid.NewGuid(),
RulesOperator = Rule.InterRuleOperatorType.And,
RulesGroups = new RulesGroup[] {
new RulesGroup {
RulesOperator = Rule.InterRuleOperatorType.And,
Rules = new[] {
new Rule {
ComparisonOperator = Rule.ComparisonOperatorType.Equal,
ComparisonValue = 5.ToString(),
ComparisonPredicate = nameof(TestModel.NumericField)
}
}
}
}
}
});
I want to add Microsoft's Rules Engine library for future visits;
https://github.com/microsoft/RulesEngine
I am trying to create a generic method that will read an attribute on a class and return that value at runtime. How do would I do this?
Note: DomainName attribute is of class DomainNameAttribute.
[DomainName("MyTable")]
Public class MyClass : DomainBase
{}
What I am trying to generate:
//This should return "MyTable"
String DomainNameValue = GetDomainName<MyClass>();
public string GetDomainName<T>()
{
var dnAttribute = typeof(T).GetCustomAttributes(
typeof(DomainNameAttribute), true
).FirstOrDefault() as DomainNameAttribute;
if (dnAttribute != null)
{
return dnAttribute.Name;
}
return null;
}
UPDATE:
This method could be further generalized to work with any attribute:
public static class AttributeExtensions
{
public static TValue GetAttributeValue<TAttribute, TValue>(
this Type type,
Func<TAttribute, TValue> valueSelector)
where TAttribute : Attribute
{
var att = type.GetCustomAttributes(
typeof(TAttribute), true
).FirstOrDefault() as TAttribute;
if (att != null)
{
return valueSelector(att);
}
return default(TValue);
}
}
and use like this:
string name = typeof(MyClass)
.GetAttributeValue((DomainNameAttribute dna) => dna.Name);
There is already an extension to do this.
namespace System.Reflection
{
// Summary:
// Contains static methods for retrieving custom attributes.
public static class CustomAttributeExtensions
{
public static T GetCustomAttribute<T>(this MemberInfo element, bool inherit) where T : Attribute;
}
}
So:
var attr = typeof(MyClass).GetCustomAttribute<DomainNameAttribute>(false);
return attr != null ? attr.DomainName : "";
System.Reflection.MemberInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++)
{
if (attributes[i] is DomainNameAttribute)
{
System.Console.WriteLine(((DomainNameAttribute) attributes[i]).Name);
}
}
I used Darin Dimitrov's answer to create a generic extension to get member attributes for any member in a class (instead of attributes for a class). I'm posting it here because others may find it useful:
public static class AttributeExtensions
{
/// <summary>
/// Returns the value of a member attribute for any member in a class.
/// (a member is a Field, Property, Method, etc...)
/// <remarks>
/// If there is more than one member of the same name in the class, it will return the first one (this applies to overloaded methods)
/// </remarks>
/// <example>
/// Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass':
/// var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);
/// </example>
/// <param name="type">The class that contains the member as a type</param>
/// <param name="MemberName">Name of the member in the class</param>
/// <param name="valueSelector">Attribute type and property to get (will return first instance if there are multiple attributes of the same type)</param>
/// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events</param>
/// </summary>
public static TValue GetAttribute<TAttribute, TValue>(this Type type, string MemberName, Func<TAttribute, TValue> valueSelector, bool inherit = false) where TAttribute : Attribute
{
var att = type.GetMember(MemberName).FirstOrDefault().GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;
if (att != null)
{
return valueSelector(att);
}
return default(TValue);
}
}
Usage example:
//Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass'
var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);
A simplified version of Darin Dimitrov's first solution:
public string GetDomainName<T>()
{
var dnAttribute = typeof(T).GetCustomAttribute<DomainNameAttribute>(true);
if (dnAttribute != null)
{
return dnAttribute.Name;
}
return null;
}
' Simplified Generic version.
Shared Function GetAttribute(Of TAttribute)(info As MemberInfo) As TAttribute
Return info.GetCustomAttributes(GetType(TAttribute), _
False).FirstOrDefault()
End Function
' Example usage over PropertyInfo
Dim fieldAttr = GetAttribute(Of DataObjectFieldAttribute)(pInfo)
If fieldAttr IsNot Nothing AndAlso fieldAttr.PrimaryKey Then
keys.Add(pInfo.Name)
End If
Probably just as easy to use the body of generic function inline.
It doesn't make any sense to me to make the function generic over the type MyClass.
string DomainName = GetAttribute<DomainNameAttribute>(typeof(MyClass)).Name
// null reference exception if MyClass doesn't have the attribute.
In case anyone needs a nullable result and for this to work across Enums, PropertyInfo and classes, here's how I solved it. This is a modification of Darin Dimitrov's updated solution.
public static object GetAttributeValue<TAttribute, TValue>(this object val, Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
{
try
{
Type t = val.GetType();
TAttribute attr;
if (t.IsEnum && t.GetField(val.ToString()).GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() is TAttribute att)
{
// Applies to Enum values
attr = att;
}
else if (val is PropertyInfo pi && pi.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() is TAttribute piAtt)
{
// Applies to Properties in a Class
attr = piAtt;
}
else
{
// Applies to classes
attr = (TAttribute)t.GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault();
}
return valueSelector(attr);
}
catch
{
return null;
}
}
Usage examples:
// Class
SettingsEnum.SettingGroup settingGroup = (SettingsEnum.SettingGroup)(this.GetAttributeValue((SettingGroupAttribute attr) => attr.Value) as SettingsEnum.SettingGroup?);
// Enum
DescriptionAttribute desc = settingGroup.GetAttributeValue((DescriptionAttribute attr) => attr) as DescriptionAttribute;
// PropertyInfo
foreach (PropertyInfo pi in this.GetType().GetProperties())
{
string setting = ((SettingsEnum.SettingName)(pi.GetAttributeValue((SettingNameAttribute attr) => attr.Value) as SettingsEnum.SettingName?)).ToString();
}
When you have Overridden Methods with same Name Use the helper below
public static TValue GetControllerMethodAttributeValue<T, TT, TAttribute, TValue>(this T type, Expression<Func<T, TT>> exp, Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
{
var memberExpression = exp?.Body as MethodCallExpression;
if (memberExpression.Method.GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault() is TAttribute attr && valueSelector != null)
{
return valueSelector(attr);
}
return default(TValue);
}
Usage: var someController = new SomeController(Some params); var str =
typeof(SomeController).GetControllerMethodAttributeValue(x =>
someController.SomeMethod(It.IsAny()), (RouteAttribute
routeAttribute) => routeAttribute.Template);
Rather then write a lot of code, just do this:
{
dynamic tableNameAttribute = typeof(T).CustomAttributes.FirstOrDefault().ToString();
dynamic tableName = tableNameAttribute.Substring(tableNameAttribute.LastIndexOf('.'), tableNameAttribute.LastIndexOf('\\'));
}