| Author |
Share Topic Topic Search Topic Options
|
Peer
Newbie
Joined: 31-Aug-2010
Location: Netherlands
Posts: 15
|
Post Options
Quote Reply
Topic: View Model architecture Posted: 24-Sep-2010 at 5:01am |
I'm working on my first self-made DevForce driven application implementing the VM concept.
The VM is obvious a mediator between the view an the model, right ? I would prefer a generic VM (like the prismExplorer) which drives an entity in the entitymanager and provide it with navigation events and other standard factorymehods. Specific Viewmodels are subclassed from the generic ViewModel. The ViewModel is actualy a wrapper around an entity.
Why this architecture? We are used to have an Entity ebedded in a BO class and provide the entity whith generic behaviour or even specific behaviour (adding rules, defaul values, pre-post crud hooks) These BO's could relate to other BO's in a parent-child relation. In this way cascading event were fired surch as Save-delete. Transaction behaviour was activated in the parent BO so all the child BO's travelled inside the Parent Transaction.
BO's could be added at designtime to a class (an Event Object) to perform bigger tasks, e.g. the complete functionality of an orderentry module. No UI dependencies at this level. *-- Here is my point / question: Should the new viewmodel be our former Eventobject or Our former BO. ? i.o.w. Is the new ViewModel an (single)Entity driver or a class containing specific tasks (e.g. all the Order entry functions) ? I hope you get my point.
*-- a second question is more technical: In my VM I instantiate a EM (_mgr) and building a query. The result exist as a collection in the query (Entity Job = _mgr.Job)
Why should I create a second collection in my VM ( Jobs = _mgr.job), Why not use the _mgr.job collection for binding ? Right now I have to sync the two collections ( _mgr.EntityChanged += ...)
|
 |
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 24-Sep-2010 at 10:37am |
The ViewModel could be seen as a wrapper around the model elements that are exposed in the view. In many of our examples, we only present a single model element.
But the ViewModel is not just a wrapper. It maintains LOGIC and STATE for the VIEW that it supports. It is UI aware without being bound to the specifics of the View. For example, the VM could receive and handle the "NEXT ITEM" request from the View. The VM doesn't know if the request was a result of a button click, link click, scroll, page-landing ... doesn't know and doesn't care.
The View has no idea whether the move to the next item will be allowed or what might be going on in the background. That's the VM's job. The VM could decide that the user should not move from the current item. Or it might decide to save changes to the current item first. Or it might present a confirmation dialog. Or it might send a message to the world that the user is moving to a new item.
The VM is responsible for all of these potential actions in response to a user gesture. The View shouldn't make such decisions.
And your Model - the Business Objects - don't make these decisions either! They don't know about users and moving from one item to another. They don't know when to save or not to save. They don't send messages to the UI; they don't even know that there is a UI.
The VM is task oriented. The VM says "I support my View by telling it what it should present and what the user can do within this View. I will handle all user requests from my View ... typically by delegating to other classes that carry out my instructions."
No, the VM is never an entity. If I understand properly, it won't be your "former EventObject or B.O." either
In sum: the VM is far more than a friendly facade over the Model or its entities.
It's worth reading about MVVM elsewhere. You might look at MVVM Light for a simple but useful example of the pattern.
--
You wrote: "In my VM I instantiate a EM (_mgr) and building a query. "
I do that in demos but not in real code. I have a firm rule: the VM never talks directly to an EntityManager. It always talks to an injected helper class that is responsible for acquiring the entities. That helper class is often known as a "repository".
The repository has access to the EntityManager and actually issues the queries. The repository exposes a method (e.g. "GetCurrentCustomers" ) that encapsulates the query. The VM would call upon the repository to fill a target collection (e.g., "customers = repository.GetCurrentCustomers()" ). The View binds to this VM collection.
My examples assume a synchronous data access approach for simplicity of exposition here. You'd need an asynchronous interface for a Silverlight client.
The separation is critical for testable and maintainable code.
It is also necessary to pour the query results into a collection that supports the View. The EntityManager returns a kind of List. In WinForms, WPF, or Silverlight you likely want a different kind of list ... one that is both richer and more specific to the binding framework ... like ObservableCollection<T>.
HTH
Edited by WardBell - 24-Sep-2010 at 10:39am
|
 |
