How to avoid recursion call in property getter? Here is my simple code
public class UploadAttribute : Attribute
{
private Type _resourceType;
private string _select;
private string _change;
private string _remove;
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return GetResourceText(m => m.Select, "Select..."); }
set { _select = value; }
}
public string Change
{
get { return GetResourceText(m => m.Change, "Change..."); }
set { _change = value; }
}
public string Remove
{
get { return GetResourceText(m => m.Remove, "Remove"); }
set { _remove = value; }
}
private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string #default)
{
var value = expression.Compile().Invoke(this); // here .net is creating new UploadAttribute instance and use it for expression fnc
var result = value ?? #default;
if (_resourceType != null && !string.IsNullOrEmpty(value))
{
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(value);
}
catch
{
// if string wasn't found in resource file than use what user specify; don't by big brother.
}
}
return result;
}
}
But if you look at method GetResourceText, there is line where I need to compile and invoke expression to get value for given property. Unfortunately this operation creates new instace of UploadAttribute. In that moment .net goes thru all properties and call getter, if I'm not mistaken, and in getter .net compiles and invokes expression to get value or given property, again, again and again to StackOverlowException.
Can you advice me how to avoid this behavior but with simplicity of this solution?
Edit: The porpose of this class is provide caption for buttons - what user sets with ability to use multi language caption from resource manager.
In example above is button Select translated from Resources, for Change button is used default text "Change..." and for Remove button caption "Destroy this #&#!". So if user doesn't specify property value, application use default text, otherwise try to find text in resources and if match is found than use text from resources else use what user set.
[Required]
[Upload(ResourceType = typeof(Resource), Select = "UploadSelect", Remove = "Destroy this #&#!")]
public HttpPostedFileBase Logo { get; set; }
It appears that what you are trying to achieve is to have some way of initializing these properties if they are not explicitly set. The manner in which you are doing that, will not work.
The m => m.Remove type expressions will cause calling the property getter again in infinite recursion until a stack overflow occurs.
You could use a lazy construct as illustrated below. It works as follows:
If the user does not specify a value for a property, it will return the hard-coded default value when the property getter is invoked.
If the user specifies a value for a property, that value it will be used first as a key in an attempt to retrieve a corresponding string value from the resources. If the resource is not found, it is used as the value for the property, provided that it is not empty, otherwise it will fall back on the hard-coded default value.
Note that this dual purpose for an attribute property value leads to a rather brittle design solution. If a resource with key "UploadSelect" is not found, that will become the caption on the button.
public class UploadAttribute : Attribute
{
private static readonly string kSelectDefaultCaption = "Select...";
private static readonly string kChangeDefaultCaption = "Change...";
private static readonly string kRemoveDefaultCaption = "Remove...";
private Type _resourceType;
private Lazy<string> _select = new Lazy<string>(() => kSelectDefaultCaption);
private Lazy<string> _change = new Lazy<string>(() => kChangeDefaultCaption);
private Lazy<string> _remove = new Lazy<string>(() => kRemoveDefaultCaption);
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return _select.Value; }
set { _select = new Lazy<string>(() => GetResourceText(value, kSelectDefaultCaption)); }
}
public string Change
{
get { return _change.Value; }
set { _change = new Lazy<string>(() => GetResourceText(value, kChangeDefaultCaption)); }
}
public string Remove
{
get { return _remove.Value; }
set { _remove = new Lazy<string>(() => GetResourceText(value, kRemoveDefaultCaption)); }
}
private string GetResourceText(string key, string #default)
{
// initialize to default.
var result = #default;
if (_resourceType != null && !string.IsNullOrEmpty(key))
{
// initialize to the value of the key,
// that could be a user supplied string literal
result = key;
// attempt to retrieve it from the resources.
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(key);
}
catch
{
// could not retrieve key, using the key value as the result.
}
}
return result;
}
}
I'm pretty dump. It doesn't creates new instances at all; I answered my question in moment when I asked.
return GetResourceText(m => m.Select, "Select..."); is recursion without end, but return GetResourceText(m => m._select, "Select..."); isn't, because I'm not calling method GetResourceText again.
I'm sorry boys for silly question.
An slight modification to Alex's answer, if you wish to keep your compile-time safety on using the property names:
public class UploadAttribute : Attribute
{
private Type _resourceType;
private Lazy<string> _select;
private Lazy<string> _change;
private Lazy<string> _remove;
UploadAttribute()
{
_select = new Lazy<string>(() => GetResourceText(m => m.Select, "Select..."));
_change = new Lazy<string>(() => GetResourceText(m => m.Change, "Change..."));
_remove = new Lazy<string>(() => GetResourceText(m => m.Remove, "Remove..."));
}
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return _select.Value; }
set { _select = new Lazy<string>(() => value); }
}
public string Change
{
get { return _change.Value; }
set { _change = new Lazy<string>(() => value); }
}
public string Remove
{
get { return _remove.Value; }
set { _remove = new Lazy<string>(() => value); }
}
private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string #default)
{
var result = #default;
var memberExpression = expression.Body as MemberExpression;
if (_resourceType != null && memberExpression != null)
{
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(memberExpression.Member.Name);
}
catch
{
// if string wasn't found in resource file than use what user specify; don't by big brother.
}
}
return result;
}
}
Related
I have a property on a class which is implemented by an interface. Now I want to get all attributes from a specific type declared on that property and their interface Pendants.
In order to regard multi implementation with implicit and explicit members I wrote an test-class (with xUnit).
[DebuggerDisplay("{Tooltip}")]
[AttributeUsage(AttributeTargets.Property)]
public class TooltipAttribute : Attribute
{
public TooltipAttribute(string tooltip)
{
Tooltip = tooltip;
}
public string Tooltip { get; set; }
}
public interface IAmGood
{
[Tooltip("GOOD: I am a very generic description.")]
int Length { get; }
}
public interface IAmBad
{
[Tooltip("BAD: This description is not wanted to be shown.")]
int Length { get; }
}
public class DemoClassImplicit : IAmGood, IAmBad
{
[Tooltip("GOOD: Implicit")]
public int Length => throw new NotImplementedException();
[Tooltip("BAD: Explicit")]
int IAmBad.Length => throw new NotImplementedException();
}
public class DemoClassExplicit : IAmGood, IAmBad
{
[Tooltip("GOOD: Explicit")]
int IAmGood.Length => throw new NotImplementedException();
[Tooltip("BAD: Implicit")]
public int Length => throw new NotImplementedException();
}
public class DemoClassImplicitForBoth : IAmGood, IAmBad
{
[Tooltip("I am GOOD and BAD")]
public int Length => throw new NotImplementedException();
}
public class TestClass
{
[Fact]
public void GetTooltipFromImplicit()
{
var demoClassImplicit = new DemoClassImplicit();
var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperty("Length");
var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);
Assert.Equal(2, tooltips.Count());
Assert.All(tooltips, o => Assert.Contains("GOOD", o.Tooltip));
}
[Fact]
public void GetTooltipFromExplicit()
{
var demoClassImplicit = new DemoClassExplicit();
var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperties().First(o => o.Name.EndsWith(".Length"));
var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);
Assert.Equal(2, tooltips.Count());
Assert.All(tooltips, o => Assert.Contains("GOOD", o.Tooltip));
}
[Fact]
public void GetTooltipFromImplicitForBoth()
{
var demoClassImplicit = new DemoClassImplicitForBoth();
var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperty("Length");
var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);
Assert.Equal(3, tooltips.Count());
}
/// <summary>
/// The core method.
/// </summary>
public IEnumerable<T_Attribute> GetTooltipAttribute<T_Attribute>(PropertyInfo propInfo)
where T_Attribute : Attribute
{
var result = new List<T_Attribute>(propInfo.GetCustomAttributes<T_Attribute>());
var declaringType = propInfo.DeclaringType;
// The get method is required for comparing without use the prop name.
var getMethodFromGivenProp = propInfo.GetGetMethod(true);
// Check for each interface if the given property is declared there
// (it is not a naming check!).
foreach (var interfaceType in declaringType.GetInterfaces())
{
var map = declaringType.GetInterfaceMap(interfaceType);
// Check if the current interface has an member for given props get method.
// Attend that compare by naming would be cause an invalid result here!
var targetMethod = map.TargetMethods.FirstOrDefault(o => o.Equals(getMethodFromGivenProp));
if (targetMethod != null)
{
// Get the equivalent get method on interface side.
// ERROR: The error line!
var interfaceMethod = map.InterfaceMethods.FirstOrDefault(o => o.Name == targetMethod.Name);
if (interfaceMethod != null)
{
// The get method does not help to get the attribute so the property is required.
// In order to get the property we must look which one has the found get method.
var property = interfaceType.GetProperties().FirstOrDefault(o => o.GetGetMethod() == interfaceMethod);
if (property != null)
{
var attributes = property.GetCustomAttributes<T_Attribute>();
if (attributes != null)
{
result.AddRange(attributes);
}
}
}
}
}
return result;
}
}
The test method 'GetTooltipFromExplicit' fails because in the core method is a comparison by name. I marked the line above with // ERROR: The error line!.
I have no idea how to find the method-pendant inside of 'InterfaceMapping'-class.
The solution was to know that the order of the two collections in InterfaceMapping are mirrorred.
So replace the line below is the solution:
// ERROR: The error line!
var interfaceMethod = map.InterfaceMethods.FirstOrDefault(o => o.Name == targetMethod.Name);
// SOLUTION: The working line:
var interfaceMethod = map.InterfaceMethods[Array.IndexOf(map.TargetMethods, targetMethod)];
This detailed was explained on official member documentation (but not on the class itself). See:
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.interfacemapping.interfacemethods?view=netcore-3.1#remarks
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.interfacemapping.targetmethods?view=netcore-3.1#remarks
i am working on asp.net core webapi and using appsettings.json to store some settings. I have a class to get the property value from appsettings using IOptions<>.
Wonder if there is a simple way to use generic to get the property value instead of creating individual method names as i am doing below:
public class NotificationOptionsProvider
{
private readonly IOptions<NotificationOptions> _notificationSettings;
public NotificationOptionsProvider(IOptions<NotificationOptions> notificationSettings)
{
_notificationSettings = notificationSettings;
InviteNotificationContent = new InviteNotificationContent();
}
public string GetRecipientUserRole()
{
if (string.IsNullOrWhiteSpace(_notificationSettings.Value.RecipientUserRole))
{
throw new Exception("RecipientUserRole is not configured");
}
return _notificationSettings.Value.RecipientUserRole;
}
public string GetInvitationReminderTemplateCode()
{
if (string.IsNullOrWhiteSpace(_notificationSettings.Value.AssignReminderTemplateCode))
{
throw new Exception("InvitationReminderTemplateCode is not configured");
}
return _notificationSettings.Value.AssignReminderTemplateCode;
}
public string GetSessionBookedTemplateCode()
{
if (string.IsNullOrWhiteSpace(_notificationSettings.Value.SessionBookedTemplateCode))
{
throw new Exception("SessionBookedTemplateCode is not configured");
}
return _notificationSettings.Value.SessionBookedTemplateCode;
}
}
Thanks
You could write something like:
public string GetSetting(Func<NotificationOptions, string> selector)
{
string value = selector(_notificationSettings.Value);
if (string.IsNullOrWhiteSpace(value))
{
throw new Exception("Not configured");
}
return value;
}
And call it like:
GetSetting(x => x.AssignReminderTemplateCode);
Just elaborating on canton7's excellent answer; you could preserve the exception text like this:
public string GetSetting(Expression<Func<NotificationOptions, string>> selector)
{
Func<NotificationOptions, string> func = selector.Compile();
string value = selector(_notificationSettings.Value);
if (string.IsNullOrWhiteSpace(value))
{
var expression = (MemberExpression)selector.Body;
throw new Exception($"{expression.Member.Name} is not configured");
}
return value;
}
Although be aware the call to .Compile() will have a performance hit.
I have a .net web application that for all intents and purposes of this question is CRUD with many different domain objects.
A common theme across theses objects is the need to know which value properties have been modified as well as child domain model properties. Currently we have two different systems in place for this.
The value properties is the one I am trying to sort out with this question.
Right now the models all inherit from the PersistableModel base that has these fields and methods of note:
private readonly List<string> _modifiedProperties = new List<string>();
public virtual ModelState State { get; set; }
public IEnumerable<string> ModifiedProperties { get { return _modifiedProperties; } }
protected bool HasModifiedProperties { get { return 0 < _modifiedProperties.Count; } }
public bool WasModified(string propertyName)
{
return _modifiedProperties.Contains(propertyName);
}
public void WasModified(string propertyName, bool modified)
{
if (modified)
{
if (!WasModified(propertyName)) _modifiedProperties.Add(propertyName);
}
else
{
_modifiedProperties.Remove(propertyName);
}
}
Then within each individual model whenever a property is set we also need to call WasModified with a string of the property name and a boolean value.
Obviously this is very tedious and error prone, what I want to do is redesign this base class to automatically add entries to the dictionary when a derived class's property is set.
In my research the closest I've been able to get is to use PostSharp which is out of the question.
While working on a different project I came up with a solution that gets most of the way towards my original goal.
Note that this solution is reliant upon the Dev Express ViewModelBase as its base class, but it wouldn't be hard to make a new base class with the features being used for non Dev Express projects:
Edit: I found an issue with the reset state logic, this update removes that issue.
public abstract class UpdateableModel : ViewModelBase
{
private static readonly MethodInfo GetPropertyMethod;
private static readonly MethodInfo SetPropertyMethod;
private readonly bool _trackingEnabled;
private readonly Dictionary<string, Tuple<Expression, object>> _originalValues;
private readonly List<string> _differingFields;
static UpdateableModel()
{
GetPropertyMethod = typeof(UpdateableModel).GetMethod("GetProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
SetPropertyMethod = typeof(UpdateableModel).GetMethod("SetProperty");
}
protected UpdateableModel(bool isNewModel)
{
_originalValues = new Dictionary<string, Tuple<Expression, object>>();
_differingFields = new List<string>();
if (isNewModel) return;
State = ModelState.Unmodified;
_trackingEnabled = true;
}
public ModelState State
{
get { return GetProperty(() => State); }
set { base.SetProperty(() => State, value); }
}
public new bool SetProperty<T>(Expression<Func<T>> expression, T value)
{
var wasUpdated = base.SetProperty(expression, value);
if (_trackingEnabled && wasUpdated)
{
UpdateState(expression, value);
}
return wasUpdated;
}
/// <summary>
/// Reset State is meant to be called when discarding changes, it will reset the State value to Unmodified and set all modified values back to their original value.
/// </summary>
public void ResetState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = SetPropertyMethod.MakeGenericMethod(type);
genericPropertySetter.Invoke(this, new[]{_originalValues[differingField].Item2});
}
}
/// <summary>
/// Update State is meant to be called after changes have been persisted, it will reset the State value to Unmodified and update the original values to the new values.
/// </summary>
public void UpdateState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = GetPropertyMethod.MakeGenericMethod(type);
var value = genericPropertySetter.Invoke(this, new object[] { _originalValues[differingField].Item1 });
var newValue = new Tuple<Expression, object>(_originalValues[differingField].Item1,value);
_originalValues[differingField] = newValue;
}
_differingFields.Clear();
State = ModelState.Unmodified;
}
private static Type GetFuncType(Expression expr)
{
var lambda = expr as LambdaExpression;
if (lambda == null)
{
return null;
}
var member = lambda.Body as MemberExpression;
return member != null ? member.Type : null;
}
private void UpdateState<T>(Expression<Func<T>> expression, T value)
{
var propertyName = GetPropertyName(expression);
if (!_originalValues.ContainsKey(propertyName))
{
_originalValues.Add(propertyName, new Tuple<Expression,object>(expression, value));
}
else
{
if (!Compare(_originalValues[propertyName].Item2, value))
{
_differingFields.Add(propertyName);
}
else if (_differingFields.Contains(propertyName))
{
_differingFields.Remove(propertyName);
}
State = _differingFields.Count == 0
? ModelState.Unmodified
: ModelState.Modified;
}
}
private static bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Another quick note, the IsNewModel constructor flag serves to differentiate objects create on the UI level which don't need any kind of state tracking, and objects generated from the data access level which will need the state tracking.
As is well known, CM doesn't support passing a object of complex type through NavigationService like MVVM Light. So I searched for a workaround and did it like this.
There are two viewmodels: MainPageViewModel and SubPageViewModel.
I first defined 3 classes, namely GlobalData, SnapshotCache and StockSnapshot. StockSnapshot is the type of which the object I want to pass between the 2 viewmodels.
public class SnapshotCache : Dictionary<string, StockSnapshot>
{
public StockSnapshot GetFromCache(string key)
{
if (ContainsKey(key))
return this[key];
return null;
}
}
public class GlobalData
{
private GlobalData()
{
}
private static GlobalData _current;
public static GlobalData Current
{
get
{
if (_current == null)
_current = new GlobalData();
return _current;
}
set { _current = value; }
}
private SnapshotCache _cachedStops;
public SnapshotCache Snapshots
{
get
{
if (_cachedStops == null)
_cachedStops = new SnapshotCache();
return _cachedStops;
}
}
}
public class StockSnapshot
{
public string Symbol { get; set; }
public string Message { get; set; }
}
Next, I call the navigation service on MainPageViewModel like this:
StockSnapshot snap = new StockSnapshot {Symbol="1", Message = "The SampleText is here again!" };
GlobalData.Current.Snapshots[snap.Symbol] = snap;
NavigationService.UriFor<SubPageViewModel>().WithParam(p=>p.Symbol,snap.Symbol).Navigate();
And on SubPageViewModel I've got this:
private string _symbol;
public string Symbol
{
get { return _symbol; }
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
}
}
public StockSnapshot Snapshot
{
get { return GlobalData.Current.Snapshots[Symbol]; }
}
And that's where the problem lies. When I run the program, I find out that it always runs to the getter of Snapshot first, when Symbol hasn't been initialized yet. So later I've tried adding some extra code to eliminate the ArgumentNullException so that it can run to the setter of Symbol and then everything goes fine except that the UI doesn't get updated anyway.
Could anyone tell me where I've got wrong?
Thx in advance!!
Why not just use:
private string _symbol;
public string Symbol
{
get { return _symbol;}
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
NotifyOfPropertyChange(() => Snapshot);
}
}
public StockSnapshot Snapshot
{
get { return Symbol!=null? GlobalData.Current.Snapshots[Symbol]:null; }
}
In this case you don't try and get the data from GlobalData when Symbol is null (sensible approach anyway!) and when "Symbol" is set you call NotifyOfPropertyChange() on Snapshot to force a re-get of the property.
We use backing fields for a lot of properties on our domain objects, for example:
protected string _firstname;
public virtual string Firstname
{
get { return _firstname; }
set { _firstname = value; }
}
I've occasionally made stupid typos like the example below, and would like to write a single test that verifies all these properties, rather than manually doing a test per object.
public virtual string Firstname
{
get { return _firstname; }
set { _firstname = Firstname; }
}
Would it be easy to write or does a library already exist to test these backing fields get/set correctly? This would only run on properties with setters and (presumably) a backing field that matches the property name using camel-case underscore
Another solution would be to use automatic properties to eliminate this problem:
public virtual string FirstName { get; set; }
UPDATE (see comments, backing field seems needed):
Another possibility is to generate the pocos. Simple t4-template 'Person.tt'
<## template language="C#" #>
<# var pocos = new [] {
Tuple.Create("FirstName", "string"),
Tuple.Create("LastName", "string"),
Tuple.Create("Age", "int")}; #>
public partial class Person {
<# foreach(var t in pocos) {#>
protected <#= t.Item2#> _<#= t.Item1.ToLowerInvariant()#>;
public virtual <#= t.Item2#> <#= t.Item1#>
{
get { return _<#= t.Item1.ToLowerInvariant()#>; }
set { _<#= t.Item1.ToLowerInvariant()#> = value; }
}
<#}#>
}
Now this could of course bring with it as many problems as it solves but it may be worth looking at ... maybe:)
Apart from using auto properties I would think of using reflection to test out my models.. .
Just write a generic method that gets all the properties of your class and then use methods like these:
/ get value of property: public double Number
double value = (double)numberPropertyInfo.GetValue(calcInstance, null);
[C#]
// set value of property: public double Number
numberPropertyInfo.SetValue(calcInstance, 10.0, null);
For your example:
void Main()
{
const int testValue=5;
var test = (Test)Activator.CreateInstance(typeof(Test));
PropertyInfo valuePropertyInfo = typeof(Test).GetProperty("Value");
valuePropertyInfo.SetValue(test, testValue, null);
int value = (int)valuePropertyInfo.GetValue(test, null);
Console.Write(value); //Assert here instead
}
public class Test
{
private int _value;
public int Value {get {return _value;} set{_value=Value;}}
}
the output of the above function is 0 instead of a 5 which is expected. asserting here would have thrown an error.
What do you think about this approach.
Gallio/MbUnit has a contract verifier which does exactly what you are looking for. A typical usage of AccessContract is the following:
public class Foo // Dummy reference type.
{
private readonly int value;
public int Value { get { return value; } }
public Foo (int value)
{
this.value = value;
}
}
public class Bar
{
private Foo foo;
public Bar(string unusedParameter) { }
public Foo Foo // A complex property to be tested!
{
get { return foo; }
set
{
if (value == null)
throw new ArgumentNullException("value");
if (value.Value < 0)
throw new ArgumentOutOfRangeException();
if (value.Value == 666)
throw new ArgumentException("Inferno value strictly forbidden.");
foo = value;
}
}
}
And the test fixture which uses AccessorContract to run various tests on the property.
[TestFixture]
public class BarTest
{
[VerifyContract]
public readonly IContract AccessorTests = new AccessorContract<Bar, Foo>
{
Getter = target => target.Foo,
Setter = (target, value) => target.Foo = value,
ValidValues = { new Foo(123), new Foo(456), new Foo(789) },
AcceptNullValue = false,
DefaultInstance = () => new Bar("Hello"),
InvalidValues =
{
{ typeof(ArgumentOutOfRangeException), new Foo(-123), new Foo(-456) },
{ typeof(ArgumentException), new Foo(666) }
}
};
}
The contract verifier generates the following unit tests:
Have a look at the MbUnit test project for more usage examples.