Here is the PostSharp support discussion (titled: "NotifyPropertyChanged and ObservableCollection") that sparked my initial confusion on this topic. Further explanation is in the answer I posted (not 100% confident in my answer).
I'm a little confused on how PostSharp's [NotifyPropertyChanged] aspect affects a class's collection properties (if at all), and whether I need to be using an ObservableCollection<> or AdvisableCollection<>. If I understand correctly, if I want notifications from the collection, I MUST change it to at least an ObservableCollection<>, correct? The [NotifyPropertyChanged] aspect doesn't magically make whatever collection types are in the class observable.
If that's the case, then when would I use ObservableCollection<> or AdvisableCollection<>? Should AdvisableCollection<> be reserved for when I need the aggregation pattern applied by PostSharp? Or should I just always use AdvisableCollection<> whenever applying PostSharp's [NotifyPropertyChanged] attribute?
[NotifyPropertyChanged]
public class Test {
public int PropVal { get; set; }
public List<string> PropCollection { get; set; } //Should this be ObservableCollection
//or AdvisableCollection?
}
Congratulations on the Tumbleweed badge and many apologies for the delayed answer.
Collections are currently not well integrated with the aspect in sense that there is a bit of work needed for real-life examples to work properly. Let me explain few key steps:
[AggregateAllChanges] instructs the runtime that it should relay any change observed on the collection as a change of the property itself. This in particular means that a virtual Item[] dependency is created (which is used by a "standard" collections to notify change of collection shallow "state").
In reality properties working with collections content are usually aggregates in some sense, depending not only on set of items stored in the collection but also on state of individual objects in the collection. Currently there is no way to express that and/or selectively relay these changes. For this you need to create a class derived from ObservableCollection<T> that looks like following:
[NotifyPropertyChanged]
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (T item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= OnItemPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (T item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += OnItemPropertyChanged;
}
}
base.OnCollectionChanged(e);
}
protected void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChangedServices.SignalPropertyChanged(this, "Item[]");
NotifyCollectionChangedEventArgs collectionChangedEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
base.OnCollectionChanged(collectionChangedEventArgs);
}
}
The above does exactly what is needed - when anything changes on any object within the collection, collection reports that it itself had changed.
Now in a class it would look like this:
[NotifyPropertyChanged]
public class TestClass
{
[AggregateAllChanges]
public ObservableCollectionEx<TestItem> Items { get; } = new ObservableCollectionEx<TestItem>();
[SafeForDependencyAnalysis]
public int Sum
{
get
{
if (Depends.Guard)
{
Depends.On(this.Items);
}
return this.Items.Sum(x => x.Value);
}
}
}
[NotifyPropertyChanged]
public class TestItem
{
public int Value { get; set; }
}
The above is not ideal performance-wise if there are other properties on the TestItem class that are frequently changed. In that case, one may consider to add a some kind of filtering to ObservableCollectionEx.
Regarding AdvisableCollection<T> you are right - while it can be used, it's main purpose is different.
Well, after receiving the "Tumbleweed" badge on this question (not intended, but funny... better than downvotes), I've spent some more time trying to sort out/better define my confusion and coalesce my own answer from the chaos...
The PostSharp support discussion (titled: "NotifyPropertyChanged and ObservableCollection") that sparked my initial confusion mentions two things:
-Support for intercepting changes in ObservableCollection<> was added in 4.2.
-To raise the PropertyChanged event on each collection change, the collection must be marked with [AggregateAllChanges].
So it would seem PostSharp's NotifyPropertyChanged aspect supports at least ObservableCollection<> if the [AggregateAllChanges] attribute is applied. However, a subsequent conversation on the PostSharp site's chat feature yielded:
-"You need to use collection classes defined by PostSharp to have reliable support for NotifyPropertyChanged aspect. See Working With Collections" (which mentions the Aggregatable pattern).
-After reading the provided documentation, the conclusion seems to be that I need to use AdvisableCollection<> (a PostSharp-defined collection) instead, but the examples all seem to be in the context of the Aggregatable pattern, with parent-child relationships, etc. Hence my confusion. I'm not trying to set up parent-child relationships, just trying to get NotifyPropertyChange on collections.
Next is another support discussion that demonstrates using NotifyPropertyChanged with a non-PostSharp-defined collection, applying [AggregateAllChanges] to a BindingList<>.
-In this instance, BindingList<> did not have one of the events that NotifyPropertyChanged expected, so this had to be wired up manually with NotifyPropertyChangedServices.
For a bit more background, I read this PostSharp blog post that gets into the Aggregation and Composition patterns.
Trying to boil everything down, my (possibly incorrect) conclusion is:
-[AggregateAllChanges] applied to non-PostSharp-defined collections (ObservableCollection<>, BindingList<>) is effectively an opt-in to the NotifyPropertyChanged aspect, with the caveat that the collection you apply it to (ex. BindingList<>) may not support all of the events the aspect expects, requiring you to wire up the PropertyChanged notification yourself via NotifyPropertyChangedServices.
-AdvisableCollection<> implements INotifyPropertyChanged as well as INotifyCollectionChanged (as does ObservableCollection<>), can be used in place of standard .net collections (not BindingList<> though... different interfaces), and would still require [AggregateAllChanges] to raise PropertyChanged notifications. Otherwise, the purpose of AdvisableCollection<> is to allow injection of behaviors related to the Aggregatable pattern.
If anyone has corrections to these assumptions, please feel free :) I will gladly mark a clearer/better explanation as the answer.
Related
Last day I ended up thinking about some collections interdependence.
I cannot put a better label, name, title, or whatever suggesting this situation... so feel free to suggest anything better than my cheap labeling, that is " Interdependent Collections Modifications Management".
Long story short, the example below.
First, let's consider a couple of classes:
public class DummyItem
{
public DummyItem(String name)
{
this.Name = name;
}
public String Name { get; set; }
}
// A kind of repository of DummyItems
public class BusinessClassProvider
{
public BusinessClassProvider()
{
this.Collection = new List<DummyItem>();
}
public IList<DummyItem> DummyItems{ get; private set; }
}
public class BusinessClassConsumer
{
public BusinessClassConsumer()
{
this.Collection = new Collection<DummyItem>();
}
public ICollection<DummyItem> DummItems{ get; private set; }
}
Now let's say that BusinessClassProvider.Collection is populated via some processes, user inputs, etc.
The BusinessClassConsumer Collection is something selected from BusinessClassProvider Collection by let's say also some user inputs or any other way.
Now if I remove or add a DummyItem from the BusinessClassProvider Collection I need to create Additional Nethods to wrap or eventually if they are some events supported to notify the others of what have been changed and remove accordingly the related other Collections (e.g. BusinessClassConsumer one) to also remove this item.
Basically it means a lot of boilerplate code and a lot of stuff to handle manually.
Of course, we could go with another strategy using IEnumerable as BusinessClassConsumer Collection but it does not provide the same options about indexing for instance, furthermore it forces to iterate over and over on BusinessClassProvider Collection.
I know it raises many more questions like which collection is kinda the provider and the others which consumes from.
Is there any frameworks via Attributes, or any other nasty tricks to enforce the dependency between several collections?
By the way, once again, is there anybody who knows how to label correctly this sort of situation?
If your need is to be notified when a collection change (item added, removed or the whole collection refreshed) you might want to use an ObservableCollection.
It's used mostly in WPF to notify the UI when a collection of item has changed and needs to be refreshed.
If your need is a producer/consumer collection, you might want to look for a BlockingCollection.
It's a thread safe collection that do all the producer/consumer things for you.
Yes, there are. Quite a few actually, but ObservableCollection that come with .net are not preferred. Instead one would use Rx-Linq(Reactive extension) that adds observables, Linq and schedulers together.
I have a Node object with a Parent that i have bound in my xaml like so,
<Label>
<Hyperlink>
<TextBlock Text="{Binding Path=Node.Parent.Name}"/>
</Hyperlink>
</Label>
My ViewModel looks something like this
public class NodeViewModel
{
public Node Node { get; set; }
public NodeViewModel(Node model)
{
Node = model;
if(model.Parent != null) { } // Check if it's null, then do nothing.
// When the above line is commented out, my label displays nothing.
}
}
Why is it that when my if statement is commented out, the label/textblock is blank? Am I doing something wrong? Does my object both exist and not exist until I check if it's null?
Edit:
Forgot to mention, my node class is pretty simple, and does implement INotifyPropertyChanged for the Name property.
2nd Edit: Added my simple Node class.
[ImplementPropertyChanged] // From Fody.PropertyChanged
public class Node
{
public int? ParentID { get; set; }
public Node Parent { get; set; }
public string Name { get; set; }
public Node Node(Node p = null)
{
Parent = p;
}
}
From comments:
It is actually. It is proxied by Entity Framework. Could that be the issue? If so, is there a less hackish way to fix this?
If the problem really lies in the proxy, then, well, no. You'll get a hard time with proxies/wrappers and WPF and change-notifications, really.
WPF's Binding engine does not work with proxies. At all. Well, maybe unless they are really well written and complex. So, usually, never. This comes form the fact how INPC interface works:
void PropertyChanged(object sender, .... args)
WPF tracks the sender and Binding.Source. If a Source of a Binding is an object called "Z", that is a Proxy to an object "A", then any notifications originating from "A" that are forwarded by proxy "Z" to the WPF's engine are .. discarded because for WPF the "Z" is the Source and it does not match the advertised sender "A".
I battled with this issue for quite a long time, and the only solution I found is to have the Proxy translate the P-Changed event so that the sender=A is subsituted for Z. This can have some nasty memory leaks when not written properly. It's because it's hard to "map" a delegate to a new delegate and provide proper disposal and GCing of both of them. And usually even this is only possible if you build your own proxies with ie. Castle or similar library. I have not found any dynamic-proxy library that supports sender-replacement out of the box.
If you know any - pleease let me know!
Anyways, be careful with the terminology. I mean, proxies. An object "Z" that wraps and trims or extends operations on another different original object "A". Your case may be different. If your so-called "proxy" is a dynamic subclass of your type and if it overrides any virtual methods/properties/events you have in your original class, then it is not the Proxy I meant. In this case, the 'proxy' and the original object is the same object, it's just that you see it as a class A and the actual class is Z:A, and for WPF the Source matches the sender. If I remember correctly, this is how EF often works.
Therefore, the first thing I'd check is to inspect who is really the culprit. Maybe Fody not EF? Try to remove that magic ImplementPropertyChanged, try to implement INPC manually in ALL classes. And I mean ALL. Both in Node and in NodeViewModel. If you are lazy, you can propdp and use DependencyProperty instead. If it works - then start removing the manual INPC and replacing it with ie. that from Fody's. When things start to break, gather all results and reanalyze.
Also, check what Fody does - maybe it provides a transparent proxy/wrapper in some point?
EDIT: Considering that your code starts working when you 'touch' the Fody'ied object, I'd start blaming Fody. Maybe it is because the NodeViewModel.Node property is currently not tracked bu Fody? Have you tried marking it with ImplementPropertyChanged too? It is a pure guess and it'd mean that this library has some serious problems, but it's quick and worth trying.
I think you are missing to implement INPC on the ViewModel. The binding won't update back to the UI when you did Node = node. Raise a property changed event on the Node setter.
I'm looking for a clean and elegant solution to handle the INotifyPropertyChanged event of nested (child) objects. Example code:
public class Person : INotifyPropertyChanged {
private string _firstName;
private int _age;
private Person _bestFriend;
public string FirstName {
get { return _firstName; }
set {
// Short implementation for simplicity reasons
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
public int Age {
get { return _age; }
set {
// Short implementation for simplicity reasons
_age = value;
RaisePropertyChanged("Age");
}
}
public Person BestFriend {
get { return _bestFriend; }
set {
// - Unsubscribe from _bestFriend's INotifyPropertyChanged Event
// if not null
_bestFriend = value;
RaisePropertyChanged("BestFriend");
// - Subscribe to _bestFriend's INotifyPropertyChanged Event if not null
// - When _bestFriend's INotifyPropertyChanged Event is fired, i'd like
// to have the RaisePropertyChanged("BestFriend") method invoked
// - Also, I guess some kind of *weak* event handler is required
// if a Person instance i beeing destroyed
}
}
// **INotifyPropertyChanged implementation**
// Implementation of RaisePropertyChanged method
}
Focus on the BestFriend Property and it's value setter. Now I know that I could do this manually, implementing all steps described in the comments. But this is going to be a lot of code, especially when I'm planning to have many child properties implementing INotifyPropertyChanged like this. Of course they are not going to be always of same Type, the only thing they have in common is the INotifyPropertyChanged interface.
The reason is, that in my real scenario, I have a complex "Item" (in cart) object which has nested object properties over several layers (Item is having a "License" object, which can itself have child objects again) and I need to get notified about any single change of the "Item" to be able to recalculate the price.
Do you some good tips or even some
implementation to help me to solve
this?
Unfortunately, I'm not able/allowed to use post-build steps like PostSharp to accomplish my goal.
since I wasn't able to find a ready-to-use solution, I've done a custom implementation based on Pieters (and Marks) suggestions (thanks!).
Using the classes, you will be notified about any change in a deep object tree, this works for any INotifyPropertyChanged implementing Types and INotifyCollectionChanged* implementing collections (Obviously, I'm using the ObservableCollection for that).
I hope this turned out to be a quite clean and elegant solution, it's not fully tested though and there is room for enhancements. It's pretty easy to use, just create an instance of ChangeListener using it's static Create method and passing your INotifyPropertyChanged:
var listener = ChangeListener.Create(myViewModel);
listener.PropertyChanged +=
new PropertyChangedEventHandler(listener_PropertyChanged);
the PropertyChangedEventArgs provide a PropertyName which will be always the full "path" of your Objects. For example, if you change your Persons's "BestFriend" Name, the PropertyName will be "BestFriend.Name", if the BestFriend has a collection of Children and you change it's Age, the value will be "BestFriend.Children[].Age" and so on. Don't forget to Dispose when your object is destroyed, then it will (hopefully) completely unsubscribe from all event listeners.
It compiles in .NET (Tested in 4) and Silverlight (Tested in 4). Because the code in seperated in three classes, I've posted the code to gist 705450 where you can grab it all: https://gist.github.com/705450 **
*) One reason that the code is working is that the ObservableCollection also implements INotifyPropertyChanged, else it wouldn't work as desired, this is a known caveat
**) Use for free, released under MIT License
I think what you're looking for is something like WPF binding.
How INotifyPropertyChanged works is that the RaisePropertyChanged("BestFriend"); must only be fored when the property BestFriend changes. Not when anything on the object itself changes.
How you would implement this is by a two step INotifyPropertyChanged event handler. Your listener would register on the changed event of the Person. When the BestFriend gets set/changed, you register on the changed event of the BestFriend Person. Then, you start listening on changed events of that object.
This is exactly how WPF binding implements this. The listening to changes of nested objects is done through that system.
The reason this is not going to work when you implement it in Person is that the levels can become very deep and the changed event of BestFriend does not mean anything anymore ("what has changed?"). This problem gets larger when you have circular relations where e.g. the best friend of your monther is the mother of your best fiend. Then, when one of the properties change, you get a stack overflow.
So, how you would solve this is to create a class with which you can build listeners. You would for example build a listener on BestFriend.FirstName. That class would then put an event handler on the changed event of Person and listen to changes on BestFriend. Then, when that changes, it puts a listener on BestFriend and listens for changes of FirstName. Then, when that changes, it sends raises an event and you can then listen to that. That's basically how WPF binding works.
See http://msdn.microsoft.com/en-us/library/ms750413.aspx for more information on WPF binding.
Interesting solution Thomas.
I found another solution. It's called Propagator design pattern. You can find more on the web (e.g. on CodeProject: Propagator in C# - An Alternative to the Observer Design Pattern).
Basically, it's a pattern for updating objects in a dependency network. It is very useful when state changes need to be pushed through a network of objects. A state change is represented by an object itself which travels through the network of Propagators. By encapsulating the state change as an object, the Propagators become loosely coupled.
A class diagram of the re-usable Propagator classes:
Read more on CodeProject.
I have been Searching the Web for one day now and I found another nice solution from Sacha Barber:
http://www.codeproject.com/Articles/166530/A-Chained-Property-Observer
He created weak references within a Chained Property Observer. Checkout the Article if you want to see another great way to implement this feature.
And I also want to mention a nice implementation with the Reactive Extensions #
http://www.rowanbeach.com/rowan-beach-blog/a-system-reactive-property-change-observer/
This Solution work only for one Level of Observer, not a full Chain of Observers.
I wrote an easy helper to do this. You just call BubblePropertyChanged(x => x.BestFriend) in your parent view model. n.b. there is an assumption you have a method called NotifyPropertyChagned in your parent, but you can adapt that.
/// <summary>
/// Bubbles up property changed events from a child viewmodel that implements {INotifyPropertyChanged} to the parent keeping
/// the naming hierarchy in place.
/// This is useful for nested view models.
/// </summary>
/// <param name="property">Child property that is a viewmodel implementing INotifyPropertyChanged.</param>
/// <returns></returns>
public IDisposable BubblePropertyChanged(Expression<Func<INotifyPropertyChanged>> property)
{
// This step is relatively expensive but only called once during setup.
MemberExpression body = (MemberExpression)property.Body;
var prefix = body.Member.Name + ".";
INotifyPropertyChanged child = property.Compile().Invoke();
PropertyChangedEventHandler handler = (sender, e) =>
{
this.NotifyPropertyChanged(prefix + e.PropertyName);
};
child.PropertyChanged += handler;
return Disposable.Create(() => { child.PropertyChanged -= handler; });
}
Check-out my solution on CodeProject:
http://www.codeproject.com/Articles/775831/INotifyPropertyChanged-propagator
It does exactly what you need - helps to propagate (in elegant way) dependent properties when relevant dependencies in this or any nested view models change:
public decimal ExchTotalPrice
{
get
{
RaiseMeWhen(this, has => has.Changed(_ => _.TotalPrice));
RaiseMeWhen(ExchangeRate, has => has.Changed(_ => _.Rate));
return TotalPrice * ExchangeRate.Rate;
}
}
Please take a look at EverCodo.ChangesMonitoring. This is a framework to handle PropertyChanged and CollectionChanged events on arbitrary hierarchy of nested objects and collections.
Create a monitor to handle all change events of the object tree:
_ChangesMonitor = ChangesMonitor.Create(Root);
_ChangesMonitor.Changed += ChangesMonitor_Changed;
Do arbitrary modifications on the object tree (all of them will be handled):
Root.Children[5].Children[3].Children[1].Metadata.Tags.Add("Some tag");
Root.Children[5].Children[3].Metadata = new Metadata();
Root.Children[5].Children[3].Metadata.Description = "Some description";
Root.Children[5].Name = "Some name";
Root.Children[5].Children = new ObservableCollection<Entity>();
Handle all events in one place:
private void ChangesMonitor_Changed(object sender, MonitoredObjectChangedEventArgs args)
{
// inspect args parameter for detailed information about the event
}
My question is : how to move beyond writing a custom implementation of a technique for databinding multiple controls (controls without built-in DataSource properties), for each possible type of data, to simple properties ... as described and demonstrated in code that follows ... to achieve a more poweful solution that will be independent of whether the binding is to a string, or an int, or other types.
My guess is: this will involve reflection; but, I'm stuck at that point. I'm looking for strategic advice on which "direction" to move next, hints, clues, not a complete code answer, but of course I appreciate all responses, and I'll sure study code if you post code in reply ! Marc Clifton's 2005 article on CodeProject Simple Databinding: appears to demonstrate a reflection based approach: but, honestly, I do not really grok his code, and, in terms of .NET, 2005 is a long time ago.
Background: Partly in response to various SO questions and answers, like: Update Usercontrol on Three Forms: I've evolved a successful technique for databinding text properties of various controls simultaneously to one source defined in a Public class; also been able to "abstract" some of the details of the binding process using a static class that defines one extension method, and two public methods.
I've verifed that TextBoxes on Controls in a "MainForm," TextBoxes on a UserControl on the MainForm, and a TextBox on a second Form opened "independently" (i.e., form2.Parent == null) all update properly (i.e., two-way binding is in effect) from the "DataSource equivalent" public class. Change one: change all.
Code: an instance of this class will supply the target property (theText) for databinding:
public class TextDataBinder
{
public event PropertyChangedEventHandler PropertyChanged;
private string _theText;
public string theText
{
get { return _theText; }
// note : if 'setter is declared 'internal : blocks
// auto-updating when run-time user modifies consumers
// but will still allow update via code
set
{
_theText = value;
OnPropertyChanged(new PropertyChangedEventArgs("theText"));
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, e);
}
}
}
Code: this static class enables hiding some of the binding process complexity, and allows easy binding to multiple controls:
public static class TextBindingExtender
{
public static TextDataBinder CurrentDataSource;
public static void SetCurrentDataSource(TextDataBinder newCurrentDataSource)
{
CurrentDataSource = newCurrentDataSource;
}
// extension method for Control
public static void AddTextBinding(this Control theControl, string controlPropertyName, string targetPropertyName)
{
theControl.DataBindings.Add(controlPropertyName, CurrentDataSource, targetPropertyName, false, DataSourceUpdateMode.OnPropertyChanged);
}
// bind to all Controls in a List<Control>
public static void AddTextBindings(List<Control> theControls, string controlPropertyName, string targetPropertyName)
{
foreach (Control theControl in theControls)
{
theControl.AddTextBinding(controlPropertyName, targetPropertyName);
}
}
}
How the above classes are used (in a Form Load event) :
// create a new TextDataBinder
TextBindingExtender.CurrentDataSource = new TextDataBinder();
// bind to multiple textboxes, label, on a UserControl, on another Form, etc.
TextBindingExtender.AddTextBindings(new List<Control> { textBox1, textBox2, userControl11.tb, label1, instanceOfForm2.tb }, "Text", "theText");
// test assigning some initial text to the bound property
TextBindingExtender.CurrentDataSource.theText = "some initial text";
It really depends what you want to do; but ultimately common data-binding (for simple properties, done manually) consists of:
obtaining a property; preferably via TypeDescriptor.GetProperties(obj)[propName], giving you an abstraction (PropertyDescriptor)
asking the property if it is read-only (.IsReadOnly)
obtain (or set) the value (.GetValue(), .SetValue())
asking it for a converter to format / parse the value (.Converter, .ConvertFromString(), .ConvertToString()) THIS is a key bit that means you don't have to worry about what the data type is
asking it for the caption (.DisplayName, or .Name if that it empty/null)
asking it if it supports property-specific notification (.SupportsChangeEvents)
asking it to add/remove a change handler (.AddValueChanged(), .RemoveValueChanged())
you might also want to look at whether the object supports centralised notification (look for INotifyPropertyChanged)
If you might be binding to a list rather than a single object:
- the list might be abstracted behind IListSource
- the list might have custom properties, so check for ITypedList
- otherwise, identify the Type of the items and use TypeDescriptor.GetProperties(type)
- you need to consider a "currency manager" (i.e. should all the things bound to the same list be pointing to the same record in the list all the time)
There are also things like ICustomTypeDescriptor and TypeDescriptionProvider to consider, but most of the time TypeDescriptor handles this for you automatically.
As you can see - lots of things to think about! Lots of work... the one thing that you don't have to do is reflection; this is abstracted behind PropertyDescriptor. The reason for this is that not all data is static-typed; think about DataTable - the columns (which map to bindable data properties) are not fixed at compile-time, so reflection isn't appropriate. Likewise, some other types have custom "property bag" implementations. PropertyDescriptor lets your code handle either dynamic (not in the 4.0 sense) and reflective properties identically. It also works nicely with things like "HyperDescriptor", another property customisation.
I was wondering what is the recommended way to expose a collection within a class and if it is any different from the way of doing that same thing when working with NHibernate entities.
Let me explain... I never had a specific problem with my classes exposing collection properties like:
IList<SomeObjType> MyProperty { get; set; }
Having the setter as protected or private gives me some times a bit more control on how I want to handle the collection.
I recently came across this article by Davy Brion:
http://davybrion.com/blog/2009/10/stop-exposing-collections-already/
Davy, clearly recommends to have collections as IEnumerables instead of lets say Lists in order to disallow users of having the option to directly manipulate the contents of those collections. I can understand his point but I am not entirely convinced and by reading the comments on his post I am not the only one.
When it comes to NHibernate entities though, it makes much sense to hide the collections in the way he proposes especially when cascades are in place. I want to have complete control of an entity that is in session and its collections, and exposing AddXxx and RemoveXxx for collection properties makes much more sense to me.
The problem is how to do it?
If I have the entity's collections as IEnumerables I have no way of adding/removing elements to them without converting them to Lists by doing ToList() which makes a new list and therefore nothing can be persisted, or casting them to Lists which is a pain because of proxies and lazy loading.
The overall idea is to not allow an entity to be retrieved and have its collections manipulated (add.remove elements) directly but only through the methods I expose while honouring the cascades for collection persistence.
Your advice and ideas will be much appreciated.
How about...
private IList<string> _mappedProperty;
public IEnumerable<string> ExposedProperty
{
get { return _mappedProperty.AsEnumerable<string>(); }
}
public void Add(string value)
{
// Apply business rules, raise events, queue message, etc.
_mappedProperty.Add(value);
}
This solution is possible if you use NHibernate to map to the private field, ie. _mappedProperty. You can read more about how to do this in the access and naming strategies documentation here.
In fact, I prefer to map all my classes like this. Its better that the developer decides how to define the public interface of the class, not the ORM.
How about exposing them as ReadOnlyCollection?
IList<SomeObjType> _mappedProperty;
return new ReadOnlyCollection<SomeObjType> ExposedProperty
{
get
{
return new ReadOnlyCollection(_mappedProperty);
}
}
I am using NHibernate and I usually keep the collections as ISet and make the setter protected.
ISet<SomeObjType> MyProperty { get; protected set; }
I also provide the AddXxx and RemoveXxx for collection properties where they are required. This has worked quite satisfactorily for me most of the time. But I will say that there have been instances where it had made sense to allow client code add items to the collection directly.
Basically, what I have seen is if I follow the principle of "Tell, Don't Ask" in my client code, without worrying too much about enforcing rigid access constraints on my Domain Object properties, then I always end up with a good design.