BillG
DevForce MVP
Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
|
Post Options
Quote Reply
Posted: 24-Sep-2010 at 1:25pm |
So the ViewModel calls the CustomerRepository class and retrieves the customer. What if I also need a list of offices, trade codes, statuses, etc. Do I create a separate repository for each of these. Should I have a equal number of repositories for all my classes that handles the CRUD methods?
Bill
|
 |
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 24-Sep-2010 at 5:55pm |
Great question.
My answer: try to write one repository that exposes the methods you need for the module. If the module needs "offices, trade codes, statuses", then the Repository gets them.
Why: I orient the Repository to the specific needs of a module ... and strive for ONE repository per module. I drive the Repository API from the needs of the module and the scenarios it supports.
----
Discussion:
Your repository won't expose "Get..." methods for all of these types. That's a bad idea. Many of these types are just reference entities that should be held in cache and accessed by your primary business objects via navigation.
For example, "Order.OrderStatus" could return an "OrderStatus" reference entity. If I don't need an OrderStatus method on my repository, why bother? I just need to be able to get to OrderStatus codes from my Order.
Therefore, I won't have a "repository.GetOrderStatus()" method ... or one for TradeStatus or any other of these auxilliary reference entities. Instead, my repository will have an initialization moment during which it acquires the reference entities en masse.
Aside: you often need to populate ComboBoxes with reference entities. When that happens, you can write a generalized reference entity retriever for this purpose. Maybe you write a "service" that provides codes by name or type. Such a service would delegate to a repository to do the work. Of course the repository would not have a separate method for each code type.
The repository could offer "GetActiveCustomers" and "GetCustomerOpenOrders" methods if those had business domain meaning and if (really only if) one of the module's ViewModels or some other module service component needed such methods.
Ward's rule: Do not anticipate. Write a repository member "just-in-time" ... when some module component actually needs that member.
A corollary of this approach: "IRepository<T>" is never useful. I never seem to find a generalization that pays off.
Btw, you don't write CUD methods on a repository. I don't even have a "Save" on my repositories. "Save" is a method on the UnitOfWork container (I call it a "gateway"). The UoW actually owns my EntityManager which it shares with the Repository.
Does that seem like too many moving parts? Why would I separate the thing that saves from the thing that gets? Because I have many screens that can only get and present. They aren't allowed to save. "Save" is a potentially dangerous operation. I don't want my ViewModel to be able to save unless it is supposed to be able to save.
By separating the "Saver" from the "Getter", I can safely give the "Getter" (the repository) to all kinds of ViewModels ... and never lose sleep over one of them trying to Save. I give the "Saver" (the "Gateway") only to those VMs that really, really need it.
Such separation is overkill for small modules but is a lifesafer in bigger ones. When I start to have a lot of screens - a lot of V/VM pairs, I work hard to make sure that all my parts stay small, simple, and do only what they need and should do.
Hope that makes sense to you.
----
"This doesn't sound like the repository pattern I've read about"
Most discussions of the Repository pattern are conducted by Web developers who are trying to satisfy a single web request. A proper web request controller maintains no state between requests and each individual request isn't going to need much in the way of entities. A web developer doesn't cope with a variety of long-lived entities. That's why you see them talk about IRepository<T> and about "aggregates".
On the other hand, RIA and desktop applications maintain lots of state for prolonged periods of time. A given module instance will juggle a great variety of entity types at any one time.
Desktop and RIA apps have a completely different paradigm - one that requires a completely different take on the repository pattern.
That said, the fundamental insight - encapsulation of queries and cached business objects within a "Repository" component that reifies the container metaphor - this is as useful to us as it is to web developers.
Edited by WardBell - 24-Sep-2010 at 6:13pm
|
 |
BillG
DevForce MVP
Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
|
Post Options
Quote Reply
Posted: 25-Sep-2010 at 8:56am |
|
I wish Ideablade had a sample Silverlight or Wpf application that illustrated all these patterns (MVVM,Repository,Unit of Work etc.).
|
 |
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 25-Sep-2010 at 12:41pm |
|
Guess what I'm working on :-)
|
 |
