Powered by MSDN

US - English
NEW! Silverlight 5 is available Learn More

MVVM and WCF RIA Services: abstracting RIA Service deta... RSS

23 replies

Last post Feb 08, 2011 11:31 PM by onecurlyfry

(0)
  • lein4d

    lein4d

    Member

    11 Points

    24 Posts

    MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 27, 2011 02:14 PM | LINK

    When using MVVM Pattern with WCF RIA Services, is it better to wrap the client side RIA code into service classes than to have it directly inside viewmodel? In many of the recent MVVM examples I've seen, the classes are separated into ViewModel, IServiceAgent, and ServiceAgent classes, in which only the ServiceAgent Class knows about RIA details. I've been following this approach for a while, but I found it too cumbersome to create all these boilerplate codes each time I create another ViewModel. The application I'm creating is doesn't have unit tests, and is not making use of design-time datas. in this case, is it still necessary to abstract away the RIA service details using IServiceAgent and ServiceAgent classes? I think it'll be more productive and lot less duplicate code to have DomainContext class directly inside ViewModel and load entities from there. What is the rationale behind having IServiceAgent and ServiceAgent classes? as for sharing queries between ViewModels, isn't it sufficient to put shared queries in the server side?

    thanks for your time.

    MVVM WCF RIA Service

  • ColinBlair

    ColinBlair

    All-Star

    29787 Points

    5012 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 27, 2011 02:58 PM | LINK

    There is no global "better" way to do things. There are anti-patterns that you should avoid (like using ObservableCollection instead of EntityList) but I do not consider having your DomainContext inside your ViewModel an anti-pattern. If you have a 1:1 correlation between your ViewModel and your DomainContext then feel free to skip the ServiceAgent part.

    Edit: After careful thought, and some well deserved corrections later in this thread, I am changing my mind here. I think Kyle and I both made the same mistake here so let me backtrack this a little. Kyle and I both think that John Papa's example of a ServiceAgent can be a little complicated for all situations. However, the basic concept of the ServiceAgent, moving the service code outside of the ViewModel, is very important and not something that should be skipped. However, as Ward said, you do not need a separate ServiceAgent for every ViewModel. Also, if the IServiceAgent design is more complicated then you really need you can substitute your own simpler design if you wish. My original comment that there is no best way to do things still stands, I will not say that any given design of a service agent is the best and only way to do things, but I will say that you should at least have something that separates the service calls outside of the ViewModel.

    It is when you have the same DomainContext instance being shared between multiple ViewModels that ServiceAgent becomes important. In that situation, you do not want to have one ViewModel doing a clear (or a Submit for that matter) on the DomainContext when the other ViewModel has unsaved changes that it is still working on.

    There are at least three methods that you can use:

    1) DomainContext in the ViewModel
    2) John Papa style ServiceAgent (this is what you linked to)
    3) ServiceAgent that exposes the EntityContainer

    That last one hasn't been shown around as much, in that one you give the ViewModel access to the EntityContainer. That gives you full access to the EntitySets but doesn't allow you to initiate loads, submits, or invokes.

  • sjb500

    sjb500

    Member

    303 Points

    248 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 27, 2011 03:41 PM | LINK

    What are these anti-patterns you mention? We tend to use ObservableCollections all over the place - are we doing something wrong? Thanks

  • ColinBlair

    ColinBlair

    All-Star

    29787 Points

    5012 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 27, 2011 04:25 PM | LINK

    Sorry, very late on coming up with a blog entry on this. Just hit a lot of roadblocks and I am waiting for some more info.

    First, an important distinction between a bad idea and an anti-pattern is that a bad idea is just a bad idea. An anti-pattern looks, sounds, and acts like a good idea. It is usually when you start trying to scale the pattern up that it reveals its limitations.

    Scenario 1: You have an ObservableCollection which contains Customer entities. Some other place in your code you have a place that deletes customers. This code may be in a separate view with a separate view model but you are sharing the same DomainContext at the service agent level. So that other code, which was written by a different programmer without your knowledge, deletes the Customer and that change is saved to the DomainService. That customer is still in your ObservableCollection, still displayed on screen, but now has an EntityState of Detached. The user then does something to update that Customer and it sets an associated property. Lets say, CustomerType was set. Since that Customer entity was detached, it is now added to the DomainContext as a new customer. This is the exact kind of problem that can easily slip through your own testing and make it into a final product. Now you have users complaining that deleted data keeps randomly reappearing, not a good place to be.

    Scenario 2: This is actually the same scenario, but instead of deleting the customer in the second view it was just detached (or EntitySet.Clear was used). In this case, the first view is still going to re-add the Customer as a new entity but the result is going to be duplicate data in the database instead of deleted data reappearing.

    The other problem with ObservableCollection is that ObservableCollection is editable. This means that it looks like you should be able to add new entities directly to the collection when, unless you have done the work, this isn't the case. That is why the LoadOperation's Entities property returns a ReadOnlyObservableCollection to keep you from making that same mistake.

    Now, any time you are putting entities into lists, dictionaries, etc. you are open to the same problem but these situations are especially bad with the ObservableCollection as we assume that ObservableCollection will tell us when things change and it makes us a little complacent.

    The solution is the EntityList which is part of the SP1 Toolkit release. The EntityList inherits ObservableCollection, which means you can pass it around as an ObservableCollection, but you have to pass the EntitySet into its constructor. Because it has that reference, when you delete an entity from the EntityList it deletes it from the EntitySet. When you add something to the EntityList then, if it isn't already there, it adds it to the EntitySet. When an entity is deleted or deached from the EntitySet then it automatically disappears from the EntityList. The converse isn't true, items added to the EntitySet to not automatically appear in the EntityList. If you want that functionality then you want to use CollectionViewSource to create an ICollectionView for you.

  • kylemc

    kylemc

    Contributor

    7243 Points

    1147 Posts

    Microsoft

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 28, 2011 12:11 AM | LINK

    I agree with Colin here. There are many ways to implement the MVVM pattern. John's sample is great, but I consider it a little more complex than most RIA developers need. Feel free to use the DomainContext directly in your ViewModel.

  • WardB

    WardB

    Member

    92 Points

    29 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 29, 2011 06:42 PM | LINK

    And now ... a dissenting voice.

    Hey Colin - I'm not sure what to make of the difference between a bad idea and an anti-pattern. You say a bad idea is bad from the start whereas an anti-pattern looks like a good idea at the start, revealing it's failings over time. I don't know an honest person who deliberate implements an idea that is bad from the start. Oh well.

    Lein4d asks why ViewModels should delegate persistence concerns to a Service Agent (aka Data Service, aka Repository) if he's not going to write automated tests or design with data. Then we lecture him on the evils of ObservableCollection? That's like berating an alcoholoic smoker for eating a twinkie. I don't think we have our priorities in order.

    [aside: I appreciate your critque of the OC and the risks it presents. I'm rarely delete entities - I think this causes no end of trouble - so my code is less vulnearable to the dangers you enumerate. But you make a strong case.]

    @Lein4d, why are you using ViewModel at all? Why not put everything in the code behind? It's a lot easier and there are fewer classes to worry about. The primary benefits of VM are single responsibility, design/development independence, and testability. You don't seem to be interested in any of them. If not, then the pattern isn't pulling its weight.

    -- Moving on to my main point: "Service Agent" is a necessity --

    I don't have a lot of hard and fast rules. I do have a few. This is one of them.

    "No persistence machinery in the ViewModel"

    I'm going to try to persuade you of this even if you don't care whether your ViewModels are readable (sigh), don't test your ViewModels (sigh), and don't design with data (sigh).

    I'm going to make my case solely on the Single Responsibility Principle ... and its implications for consistency and maintainability. If these characteristics don't matter to you ... stop reading ... and stop bothering with MVVM or any other pattern.

    I ask all of my customers to write ViewModels that concentrate on providing state (data) and behavior (logic) that their views need. Complex tasks that are not strictly concerned with the view (e.g., how to query for entities or save them) should be delegated to supporting "services". A focused ViewModel is easier to understand and easier to test [oops ... I mentioned those points again]. It is also less vulnerable to changes that have nothing to do with the View.

    The DomainContext itself is already a step down this road. Without it, in the extreme case, your ViewModel would be making its own connections to the server, issuing web requests on a background thread, waiting for the response, intepreting the response, transferring data into entities and marshalling them back to the UI thread.  You wouldn't dream of writing that code in each of your Views or ViewModels. The DC abstracts that for you.

    But it's not a sufficient abstraction for the ViewModel. There are still too many mechanics. Let's get concrete.

    Suppose the ViewModel provides Customers to the View. That's really all the VM cares about ... some notion of a list of Customers. It wants to have a method like "GetCustomers". Why should it care about how that method is implemented?

    Should that method return all customers or only active customers? Only customers that the current user is allowed to see? Can it be customers in cache or should it be customers fresh from the database? Should the query be for customers only or should the query include related entities as well? Should the customers be sorted? Is there an upper bound on the number of Customers to retrieve for this view? Should the query use an existing DomainContext or create a new one? What if the user isn't authenticated? Or not authorized? What if the DC can't connect to the server? What if the server returns an error?

    These are all important considerations. None of them have anything to do with getting customers on to the screen ... which is the VM's job.

    Will you worry about all of these considerations on day one? Probably not. But when they do become important, would you expect to edit all of your ViewModels to address them? Or would you expect to edit a Data Service to address them?

    For me the answer turns on this: would changing the way I get the customers change the appearance of the view or how the user interacts with the view? If the answer is "no", then the code belongs in a Data Service, not the ViewModel.

    Let's generalize that thought. You should be able to answer this question for any code in your ViewModel: would changing this code alter the appearance of the view or how the user interacts with the view? If "yes", the code can stay. If "no", the code belongs elsewhere, in a helper class to which the VM delegates.

    This is NOT a hypothetical issue or an academic discussion. You asked for advice rooted in experience. Presumably if you were certain, you wouldn't ask. So listen up to someone who has been working in this corner of .NET since 2003.

    I've looked at a lot of customer code. That's part of my job description: work with customers who are building smart client / RIA applications. Obviously I wasn't always looking at Silverlight code; usually it was Windows Forms where the corresponding collaborator is "Presenter" in an MVP pattern. Every time I saw persistence code in the VM or Presenter, I saw trouble. Every one of those VMs or Ps implemented queries differently ... often the same query. Yup ... I'd see three or four variations on GetCustomers, all of them unintentionally different.

    The variations had multiple explanations. Maybe devs had understood the business rules differently. Maybe the rules changed as the application evolved. Maybe the devs (or the same dev at different times) didn't understand how the persistence machinery works. Maybe awareness of potential errors came and went.

    Whatever the cause, it's a mess ... a mess spread across your code base. Get that stuff out of there. Put it in a Data Service where you can find it, watch it, and tune it. 

    P.S.: If you ever decide to test your ViewModels and/or test the many ways that your application accesses data, you'll be glad you made this move.

    A final detail. Your question seemed to imply that you were creating a new "ServiceAgent" and interface for each ViewModel. You wrote "I found it too cumbersome to create all these boilerplate codes each time I create another ViewModel."

    I would find that too cumbersome as well. I tend to write one "ServiceAgent" (DataService, Repository, whatever) per module. That's not a strict rule. But I do find that I use the same service across multiple VMs.  John Papa's tiny BookClub example has a single "BookService" that does the trick for both the BookViewModel and the CheckoutViewModel. You really don't need a lot of these services in your application.

  • ColinBlair

    ColinBlair

    All-Star

    29787 Points

    5012 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 29, 2011 09:20 PM | LINK

    WardB

    Hey Colin - I'm not sure what to make of the difference between a bad idea and an anti-pattern. You say a bad idea is bad from the start whereas an anti-pattern looks like a good idea at the start, revealing it's failings over time. I don't know an honest person who deliberate implements an idea that is bad from the start. Oh well.

    The common definition for an anti-pattern is, "a pattern that may be commonly used but is ineffective and/or counterproductive in practice". An earlier defintion was, "obvious, but wrong, solutions to recurring problems" which I like since it also explains why the word pattern is being used. Honest people implement bad ideas all the time, they just don't know it is a bad idea. I should have worded that to say that an anti-pattern is still a bad idea, it is just a special type of bad idea.

    WardB

    Lein4d asks why ViewModels should delegate persistence concerns to a Service Agent (aka Data Service, aka Repository) if he's not going to write automated tests or design with data. Then we lecture him on the evils of ObservableCollection? That's like berating an alcoholoic smoker for eating a twinkie. I don't think we have our priorities in order.

    Where exactly was I lecturing him on the evils of ObservableCollection? Don't take my reply to a second question as being a lecture to the original question.

    WardB

    [aside: I appreciate your critque of the OC and the risks it presents. I'm rarely delete entities - I think this causes no end of trouble - so my code is less vulnearable to the dangers you enumerate. But you make a strong case.]

    Me either, actually my database is versioned so in a way I don't event update.

    WardB

    @Lein4d, why are you using ViewModel at all? Why not put everything in the code behind? It's a lot easier and there are fewer classes to worry about. The primary benefits of VM are single responsibility, design/development independence, and testability. You don't seem to be interested in any of them. If not, then the pattern isn't pulling its weight.

    I disagree with this statement, not with the rest of it. I think everything else you say is true Ward and has a lot of merit and I agree with almost everything you said. However, the primary purpose of a ViewModel is to be the binding source of the View. The View cannot bind to itself, you need the ViewModel to be the source for everything that is being bound. The basic structure of a View, which is just the UI, and a ViewModel which is the binding source (and lets face it, is really the code behind moved into a separate file) is still an important basic pattern and a good starting place. Everything else that the ViewModel allows can grow from that. I am not comfortable telling someone that that they are a bad programmer because they don't have five classes, three interfaces, and a testing project just to write a simple hello world application. Yes, you can use a ViewModel without a separate service class. Would a service class be better? Of course it would, all of the stuff you mentioned would be great but it is certainly not required.

  • lee_sl

    lee_sl

    Contributor

    4222 Points

    864 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 29, 2011 09:32 PM | LINK

    ColinBlair

    However, the primary purpose of a ViewModel is to be the binding source of the View. The View cannot bind to itself, you need the ViewModel to be the source for everything that is being bound.

    I would say the purpose is for testability. if you dont plan to write test(s), there is no point in going mvvm route. Get all the data in the view codebehind and just set DataContext = this and you are done

     

    ----------------------------------------------
    Available for consulting in Dallas, TX
    http://leeontech.wordpress.com/
  • WardB

    WardB

    Member

    92 Points

    29 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 29, 2011 10:32 PM | LINK

    Hi Colin - I'm going straight to the meat which is the MVVM discussion.

    You know I hold you in the highest regard. I know you would use a service in your own work so I'm not challenging how you would write your code. I'm challenging your advice to someone trying to grasp MVVM and struggling with it.

    I know that we're all a little skittish about laying down the law on patterns. Many of the debates turn ridiculous with absolutist statements that are little more than personal preferences.

    However, some times things are what they are. Don't drink and drive. You can't jump off a 10 story building and expect to live. Put the clothes in the hamper not on it. These are statements we can make with confidence and we do the honest questioner a disservice when we pretend to uncertainty.

    I have seen many justifications for ViewModel. I have not, until this moment, seen one that mentioned "data binding" as the reason to do it.

    The primary purpose of a ViewModel is not data binding. Data binding matters because MVVM isn't worth trying unless you have a strong data binding infrastructure. That explains why we're hearing about MVVM now instead of five years ago. The XAML platforms made MVVM feasible. But that's not a reason to use it.

    The best reasons to use it that I have seen are Single Responsibility (or Separation of Concerns if you prefer), Testability, and Designer/Developer independence.

    Btw, you absolutely CAN bind a view to itself. I just did it. , set "DataContext = this" in the View's constructor, added a public string property, and bound a UI control to that property. Worked fine. Didn't even have to implement INotifyPropertyChanged.  I'm not recommending this; I'm just saying.

    The ViewModel is not simply the code-behind moved to a separate file. That's how you refactor to ViewModel but that is not what a ViewModel is. A ViewModel is a separate CLASS. That means it can be instantiated independently of the View ... which in turn means it can be tested and repurposed with other views.

    Let me quickly agree that I would not call someone a bad programmer if they refused to implement "Hello, World" in six classes. Quite the contrary. You ARE a bad programmer if you implement "Hello, World" with six classes - unless you're joking or teaching MVVM :)  "Hello, World" - and its real world equivalents - should be written in as few classes as possible.

    But @Lein4d isn't asking for advice on building "Hello, World". We must presume that he is building a sizeable application, a Line-Of-Business application, with lots of views (more than 5). If he were building anything less I'd say "stop ... don't bother." For "Hello, World" the answer is "don't use MVVM", not "it's ok to use a DataContext in your ViewModel."

    MVVM only makes sense in a data-orietned application of some size with at least a one year lifespan. When MVVM makes sense - when used in other than a toy application - than the VM should delegate persistence chores to a helper service class.

    Not only am I comfortable mandating a service class. I'm extremely *uncomfortable* suggesting that you can write MVVM application without one.

    I tell everyone "when I code-review your VM, I don't want to see any reference to DC. That is an instant fail." That's the kind of unambiguous guidance that we all crave.

    P.S.: This rule can't guarantee that the program will be good. There are myriad ways you can screw this up. I am sure that there are DataService implementations that make matters worse than if the devs just put the persistence code in the VM. You can say that about anything, true? When recommending a course of action, our job is to ensure that the recommendation is clear and easy to follow and the developer's obligations is to make an effort and do a decent job.

    --

    @lee_sl: I feel strongly about automated testing too. Fortunately MVVM is valuable because it facilitates SRP and Design/Developer indepenence as well. If it were not, MVVM would have been abandoned long ago. Testing gets a lot of lip service. I can count on one hand the number of ViewModel testing examples that I've seen ... in demos or in production. Testability alone is rarely a sufficient reason to adopt a pattern.

  • ColinBlair

    ColinBlair

    All-Star

    29787 Points

    5012 Posts

    Re: MVVM and WCF RIA Services: abstracting RIA Service details?

    Jan 30, 2011 03:38 AM | LINK

    WardB

    Btw, you absolutely CAN bind a view to itself. I just did it. , set "DataContext = this" in the View's constructor, added a public string property, and bound a UI control to that property. Worked fine. Didn't even have to implement INotifyPropertyChanged.  I'm not recommending this; I'm just saying.

    You caught me there. A few years ago when I was first using Silverlight (that was SL2), I had another programmer try to do just and it didn't work at all. Looking back, I am not sure if that was an actual SL2 problem or just his bad code but it was something I still believed without realizing it did have a lot of backing.

    WardB

    But @Lein4d isn't asking for advice on building "Hello, World". We must presume that he is building a sizeable application, a Line-Of-Business application, with lots of views (more than 5). If he were building anything less I'd say "stop ... don't bother." For "Hello, World" the answer is "don't use MVVM", not "it's ok to use a DataContext in your ViewModel."

    I think you make a good point here because I was assuming the exact oppposite and re-reading the question I think you are correct here and I made a mistake. My original understanding was that lien4d was saying he has been using the DataService and was asking if he always needed to use a DataService every time he was creating an application.

    The question now that I re-read it is about new ViewModels within that same application that already has data services in it. That is an entirely different kettle of fish. If the application is complex enough that you are complaining about creating additional ViewModels and services for that application then saying don't worry about it was completely the wrong answer and Ward, you are completely correct to call BS on my original answer.

    WardB

    MVVM only makes sense in a data-orietned application of some size with at least a one year lifespan. When MVVM makes sense - when used in other than a toy application - than the VM should delegate persistence chores to a helper service class.

    That I still disagree with, but only because while I agree with a lot of what you said it will take a lot more convincing then that to get me to agree that putting code in the code behind is ever a good idea. I will be the first to admit that my original ViewModels from when I was first learning Silverlight (mostly still in production now) are horrible in retrospect. While the load and save code is not in the ViewModels themselves, it is in the base class of the ViewModel and not a separate service class. I suppose someone might argue that there is little difference between the separate ServiceClass and the base class I was using but that someone isn't me. However, refactoring that code to use a ServiceClass and bring it up to my current standards has turned out to be a relatively painless thing to do. If I had left all of the code in the code behinds then my current refactoring task would be a complete rewrite, not just a minor refactoring. So, in my experience, using the MVVM pattern to write an application still results in a more maintainable application even if you do it wrong. Still, there is a huge difference between being happier that someone is at least trying to follow the pattern and endorsing doing it wrong.

    I do want to correct one misunderstanding:

    WardB

    The ViewModel is not simply the code-behind moved to a separate file. That's how you refactor to ViewModel but that is not what a ViewModel is. A ViewModel is a separate CLASS. That means it can be instantiated independently of the View ... which in turn means it can be tested and repurposed with other views.

    I was commenting that if you have a ViewModel without a separate service class then the code in the ViewModel is the same code that would originally be in the code behind. I was saying that it had the same utility as the code behind, that is pretty the opposite of my point.

    OK, that out of the way, I am about to re-edit my original answer to get rid of the original error. Ward, please let me know if I put my foot in it again.