locked
Basic architecture related question in WPF MVVM Light RRS feed

  • Question

  • Hi

    We have just started working on WPF. 
    We have designed below architecture for our application; please let me know if anyone has any input in this:

    1. View – All XAML Window and User Controls
    2. ViewModel – ViewModel class for each view
    3. Service – Multiple service classes for view model (Generally one service class for one view model)
    4. Business - Business logic methods which will be called from Services or View Models
    5. Model – Entity Framework object and partial classes of entity
    6. Common - View Administrator and Enum class
    7. Converters – Value Converters which will be used in ViewModel

    We are using MVVM light to develop this application and running through below basic questions:

    1. If we are using Entity Framework then can we skip Service Classes (we are not using WCF)?
    2. In ViewModel can we write basic data access methods in Service classes and RelayCommand methods (which will be called from ViewModel) in Business classes (BLL)? (I mean to say is this correct method?)

    Appreciate all your help and suggestions.
    Thanks in advance.

    Regards
    Somnath


    Tuesday, January 5, 2016 8:24 AM

Answers

  • >1. If we are using Entity Framework then can we skip Service Classes (we are not using WCF)?

    Probably.

    It kind of depends what they do but you will probably find you have 1:1 with your data access to a method in a viewmodel.

    For testing purposes, you will often need to be able to mock your database or the results of reading it.

    Actually reading a database is not practical in automated tests. At least not unit/fast tests you run in visual studio. Too slow.

    2. In ViewModel can we write basic data access methods in Service classes and RelayCommand methods (which will be called from ViewModel) in Business classes (BLL)? (I mean to say is this correct method?)

    It's often more convenient for your business layer and model to be merged.

    Consider the approach I use in this:

    https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204

    Take a look in EntityData.

    Here's a buddy and partial class for one of the entitites - Customer.

    namespace wpf_EntityFramework.EntityData
    {
    
       [MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]
        public partial class Customer : BaseEntity
        {
           public void MetaSetUp()
           {
            // In wpf you need to explicitly state the metadata file.
            // Maybe this will be improved in future versions of EF.
               TypeDescriptor.AddProviderTransparent(
                   new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Customer),
                   typeof(CustomerMetadata)),
                   typeof(Customer));
           }
           internal sealed class CustomerMetadata
           {
               [Required]
               [StringLength(80, MinimumLength=4, ErrorMessage="Invalid customer name")]
               public string CustomerName { get; set; }
               [Required]
               [StringLength(80, MinimumLength = 4, ErrorMessage = "Invalid first line of address")]
               public string Address1 { get; set; }
               [Required]
               [StringLength(80)]
               public string Address2 { get; set; }
               [StringLength(80)]
               [Required]
               public string Address3 { get; set; }
    
               [StringLength(50, MinimumLength = 4, ErrorMessage = "Invalid Town or City")]
               [Required(ErrorMessage = "Town or City is a required field")]
               public string TownCity { get; set; }
    
               [StringLength(8)]
               [RegularExpression("(GIR 0AA)|((([A-Z-[QVX]][0-9][0-9]?)|(([A-Z-[QVX]][A-Z-[IJZ]][0-9][0-9]?)|(([A-Z-[QVX]][0-9][A-HJKSTUW])|([A-Z-[QVX]][A-Z-[IJZ]][0-9][ABEHMNPRVWXY])))) [0-9][A-Z-[CIKMOV]]{2})"
                   , ErrorMessage= "Invalid pattern post code")]
               [Required(ErrorMessage = "PostCode is a required field")]
               public string PostCode { get; set; }
    
               [Range (0,50000, ErrorMessage= "Credit Limit must be between 0 and 50,000" )]
               [DataType(DataType.Currency)]
               public Nullable<decimal> CreditLimit { get; set; }
               [Required]
               [DataType(DataType.Currency)]
               public Nullable<decimal> Outstanding { get; set; }
    
                private CustomerMetadata()
               {
                   var propertiesToValidate = GetType()
                                              .GetRuntimeProperties()
                                              .Where(c => c.GetCustomAttributes(typeof(ValidationAttribute)).Any());
               }
           }
        }
    }

    Notice how that is adding inheritance to a base class and a bunch of dataannotations.

    The dataannotations are great for basic validation. ( As used in that app ).

    You can add any extra wrapping and validation methods you need for your business logic in that partial class without changing the model directly.

    Each of those is also wrapped with a viewmodel, even if just shown in a datagrid.

    .

    This approach will work find for small to medium sized systems.

    Larger ones and those which share data feeds across some enterprise appfabrik or a bunch of rabbitmyq and web servers... those will need more sophistication.

    If you need that sort of thing then you need an architect - but you wouldn't be asking here.

    .

    By the way.

    With MVVM the viewmodel is an adapter between view and model.

    You usually need less converters because you can (mostly ) wrap a property out your model with a property in a viewmodel and do whatever translation, calculation etc that you want to in there.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Tuesday, January 5, 2016 8:53 AM
  • >>1. If we are using Entity Framework then can we skip Service Classes (we are not using WCF)?

    Entity Framework is a data access technology and doesn't generally replace the need for client side services. In an N-tier application, Entity Framework belongs to the Data Access Layer (DAL). A client side service may call a remote service that connects to a business layer, that in turn connects to a DAL that may or may not use Entity Framework. The important thing is that the actual retrieval of the data is abstracted away by the service. And the view model is typically injected with a service interface and uses this reference to read or write data without knowing anything about its implementantion, i.e. whether Entity Framework is used somewhere in the backend or not.

    >>2. In ViewModel can we write basic data access methods in Service classes and RelayCommand methods (which will be called from ViewModel) in Business classes (BLL)? (I mean to say is this correct method?)

    A view model should not contain any data access methods. It should contain a reference to a service that is responsible for returning the data. The service may fetch the data directly from a database in a small application, or connect to a remote business or service layer (for example via WCF) to get the data. But the view model doesn't know or care where this data comes from. And the view model should not contain any business logic either. It may reference a business object (or a service that communicates with the business layer).


    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Tuesday, January 5, 2016 7:02 PM