paul
Newbie
Joined: 16-Sep-2010
Posts: 19
|
Post Options
Quote Reply
Posted: 25-Sep-2010 at 4:39pm |
I hate to be the guy that asks "when" but... I am starting a large new SL app and I am trying my hand at DevForce. I am coming off a major WPF application that used CSLA. I would love to see the concepts you described previously in a real-world application. At this point I would love to see your post above sprinkled with code examples. Your spin on the repository pattern intrigues me since I am old asp.net hack.
I am also studying the limits of EF4. My old WPF/CSLA app used LinqToSql (L2S) for the DAL. We broke the L2S models up based on our form structure/unit of work since our database has almost a 1000 tables and views. As you can imagine we have hundreds of L2S models. I don't regret the decision, it works well for that project. Researching EF4 it is basically understood that VS or maybe EF itself can't handle very large EF models. I am not sure what people consider large? 10, 50, 100 entities? I have been reading and watching your posts on the subject and I am considering this: Our new application will have a very large SQL Azure DB. We are planning to have multiple SL XAPs loaded on demand broken up by the business verticals we want to attack; CRM, payroll, AR, AP, etc... One EF model for each business verticals with either one matching DevForce domain model or domain models broken down further by form/unit of work. Currently I am worried with having multiple domain models for one EF model that I might have to replicate business logic within the domain models. Like if I have a rule for the customer object but that object is in many domain models. I haven't found a good way to handle that yet.
I am not looking for free enterprise application design, I am willing to do the footwork but I want to make sure I am going in the right direction. Easier to turn around now than 12 months down the road.
Thanks...
|
 |
hijaggu
Newbie
Joined: 26-Sep-2010
Posts: 21
|
Post Options
Quote Reply
Posted: 26-Sep-2010 at 1:21pm |
Hi ,
I like the idea and advantages of implementing repository. Though I see some chanllanges while implementing in real world application. Say for example , how to keep list of customers supplied by repository to VM up-to-date in below case.
customers = repository.GetCurrentCustomers()
we are using SQLDependancy to get notifications if there is any change in Customer table and to keep local cache up-to-date. If there are couple of inserts / updates / deletes on Customer table , we just fetch the delta records ( recently added / modified / deleted records since last refresh ).
One way of keeping customers list up-to-date with the cache is using EntityListManager like below but then we are breaking encapsulation and accessing EntityManager _em1 out side the repository ,
var filter = new Predicate<Customer>( delegate(Customer anCustomer) { return anCustomer.City == "NewYork"; });
_CustomerEntityListManager = new EntityListManager<Customer>(_em1, filter, null);
bool refreshListWhenPlacedUnderManagement = true; _employeeEntityListManager.ManageList( customers , refreshListWhenPlacedUnderManagement);
Would you please suggest me if there is any soluton for such chanllange ?
|
 |
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 27-Sep-2010 at 3:12pm |
@paul - The large model question is an important one ... and one I address with heat and enthusiasm. Just not here :-). Try this forum post.
|
 |
WardBell
IdeaBlade
Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
|
Post Options
Quote Reply
Posted: 27-Sep-2010 at 3:25pm |
@hijaggu - I would delegate wiring of a list manager to the repository. This is a perfect example of how the Repository can help you.
Could you put that into GetCurrentCustomers?
If the filter changes from one usage to another, could you change the signature to GetCurrentCustomers(filter)?
If you want to separate managed from unmanaged customer lists, could you create a GetCurrentCustomerAsManagedList(yourCustList, filter) method?
Perhaps you want to keep the notification apart from the Repository ... maybe put it in a RemoteStorageChangeNotificationService. Give that a method to register (and unregister?) the list and filter. That service can have access to the gateway.
The objective is to restrict the number of EntityManager access points to give you better control, testability, and quality.
Obviously you don't want to hamstring daily development. But ... really ... do you want the kind of list manager registration code you showed lurking in all of your ViewModels? That's seven tricky lines of persistence that have nothing to do with presenting information in the view. ViewModels don't care about that stuff.
Better to delegate to a service (the Repository or NotificationService) that is responsible. I believe your code will look a lot cleaner and you'll sleep better.
HTH
|
 |
hijaggu
Newbie
Joined: 26-Sep-2010
Posts: 21
|
Post Options
Quote Reply
Posted: 27-Sep-2010 at 3:29pm |
|
Thanks a lot ..I appriciate your help :)
|
 |
BillG
DevForce MVP
Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
|
Post Options
Quote Reply
Posted: 28-Sep-2010 at 2:14am |
So I would have a MemberRepository class that would have a GetMembers() method along with helper objects that would populate dropdowns etc such as GetStatuses(), GetTradeCodes(), GetJobs() etc.
I would have a MemberUnitOfWork class that would have CreateMember(), UpdateMember(), SaveMember, and DeleteMember.
I have a MembersListView which displays a grid of all members depending on search criteria and a MemberView should displays information about a paticular member. Is it ok to have both of them share the same ViewModel and that ViewModel would access the MemberRepository and MemberUnitOfwork classes.
|
 |
