Ok, it’s been a while since the DomainCollectionView came out (as luck would have it, it came out just as i finished my own implementation of the same thing). Since then, i switched from my custom implementation to DomainCollectionView (in the following text i’ll just call it DCV) and found some stuff which didn’t quite agree with me so i decided to wrap it.
Just to be clear, wrapping any 3rd party class in your own is a GOOD THING. With DCV however, it proved for me even better. If you’re not familiar with DCV you should probably take a look at the following post:
Basically it’s all what we’ve been looking for, except that instead of getting one class replacing DDS ,we get 2 (which is not a bad thing actually). There’s also a constructor override for DCV which omits the instantiation of loader but that’s something DCV could do without (since it creates a loader internally, and that part is not available for you to mess with).
So how do we use it?
Well, first off i called my wrapper CompositeCollectionView (CCV). It doesn’t inherit from DCV but it contains it. It also contains references to the source and creates a loader internally. If you have a really simple case where you have to use a service and load up the entities from 1 query to a grid, it would look something like this:
public class ActivitiesMasterViewModel : MasterViewModel { private readonly AltiFinContext context; public CompositeCollectionView<ActivityPm> Activities { get; private set; } public ActivitiesMasterViewModel(IToolbarModel toolbarModel, AltiFinContext context) : base(toolbarModel) { this.context = context; Activities = new CompositeCollectionView<ActivityPm>(GetLoadQuery, context.ActivityPms); Activities.Refresh(); } public DomainContext Context { get { return context; } } public LoadOperation<ActivityPm> GetLoadQuery() { return context.Load(context.GetActivitiesQuery().SortPageAndCount(Activities)); } }
Ok, so what we have here is a really simple viewmodel. You can ignore the toolbarmodel part, other than that you have a context, a CCV which is constructed from the defined method and the entitySet. This is not much different from the way you would define the same for DCV, actually the arguments for the DCV constructor are the SAME. What i really wanted to do is avoid doing that method definition on the bottom (which is pretty much always the same for the simple cases), so what CCV really enables me is to define another constructor. The revised VM would look like this:
public class ActivitiesMasterViewModel : MasterViewModel { private readonly AltiFinContext context; public CompositeCollectionView<ActivityPm> Activities { get; private set; } public ActivitiesMasterViewModel(IToolbarModel toolbarModel, AltiFinContext context) : base(toolbarModel) { this.context = context; Activities = CompositeCollectionView<ActivityPm>.Create( context, context.GetActivitiesQuery() ); Activities.Refresh(); } public DomainContext Context { get { return context; } } }
Well, the gain isn’t huge but it’s definitely more concise and reduced on boilerplate code, which is always a good thing. So what do we have here? Well, we have a new constructor for CCV which is defined like this:
public CompositeCollectionView(DomainContextAdapter<T> contextAdapter,Action<LoadOperation<T>> queryCompleted=null) :this(contextAdapter.LoadOp,contextAdapter.EntitySet,queryCompleted) { }
DomainContextAdapter is basically a class which provides a LoadOperation for the default constructor and EntitySet. It is pretty simple actually
public class DomainContextAdapter<T> where T : Entity { private readonly DomainContext context; private readonly EntityQuery<T> query; private CompositeCollectionView<T> cView; public DomainContextAdapter(DomainContext context, EntityQuery<T> query) { this.context = context; this.query = query; } public EntitySet<T> EntitySet { get { return context.EntityContainer.GetEntitySet<T>(); } } public LoadOperation<T> LoadOp() { return context.LoadEx(query, cView); } public void SetCollectionView(CompositeCollectionView<T> collectionView) { cView = collectionView; } }
Next i defined a simple static method Create which looks like this:
public static CompositeCollectionView<T> Create(DomainContext context, EntityQuery<T> query) { return new CompositeCollectionView<T>( new DomainContextAdapter<T>(context, query) ); }
This is all basically done so i can keep my creation as simple as possible.
Now, the first knee-jerk question would be:
Why in the hell did you do all of this? Why didn’t you just define a new constructor inside CCV instead of the “create” method and all of this constructor mess?
Well, my answer would be:
Try it.
You will soon realize (as did I) that you would have to keep a reference to DomainContext inside your CCV. And why is that bad? because you basically don’t need it there. It’s useless and misleading. The only use of it would be that you’d have it in defined in the constructor. Second thing, you’d have to have that LoadOperation defined in the CCV too. Which is also pretty bad and breaks the whole Single Responsibility concept. And last but not least, you’d soon face some constructor code copying, or you would have to refactor constructor code into a separate method and call it inside every constructor. My rule of thumb is usually:
If you HAVE to copy code between constructors, or separate it to a method and call it from both constructor, something is wrong with your class.
There should be one basic constructors, the other ones should just be expanding on that or adapting to it.
Using a separate class for wiring to DomainContext (DomainContextAdapter) which is closely related to CCV is much cleaner and separated solution. This is only a first post about the DomainCollectionView, the following one will contain some implementation details, and afterwards who knows:). Oh and the next one will contain some source code too. So stay tuned:)
rknell Apr 14 , 2011 at 7:59 am /
Would love to hear some of those examples you promised, I too feel this control could be refined further – the very first thing I started doing when I began using it.
Im a big beliver in blend and a total separation of the UI (ie. a viewmodel for a view is WAY to specific and linked – even if not by code but by concept) So im trying to work out how to override the return types on this beast from a generic object to a specific entity (we have to define it anyway)