All replies

  • >1. If we are using Entity Framework then can we skip Service Classes (we are not using WCF)?

    Probably.

    It kind of depends what they do but you will probably find you have 1:1 with your data access to a method in a viewmodel.

    For testing purposes, you will often need to be able to mock your database or the results of reading it.

    Actually reading a database is not practical in automated tests. At least not unit/fast tests you run in visual studio. Too slow.

    2. In ViewModel can we write basic data access methods in Service classes and RelayCommand methods (which will be called from ViewModel) in Business classes (BLL)? (I mean to say is this correct method?)

    It's often more convenient for your business layer and model to be merged.

    Consider the approach I use in this:

    https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204

    Take a look in EntityData.

    Here's a buddy and partial class for one of the entitites - Customer.

    namespace wpf_EntityFramework.EntityData
    {
    
       [MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]
        public partial class Customer : BaseEntity
        {
           public void MetaSetUp()
           {
            // In wpf you need to explicitly state the metadata file.
            // Maybe this will be improved in future versions of EF.
               TypeDescriptor.AddProviderTransparent(
                   new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Customer),
                   typeof(CustomerMetadata)),
                   typeof(Customer));
           }
           internal sealed class CustomerMetadata
           {
               [Required]
               [StringLength(80, MinimumLength=4, ErrorMessage="Invalid customer name")]
               public string CustomerName { get; set; }
               [Required]
               [StringLength(80, MinimumLength = 4, ErrorMessage = "Invalid first line of address")]
               public string Address1 { get; set; }
               [Required]
               [StringLength(80)]
               public string Address2 { get; set; }
               [StringLength(80)]
               [Required]
               public string Address3 { get; set; }
    
               [StringLength(50, MinimumLength = 4, ErrorMessage = "Invalid Town or City")]
               [Required(ErrorMessage = "Town or City is a required field")]
               public string TownCity { get; set; }
    
               [StringLength(8)]
               [RegularExpression("(GIR 0AA)|((([A-Z-[QVX]][0-9][0-9]?)|(([A-Z-[QVX]][A-Z-[IJZ]][0-9][0-9]?)|(([A-Z-[QVX]][0-9][A-HJKSTUW])|([A-Z-[QVX]][A-Z-[IJZ]][0-9][ABEHMNPRVWXY])))) [0-9][A-Z-[CIKMOV]]{2})"
                   , ErrorMessage= "Invalid pattern post code")]
               [Required(ErrorMessage = "PostCode is a required field")]
               public string PostCode { get; set; }
    
               [Range (0,50000, ErrorMessage= "Credit Limit must be between 0 and 50,000" )]
               [DataType(DataType.Currency)]
               public Nullable<decimal> CreditLimit { get; set; }
               [Required]
               [DataType(DataType.Currency)]
               public Nullable<decimal> Outstanding { get; set; }
    
                private CustomerMetadata()
               {
                   var propertiesToValidate = GetType()
                                              .GetRuntimeProperties()
                                              .Where(c => c.GetCustomAttributes(typeof(ValidationAttribute)).Any());
               }
           }
        }
    }

    Notice how that is adding inheritance to a base class and a bunch of dataannotations.

    The dataannotations are great for basic validation. ( As used in that app ).

    You can add any extra wrapping and validation methods you need for your business logic in that partial class without changing the model directly.

    Each of those is also wrapped with a viewmodel, even if just shown in a datagrid.

    .

    This approach will work find for small to medium sized systems.

    Larger ones and those which share data feeds across some enterprise appfabrik or a bunch of rabbitmyq and web servers... those will need more sophistication.

    If you need that sort of thing then you need an architect - but you wouldn't be asking here.

    .

    By the way.

    With MVVM the viewmodel is an adapter between view and model.

    You usually need less converters because you can (mostly ) wrap a property out your model with a property in a viewmodel and do whatever translation, calculation etc that you want to in there.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Tuesday, January 5, 2016 8:53 AM
  • >>1. If we are using Entity Framework then can we skip Service Classes (we are not using WCF)?

    Entity Framework is a data access technology and doesn't generally replace the need for client side services. In an N-tier application, Entity Framework belongs to the Data Access Layer (DAL). A client side service may call a remote service that connects to a business layer, that in turn connects to a DAL that may or may not use Entity Framework. The important thing is that the actual retrieval of the data is abstracted away by the service. And the view model is typically injected with a service interface and uses this reference to read or write data without knowing anything about its implementantion, i.e. whether Entity Framework is used somewhere in the backend or not.

    >>2. In ViewModel can we write basic data access methods in Service classes and RelayCommand methods (which will be called from ViewModel) in Business classes (BLL)? (I mean to say is this correct method?)

    A view model should not contain any data access methods. It should contain a reference to a service that is responsible for returning the data. The service may fetch the data directly from a database in a small application, or connect to a remote business or service layer (for example via WCF) to get the data. But the view model doesn't know or care where this data comes from. And the view model should not contain any business logic either. It may reference a business object (or a service that communicates with the business layer).


    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Tuesday, January 5, 2016 7:02 PM
  • Thank you so much Andy and Magnus for your quick responses.

    Actually yesterday I immediately started reading on DataAnnotation and INotifyDataErrorInfo so delayed in responding to this thread.

    But want little more clarification on second question.

    As Magnus mentioned I am referring to Service Class Interface (Methods) in ViewModel to get the data. Service Classes are calling Functions/Methods from BLL.

    So is it ok if I call Service Interface Methods in ViewModel to get the data and call BLL Class methods to execute RelayCommands? 

    The reason why I am asking this question is, whenever I am writing any method in Service Interface Class I have to implement that in Actual Data Access Service class (which executes at run time) and Design Time Data Access Service class (Which runs design time - My static data). So if I call RelayCommand methods from services then I have to implement that in both classes, and putting relaycommand methods in Design Time Service Class is of no use.

    I hope you understood the questions.

    Please suggest.

    Regards

    Somnath

    Wednesday, January 6, 2016 8:09 AM
  • I'm not sure I follow your point there.

    In the code I posted above the partial class is effectively the business layer. That has business logic in dataannotations and methods.

    There is no service layer.

    You can split that out but remember when you create another separate layer you are creating two interfaces that your data and metadata needs to cross.

    I have two mocking approaches I use for small - mid sized apps.

    I either inject a mock datacontext and hence a sort of in memory database.

    Or

    I inject all or some of the various get, insert, update and delete methods in the viewmodel.

    This is pretty similar to injecting a service/class as another layer, but often it's just the get I inject this way in order to give design time data.

    I'm not really going to test writing to a database except manually or with overnight CI testing. Both use real databases.

    .

    ps

    It's not always practical to keep all business logic in a layer separate from the viewmodel. Sometimes you're best (effectively) repeating business logic in the viewmodel.

    Expensive tasks where you have to go read stuff off a server might take so long they don't fit in with validating on change submit.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Wednesday, January 6, 2016 8:52 AM
  • >>So is it ok if I call Service Interface Methods in ViewModel to get the data

    Yes.

    >>...and call BLL Class methods to execute RelayCommands?

    I don't know what exactly what you mean here. If you are referring to RelayCommands as a type of command that implements the ICommand interface, the BLL classes should not have any knowledge about these. The ICommand properties belong to the view model and you bind to these from the view. Please refer to my blog post for more information: http://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/. The view model may know about the business layer and call methods of the BLL classes but the BLL classes don't know anything about the view models.

    >>The reason why I am asking this question is, whenever I am writing any method in Service Interface Class I have to implement that in Actual Data Access Service class (which executes at run time) and Design Time Data Access Service class (Which runs design time - My static data).

    You always have to implement an interface so this is nothing strange.

    >>So if I call RelayCommand methods from services then I have to implement that in both classes, and putting relaycommand methods in Design Time Service Class is of no use.

    Implementing the interface twice is the price you pay for design time support.

    Hope that helps.

    Please remember to mark all helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Wednesday, January 6, 2016 6:35 PM