alindzon
Newbie
Joined: 28-Jul-2010
Location: Toronto
Posts: 31
|
Post Options
Quote Reply
Posted: 03-Oct-2010 at 7:37am |
Does anyone have any example of a repository that supports get and save and viewmodel, so I can see what one looks like that is properly constructed?
If it happens to be a for use with raddatagrid or a similar control even better. I am trying to understand how it all fits together.
Thank you in advance.
|
 |
BillG
DevForce MVP
Joined: 05-Dec-2007
Location: Monroe, MI
Posts: 233
|
Post Options
Quote Reply
Posted: 12-Oct-2010 at 9:11am |
Here is an example of a method in a class called MembersRepository that retrieves members from the members table based on a query. In my ViewModel class, I have a method called FetchMembers that calls this method. This method returns the members back to a Member ObservableCollection object in the ViewModel. The ViewModel as a result of the INotifyPropertyChanged interface automatically binds the Members collection to the appropriate control which in my case is a datagrid.
private readonly LaborWareEntities mgr = SingletonEntityManager.GetManager();
public ObservableCollection<Member> Members { get; private set; }public ObservableCollection<Member> GetMembers(MemberQO q)
{
Members = new ObservableCollection<Member>();
var query = from Member in mgr.Members
where Member.LastName.StartsWith(q.LastName) && Member.FirstName.StartsWith(q.FirstName) && Member.Status == q.Status && Member.Office == q.Office
orderby Member.LastName, Member.FirstName
select Member;
query.ExecuteAsync(op => op.Results.ForEach(Members.Add));
return Members;
}
You mention about being for use in raddatagrid or a similar control. Your repository class does not konw anything about your Views or whats on those views. It is strictly for retrieving the data from the datasource.
|
 |
Peer
Newbie
Joined: 31-Aug-2010
Location: Netherlands
Posts: 15
|
Post Options
Quote Reply
Posted: 12-Oct-2010 at 9:42am |
Thank Bill for your suggestions :).
I'v implemented a similar strategy in my project but different in a important way. The repository class is an abstact class which I can instantiate in my viewmodel. The result is an observable collection (Items)
for example: Companies = new Repository<Company>(_mgr); Projects = new Repository<Project>(_mgr);
the result are two object whith an observable collection items ... Companies.Items[] and Projects.items[] default properties also Companies.CurrentItem and Project.CurrentItem to which the selecteditem attribute in Xaml can be bound.
I inject the where clause with a lambdaexpression: Projects.Where = p => p.companyid == Companies.CurrentItem.id;
This scenario works perfect for me :) but maybe not for everone ....
Best regards, Peer
|
 |
alindzon
Newbie
Joined: 28-Jul-2010
Location: Toronto
Posts: 31
|
Post Options
Quote Reply
Posted: 12-Oct-2010 at 2:44pm |
I really appreciate the feedback BillG and Peer. As a complete beginner at MVVM and Devforce I am trying to picture what the XAML, viewmodel, and repository all look like in one example where the UI has a datagrid which implies the filters and sorting are being controlled by the user in real time.
I think the choice of which datagrid is not really relevant, since they all have filtering and sorting built in.
And alternatively a scenario where the query is fixed and the parameters have been passed into the viewmodel when its called into being. For example product details being requested by a product grid, could launch a product details view.
And finally, what about data you want to persist for as long as the application is running, for example I have all the states/provinces in a table to allow two letter abreviations to be used but related back to the full name.
I would love to see any examples of how this is done in repository, viewmodel, and view so that it all works without loading the whole database in the client.
I am trying to wrap my head around how one designed repositories, viewmodels, and views in these cases. Please tell me if I am getting it or if I am confused.
My understanding is the repository should be somewhat abstract but tailored to meet both the needs Peer outlines as well as some more tailored queries like BillG outllines. I also understand certain rules and business logic can be placed in the repository, such as the code necessary to assing a unique ID when a new item is created as well as key data validation. One recommendation I have heard is to have one repository for each major class of data. For example customers and ship to addresses might share a single repository.
The viewmodel controls the views access to data exposed by the repository. Its the viewmodel that glues the two together and controls the interaction, and contains more specific businss rules, and that understands errors and results generated by the repository.
And finally the view itself just controls the presentation and manages the interaction with the user, and lets the viewmodel do the real work.
|
 |