I am pretty new to C#, so please go easy on me.
I have a major issue that been holding me up for days.
Problem:
We have a web application and use MVC4, when a document is opened, all the values in the model are created in the backingstore in session , by calling the method SaveValues()
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public Dictionary<string, object> BackingStore = new Dictionary<string, object>();
public Dictionary<string, object> Changes = new Dictionary<string, object>();
public bool HasChanges { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void SaveValues()
{
// Expensive, to use reflection, especially if LOTS of objects are going to be used.
// You can use straight properties here if you want, this is just the lazy mans way.
this.GetType().GetProperties().ToList().ForEach(tProp => { BackingStore[tProp.Name] = tProp.GetValue(this, null); Changes[tProp.Name] = ""; });
HttpContext.Current.Session["SbackingStore"] = BackingStore;
HasChanges = false;
}
public void RevertValues()
{
// Again, you can use straight properties here if you want. Since this is using Property setters, will take care of Changes dictionary.
this.GetType().GetProperties().ToList().ForEach(tProp => tProp.SetValue(this, BackingStore[tProp.Name], null));
HasChanges = false;
}
public void OnPropertyChanged(string propName, object propValue)
{
// If you have any object types, make sure Equals is properly defined to check for correct uniqueness.
if (HttpContext.Current.Session["SbackingStore"] != null)
{
if (propValue == null) propValue = "";
BackingStore = (Dictionary<string, object>)HttpContext.Current.Session["SbackingStore"];
if (BackingStore[propName].Equals(propValue))
{ }
else
{
Changes[propName] = propValue;
HasChanges = true;
}
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
}
I have a class setup like so , which contains ;
public class VisitViewModel : NotifyPropertyChangedBase
{
public Activity ActivityVM { get; set; }
public VBSSteps VBSStepsVM { get; set; }
public ProductTime ProductTimeVM { get; set; }
public OtherPST OtherPSTVM { get; set; }
public TimeRange TimeRangeVM { get; set; }
}
Each class that falls into the above VisitViewModel class and are coded like the below example. They inherit the NotifyPropertyChangedBase ( I will not post all the classes here as too much info ) ;
public class Activity : NotifyPropertyChangedBase
{
public int Id { get; set; }
public string NotesID { get; set; }
public string SubID { get; set; }
public string Form { get; set; } // Form Name
private string _custNumber;
[MobileCRM.Resources.LocalizedString.LocalizedDisplayName("CustomerNumber")]
[DataType(DataType.Text)]
[Required]
public string CustNumber
{
get { return _custNumber; }
set { _custNumber = value; OnPropertyChanged("CustNumber", value); }
}
private string _companyName;
[MobileCRM.Resources.LocalizedString.LocalizedDisplayName("CustomerName")]
[DataType(DataType.Text)]
[Required]
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; OnPropertyChanged("CompanyName", value); }
}
}
Now the issue is, the values that are created in the backing store ( session ) like the below ( when I expand any one of them i.e. ActivityVM, that contains the Keys and values I want. );
[0] {[ActivityVM, MobileCRM.Models.Activity]}
[1] {[VBSStepsVM, MobileCRM.Models.VBSSteps]}
[2] {[ProductTimeVM, MobileCRM.Models.ProdcutTime]}
[3] {[OtherPSTVM, MobileCRM.Models.OtherPST]}
[4] {[TimeRangeVM, MobileCRM.Models.TimeRange]}
[5] {[HasChanges, False]}
The problem with this is the code I use to get the values and compare the changed data, cannot find the value as they are stored as propertys.... can anybody suggest a way around this ?
Maybe when the values are saved, I could loop through each class and add all the values in each class to the backing store, therefore stopping the values being save as properties.
Code to get values from the backing store and perform the compare ( to see if the data has changed )
public void OnPropertyChanged(string propName, object propValue)
{
// If you have any object types, make sure Equals is properly defined to check for correct uniqueness.
if (HttpContext.Current.Session["SbackingStore"] != null)
{
if (propValue == null) propValue = "";
BackingStore = (Dictionary<string, object>)HttpContext.Current.Session["SbackingStore"];
if (BackingStore[propName].Equals(propValue)) // Errors here : gives The given key was not present in the dictionary.
You have this code:
public void SaveValues()
{
// Expensive, to use reflection, especially if LOTS of objects are going to be used.
// You can use straight properties here if you want, this is just the lazy mans way.
this.GetType().GetProperties().ToList().ForEach(tProp => { BackingStore[tProp.Name] = tProp.GetValue(this, null); Changes[tProp.Name] = ""; });
HttpContext.Current.Session["SbackingStore"] = BackingStore;
HasChanges = false;
}
All clases inherit the base class with this method. So whenever you call SaveValues method on any of your derived classes the HttpContext.Current.Session["SbackingStore"] get ovveriden with new backingstore, that is why you get "A key is missing in dictionary".
Related
So right now I am trying to design a new hire program that grants access to active directory groups, generates documents with their information and location.
Right now I am doing this with an enumeration, with a switch statement that sets the details on the ViewModel like this:
case CaneRidgeSettings.Departments.SCSC:
Model.ScannerFolder = #"scan1\Supply Chain Service Center\" + Model.UserId;
Model.ExtensionRanges = "list station 8000 to-ext 8349";
Model.AdministrativeAssistant = Loader.SCSCAdminAssistant;
Model.DuoCode = "Franklin TN - 8175";
Model.PrinterSelectedIndex = (int)CaneRidgeSettings.PrinterGroups.Cane_Ridge_5th_Floor_West;
return await find.FindNextComputer("800SCSC");
The problem I have with this design is that if I ever add more departments to this building, I have to manually update this switch. So I tried a few things around this such as a dictionary, but it didn't seem to bind to a combo-box very well (even when implementing my own INotifyCollectionChanged).
So instead I created an interface that contains this information, for simplicity and length lets just say the interface does this:
public interface IDepartmentInfo
{
string DepartmentName { get; }
List<string> ActiveDirectoryGroups { get; }
string AdministrativeAssistant { get; }
string Floor { get; }
}
I then created a new class that implements this interface
public class SCSC : IDepartmentInfo
{
public string DepartmentName { get; } = "Shared Services";
public List<string> ActiveDirectoryGroups { get; } = new List<string>() {"Example_AD_GRP","Domain_Users"};
public string AdministrativeAssistant { get; } = "Lisa_Smith#outlook.com";
public string Floor { get; } = "5th Floor East";
public override string ToString() => DepartmentName;
}
Then, on my main Building Class I have an observable collection that expects an IDepartmentInfo and initializes those departments
public class CaneRidgeBuilding : IBuilding
{
public ObservableCollection<IDepartmentInfo> Departments { get; set; } = new ObservableCollection<IDepartmentInfo>() {new SCSC(), new ARS()};
public override string ToString()
{
return "CaneRidge";
}
}
On my View Model I implemented a few properties, mainly the BuildingSelectedIndex and the DepartmentSelectedIndex.
I also have an IDepartmentInfo property that notifies when it is changed because it is databound to several labels on my UI.
public class MainWindowViewModel : BindableBase
{
public ObservableCollection<IBuilding> Buildings { get; set; } = new ObservableCollection<IBuilding>() { new CaneRidgeBuilding() };
private ObservableCollection<IDepartmentInfo> _departmentInfos = new ObservableCollection<IDepartmentInfo>();
public ObservableCollection<IDepartmentInfo> DepartmentInfos
{
get { return _departmentInfos; }
set { SetProperty(ref _departmentInfos, value); }
}
private int _buildingIndex = -1;
public int BuildingIndex
{
get { return _buildingIndex; }
set
{
SetProperty(ref _buildingIndex, value);
SetDepartments();
}
}
private void SetDepartments()
{
if (BuildingIndex != -1)
DepartmentInfos = Buildings[BuildingIndex].Departments;
}
private int _departmentIndex = -1;
public int DepartmentIndex
{
get { return _departmentIndex; }
set
{
SetProperty(ref _departmentIndex, value);
LoadDepartmentSettings();
}
}
private IDepartmentInfo _departmentInformation;
public IDepartmentInfo DepartmentInformation
{
get { return _departmentInformation; }
set { SetProperty(ref _departmentInformation, value); }
}
private void LoadDepartmentSettings()
{
if (DepartmentIndex != -1)
DepartmentInformation = DepartmentInfos[DepartmentIndex];
}
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
}
}
And it works exactly the way I want it to, however to problem I am running into now is how would I handle dependency injection? If I have 10 departments implementing IDepartmentInfo, how exactly could I pass this to an observable collection?
Because the moment I introduce a new building, if I tell Unity to resolve all IDepartmentInfos, what is going to happen is I'll get every single department even if it doesn't belong to CaneRidge.
If I split the departments to each building, then I run into issues where I can't easily load the departments into the ViewModel, because it is expecting an IDepartmentInfo collection. If I limited it to just one type of collection, then it wouldn't work.
Am I over-complicating things?
Here is an idea.
Custom attribute
Introduce a BuilingAttribute so each IDepartmentInfo implementation can declare Type of the building it belongs to (allow multiple if one department can belong to multiple buildings, I got the idea it can't).
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class BuildingAttribute : Attribute
{
public Type BuildingType { get; private set; }
public BuildingAttribute(Type buildingType)
{
this.BuildingType = buildingType;
}
}
DepartmentInfo Collection Factory
An interface that knows how to create a collection of DepartmentInfo for each building Type.
public interface IDepartmentInfoCollectionFactory
{
void RegisterDepartment<T>(Func<IDepartmentInfo> departmentCreator) where T : class, IBuilding;
ObservableCollection<IDepartmentInfo> GetDepartments<T>() where T : class, IBuilding;
}
And the implementation (will be registered as singleton).
public class DepartmentInfoCollectionFactory : IDepartmentInfoCollectionFactory
{
private readonly Dictionary<Type, List<Func<IDepartmentInfo>>> departmentCreators =
new Dictionary<Type, List<Func<IDepartmentInfo>>>();
void IDepartmentInfoCollectionFactory.RegisterDepartment<T>(Func<IDepartmentInfo> departmentCreator)
{
Type buildingType = typeof(T);
if (!this.departmentCreators.ContainsKey(buildingType))
this.departmentCreators.Add(buildingType, new List<Func<IDepartmentInfo>>());
if (!this.departmentCreators[buildingType].Contains(departmentCreator))
this.departmentCreators[buildingType].Add(departmentCreator);
}
ObservableCollection<IDepartmentInfo> IDepartmentInfoCollectionFactory.GetDepartments<T>()
{
Type buildingType = typeof(T);
if (!this.departmentCreators.ContainsKey(buildingType))
throw new InvalidOperationException(
string.Format("No departments have been registered for {0}.", buildingType.ToString()));
ObservableCollection<IDepartmentInfo> departmentInfos = new ObservableCollection<IDepartmentInfo>();
foreach(Func<IDepartmentInfo> creator in this.departmentCreators[buildingType])
{
departmentInfos.Add(creator());
}
return departmentInfos;
}
}
Configuring the factory, so it knows how to create IDepartmentInfo collections.
protected override void ConfigureContainer()
{
Container.RegisterType<IDepartmentInfoCollectionFactory, DepartmentInfoCollectionFactory>(
new ContainerControlledLifetimeManager());
this.ConfigureDepartmentInfoCollectionFactory(Container.Resolve<IDepartmentInfoCollectionFactory>());
}
private void ConfigureDepartmentInfoCollectionFactory(IDepartmentInfoCollectionFactory factory)
{
// Types implementing IDepartmentInfo
var deptInfoTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IDepartmentInfo).IsAssignableFrom(t) && !t.IsInterface);
foreach(Type type in deptInfoTypes)
{
// Get collection of BuildingAttribute for the type
var buildingAttributes = type.GetCustomAttributes(typeof(BuildingAttribute), false)
.OfType<BuildingAttribute>();
if (buildingAttributes.Count() < 1)
throw new InvalidOperationException(
string.Format("The type {0} didn't declare BuildingArgument.", type.ToString()));
var buildingType = buildingAttributes.First().BuildingType;
if (buildingType == null || !buildingType.GetInterfaces().Contains(typeof(IBuilding)))
throw new InvalidOperationException(
string.Format("{0}: BuildingType is not an IBuilding.", type.ToString()));
var registerMethod = typeof(IDepartmentInfoCollectionFactory).GetMethod("RegisterDepartment")
.MakeGenericMethod(new Type[] { buildingType });
registerMethod.Invoke(factory, new object[]
{
new Func<IDepartmentInfo>(() => (IDepartmentInfo)Container.Resolve(type))
});
}
}
Inject the factory.
public class FooBuilding : IBuilding
{
private IDepartmentInfoCollectionFactory factory;
private readonly ObservableCollection<IDepartmentInfo> departmentInfos;
public string Name { get; } = "FooBuilding";
public ObservableCollection<IDepartmentInfo> DepartmentInfos
{
get { return this.departmentInfos; }
}
public FooBuilding(IDepartmentInfoCollectionFactory factory)
{
this.factory = factory;
this.departmentInfos = factory.GetDepartments<FooBuilding>();
}
}
Adding new department
It doesn't require any editing, just create new class with the attribute.
[Building(typeof(FooBuilding))]
public class BarDepartment : IDepartmentInfo
{
public string Name { get; } = "Bar department";
}
I was able to figure out how to inject different buildings and departments, probably not the best way
EDIT: Updated it to use reflection to make it less maintenance
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterTypes(AllClasses.FromLoadedAssemblies()
.Where(type => typeof(IDepartment).IsAssignableFrom(type)), WithMappings.FromAllInterfaces, WithName.TypeName, WithLifetime.None);
ObservableCollection<IBuilding> Buildings = new ObservableCollection<IBuilding>()
{
Container.Resolve<Building1>(new ParameterOverride("departments",GetDepartmentCollection("Building1"))),
Container.Resolve<Building2>(new ParameterOverride("departments",GetDepartmentCollection("Building2")))
};
Container.RegisterInstance(typeof(ObservableCollection<IBuilding>), Buildings,
new ExternallyControlledLifetimeManager());
}
private ObservableCollection<IDepartment> GetDepartmentCollection(string buildingName)
{
var departments = new List<IDepartment>();
foreach (var registration in Container.Registrations.Where( s => s.MappedToType.Namespace.Contains(buildingName)))
{
departments.Add((IDepartment)Container.Resolve(registration.MappedToType));
}
return new ObservableCollection<IDepartment>(departments);
}
Now I am able to completely eliminate the enumeration and it can be extended in the future without breaking any code or requiring me to change anything.
I have a considerable number of strings in my application that need to be cleared each time I get new data from my source. I'd like to use something akin to string.Empty, but I am unsure of how to implement this. Ideally, I'd also like to do this only once, rather than for each separate string.
Pseudo-code:
foreach (string in application)
{
this.empty
}
Am I thinking on the right track?
Some of my code is as follows:
classtoinstantiate
public string Str1;
private string str1 {get {return Str1;}}
public void DoStuff()
{
doStuff();
}
private void doStuff()
{
//dostuff
}
And Form1.cs
classtoinstantiate class1 = new classtoinstantiate();
class.DoStuff();
//I would like to then clear the *public* iteration of string Str1 here,
//before I DoStuff() again.
String.Empty represents a not null empty string.
If you want to clear a large amount of data (string/non string) you can encapsulate all of the variables in one class and create a Clean() method that goes through all the variables and clears them or instantiate that class when you need a fresh copy when you set the default values in the constructor.
The use of class.Empty is from what I understand to have a well defined instance of what is an empty instance.
Given your comments I get the feeling that you only want to clear the strings, have a look at this C# like pseudo code:
public void ClearString(IEnumerable<object> stuffToClear)
{
// go through all the objects to clear
foreach (var item in stuffToClear)
{
// get the properties to clear
var props = from prop in item.GetType().GetProperties()
where prop.PropertyType == typeof(string) // or another type or filter
select prop;
for (var p in props)
{
// clear it
p.SetValue(item, string.Empty);
}
}
}
Not that I'm writing this in freehand, all calls will surely not be correct.
That's the basic OOP concept: construct object when you need it, destroy at the end. Constructing part always deals with default values, which is exactly what you need.
For managed objects (string) simply create a new instance of a class holding all data what has to be reset (cleared):
class SomeDataStorage
{
// default is null
public string Data1 {get; set;}
private string _data2 = "default value";
public string Data2 { get {return _data2;} set {_data2 = value;}}
}
Then you construct this object when you need it
foreach (string in application)
{
var data = new SomeDataStorage(); // default values
...
}
It will be automagically destroyed when going out of scope (leaving { } or exiting function).
For unmanaged objects, implement IDisposable and consider to use using() { } often to auto-dispose.
You can have application-wide instance of SomeDataStorage. Simply assign a new object (construct new instance) to reset values to default.
To make it even more clear:
class App
{
public SomeDataStorage MyData;
public App()
{
Reset();
}
// call this when you need to init for the first time or simply reset to default
public void Reset()
{
MyData = new SomeDataStorage();
}
}
I suggest to put all your strings in to a class and dispose the object if you get new data
public class StringCollection
{
public string StringProp1 { get; set; }
public string StringProp2 { get; set; }
public string StringProp3 { get; set; }
public string StringProp4 { get; set; }
// .... more properties here
// this property won't be touched when clearing
public int SomeOtherProperty{ get; set; }
public void ClearStrings()
{
// returns all public properties
foreach (var prop in this.GetType().GetProperties())
{
// "clear" only properties of type String and those that have a public setter
if (prop.PropertyType == typeof(string) && prop.CanWrite)
prop.SetValue(this, string.Empty, null); // <- "clear" value of the property
}
}
or, in a more general manner - use extension methods:
public class StringCollection
{
public string StringProp1 { get; set; }
public string StringProp2 { get; set; }
public string StringProp3 { get; set; }
public string StringProp4 { get; set; }
public int SomeOtherProperty { get; set; }
}
public class SomeOtherClass
{
public string a1 { get; set; }
public string a2 { get; set; }
public string a3 { get; set; }
public DateTime d1 { get; set; }
public int SomeOtherProperty { get; set; }
}
public static class MyExtensions
{
public static void ClearStrings(this Object obj)
{
// returns all public properties
foreach (var prop in obj.GetType().GetProperties())
{
// "clear" only properties of type String and those that have a public setter
if (prop.PropertyType == typeof(string) && prop.CanWrite)
prop.SetValue(obj, string.Empty, null); // <- "clear" value of the property
}
}
}
use the code:
StringCollection scol2 = new StringCollection();
// ... do soemthing
scol2.ClearStrings();
SomeOtherClass obj = new SomeOtherClass();
// ... do something
obj.ClearStrings();
Within code I want to do something like this:
item.Stage = Stage.Values.ONE;
Where Stage.Values.ONE represents some predefined Stage:
public class Stage
{
[Key]
public virtual int StageId { get; set; }
public string Name { get; set; }
public TimeSpan Span { get; set; }
}
I'm dealing with EF CodeFirst... and I have a lot of stages to define. I'm not sure if I should store the data in the database, or in the dbContext, or what, but I'm looking for the simplest implementation.
I've tried this:
I've tried the following (defining two constants):
public class Stage
{
[Key]
public virtual int StageId { get; set; }
public string Name { get; set; }
public TimeSpan Span { get; set; }
public static class Values
{
public static readonly Stage ONE = new Stage()
{
StageId = 0,
Name = "ONE",
Span = new TimeSpan(0, 0, 0)
};
public static readonly Stage TWO = new Stage()
{
StageId = 1,
Name = "TWO",
Span = new TimeSpan(0, 0, 10)
};
}
But whenever I create a new instance of an entity that has a Stage, a new Stage is added to the db. I just need a few constant stages.
Use of Stage:
public class Side
{
public Side()
{
Stage = Stage.Values.ONE; // Adds new Stage to DB, when it should be a reference to the one I defined above
}
public virtual Stage Stage { get; set; }
}
It looks a bit like an enum, and I've used a kind of 'extended enum' patter several times before with some success. Because you're refencing these values in code, it may not make sense to store them in the database as well, but it's possible if needed.
The technique is described in detail here: http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/
Basically, you create a base class which provides a number of services similar to an enum, and then to create your "enumerated class" you inherit from it and provide a bunch of static instances which call the constructor with however many properties you need to have.
To avoid link rot, here is the base class to use (just put the whole class into your project somewhere), and scroll down for your own code.
public abstract class Enumeration : IComparable
{
private readonly int _value;
private readonly string _displayName;
protected Enumeration()
{
}
protected Enumeration(int value, string displayName)
{
_value = value;
_displayName = displayName;
}
public int Value
{
get { return _value; }
}
public string DisplayName
{
get { return _displayName; }
}
public override string ToString()
{
return DisplayName;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
public int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
And now your code will look something like this:
public class Stage : Enumeration
{
public TimeSpan TimeSpan { get; private set; }
public static readonly Stage One
= new Stage (1, "Stage one", new TimeSpan(5));
public static readonly Stage Two
= new Stage (2, "Stage two", new TimeSpan(10));
public static readonly Stage Three
= new Stage (3, "Stage three", new TimeSpan(15));
private EmployeeType() { }
private EmployeeType(int value, string displayName, TimeSpan span) : base(value, displayName)
{
TimeSpan = span;
}
}
Once you have that set up, you can just store the .Value in the database. I'm afraid I haven't done it in EF, but in nHibernate it's reasonably straight-forward to tell a property to just store the ".Value" of the property, and you can wire it back up when you load the value by having it call:
Stage.FromValue<Stage>(intValue);
Hold the Stage as a property of your entity, use it the way you're doing and add
Ignore(x => x.Stage)
to your mapping. This will ignore this property when mapping to your database.
Edit: I misinterpreted the question.
If you want just the different stages in your database, you should put the stages in their own table with an ID, and refer to that ID trough a relationship. Every entity will hold an additional reference and you'll have to define relationships for them.
Is this what you were looking for?
I want to create a custom validation attribute, in which I want to compare the value of my property with another property's value in my model class.
For example I have in my model class:
...
public string SourceCity { get; set; }
public string DestinationCity { get; set; }
And I want to create a custom attribute to use it like this:
[Custom("SourceCity", ErrorMessage = "the source and destination should not be equal")]
public string DestinationCity { get; set; }
//this wil lcompare SourceCity with DestinationCity
How can I get there?
Here's how you could obtain the other property value:
public class CustomAttribute : ValidationAttribute
{
private readonly string _other;
public CustomAttribute(string other)
{
_other = other;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_other);
if (property == null)
{
return new ValidationResult(
string.Format("Unknown property: {0}", _other)
);
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
// at this stage you have "value" and "otherValue" pointing
// to the value of the property on which this attribute
// is applied and the value of the other property respectively
// => you could do some checks
if (!object.Equals(value, otherValue))
{
// here we are verifying whether the 2 values are equal
// but you could do any custom validation you like
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
Please look below for my example:
Model class implements INotifyPropertyChanged
public class ModelClass : INotifyPropertyChanged
{
private string destinationCity;
public string SourceCity { get; set; }
public ModelClass()
{
PropertyChanged += CustomAttribute.ThrowIfNotEquals;
}
[Custom("SourceCity", ErrorMessage = "the source and destination should not be equal")]
public string DestinationCity
{
get
{
return this.destinationCity;
}
set
{
if (value != this.destinationCity)
{
this.destinationCity = value;
NotifyPropertyChanged("DestinationCity");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Attribute class also contains event hendler.
internal sealed class CustomAttribute : Attribute
{
public CustomAttribute(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName { get; set; }
public string ErrorMessage { get; set; }
public static void ThrowIfNotEquals(object obj, PropertyChangedEventArgs eventArgs)
{
Type type = obj.GetType();
var changedProperty = type.GetProperty(eventArgs.PropertyName);
var attribute = (CustomAttribute)changedProperty
.GetCustomAttributes(typeof(CustomAttribute), false)
.FirstOrDefault();
var valueToCompare = type.GetProperty(attribute.PropertyName).GetValue(obj, null);
if (!valueToCompare.Equals(changedProperty.GetValue(obj, null)))
throw new Exception("the source and destination should not be equal");
}
}
Usage
var test = new ModelClass();
test.SourceCity = "1";
// Everything is ok
test.DestinationCity = "1";
// throws exception
test.DestinationCity ="2";
To simplify code I decided to omit a validation.
The best way to do this, is through of IValidatableObject. See http://msdn.microsoft.com/en-us/data/gg193959.aspx
I have a similar problem like this :
Many to many object to object relation in C#
However, imagine that the Championship would have a "Last Date Played" property (just as an example) that would map to any Participant. In this case, where would that property end up? Is it a must to create an intermediate class? (which i wouldn't want to do) what option do i have? thanks!
One way would be to have an array on each object containing pointers to the other objects either via an dictionary that stores the object as key and date as value (or a custom property class for any number of properties) or using a wrapper class around the object and a plain list, this wrapper should then implement the decorator pattern to allow direct access to the object together with any unique properties.
The wrapper object could use an internal object for the properties that is shared between the oposing wrapper objects for the 2 different objects so that any property is in sync.
Another way would be a separate list of pairs where one is wrapped like the above.
The later makes it easy to loop over all objects.
Here is a code example, it might not be exactly what you need but it might give you the basics of my idea.
void Main()
{
var p = new Player("David");
var c = new Championship("Chess");
p.LinkChampionship(c, DateTime.Now);
p.Dump();
}
// Define other methods and classes here
class Player : Properties {
public virtual String Name {get; set;}
public List<ChampionshipWrapper> champs = new List<ChampionshipWrapper>();
public Player() {
}
public Player(string name) {
Name = name;
}
public void LinkChampionship(Championship champ, DateTime when) {
var p = new Properties(when);
champs.Add(new ChampionshipWrapper(champ, p));
champ.players.Add(new PlayerWrapper(this, p));
}
}
class Championship : Properties {
public virtual String Name { get; set; }
public List<PlayerWrapper> players = new List<PlayerWrapper>();
public Championship(){}
public Championship(string name) {
Name = name;
}
public void LinkPlayer(Player play, DateTime when) {
var p = new Properties(when);
players.Add(new PlayerWrapper(play, p));
play.champs.Add(new ChampionshipWrapper(this, p));
}
}
class Properties {
public virtual DateTime LastPlayed { get; set; }
public Properties() {
}
public Properties(DateTime when) {
LastPlayed = when;
}
}
class PlayerWrapper : Player {
private Player player;
private Properties props;
public PlayerWrapper(Player play, Properties prop) {
this.player = play;
this.props = prop;
}
public override String Name {
get { return this.player.Name; }
set { this.player.Name = value; }
}
public override DateTime LastPlayed {
get { return this.props.LastPlayed; }
set { this.props.LastPlayed = value; }
}
}
class ChampionshipWrapper : Championship {
private Championship champ;
private Properties props;
public ChampionshipWrapper(Championship c, Properties prop) {
this.champ = c;
this.props = prop;
}
public override String Name {
get { return this.champ.Name; }
set { this.champ.Name = value; }
}
public override DateTime LastPlayed {
get { return this.props.LastPlayed; }
set { this.props.LastPlayed = value; }
}
}