Building an Enterprise App with WPF, MVVM, and Entity Framework

After you’ve started to build your first enterprise app with WPF, MVVM, and Entity Framework, some questions like these might pop up:

  • how to structure my Solution?
  • how to set up and use Entity Framework?
  • how to structure Views and ViewModels?
  • how to communicate between ViewModels?
  • how to load and save entities
  • how long should I keep Entity Framework’s DbContext alive?
  • how to validate user input?
  • how to work with many-to-many relationships?
  • how to create a tabbed user interfaces?
  • how to handle conflicts when multiple users are using my application?
  • how to define a modern look for my application?

Last week my latest course was released on Pluralsight that answers all of these questions (and a lot more :)). You can watch it here:

Building an Enterprise App with WPF, MVVM, and Entity Framework Code First

This course teaches you everything you need to know to build a solid enterprise application with WPF that uses MVVM and that connects to a SQL Server database with Entity Framework. You’ll start from the absolute beginning: With “File->New Project” in Visual Studio.

By the end of the course you have created this application to organize friends and meetings. Everything you create in this application is stored in SQL Server with Entity Framework:

If you want to jump into XAML and frameworks like WPF and UWP to build enterprise applications, I recommend you to watch this course about building an Enterprise App with WPF, MVVM, and Entity Framework Code First.

After this course, you can learn how to test your ViewModels with unit tests by watching
WPF and MVVM: Test Driven Development of ViewModels.

With your solid knowledge, the next thing you can learn is how to create powerful model wrappers that track all the changes made to a model. Just watch this course: WPF and MVVM: Advanced Model Treatment. You’ll learn in this course how to track model changes, so that you can display changed fields in the UI with a different color. You even learn how to display the original value of a changed field in the tooltip. Beside all of this, you learn a lot about XAML, Data Binding, Attached Properties and Styles in this course.

Share this post

Comments (24)

  • Daniel Frahn Reply

    Very nice course with ultra-precisely explained source code!
    Maybe a bit too much, especially for experienced developers, as they already expect the code you are going to write.

    I was wondering why you always await Tasks, even if it’s the last instruction within a method.
    I think, simply returning the task (and removing the async keyword) is the way you go, otherwise you have different exception behavior, and a larger compiler generated code when using async/await.

    The only reason I can think of to await a method is when you want to wait for that result in order to execute further code.

    September 11, 2017 at 4:07 pm
    • Thomas Claudius Huber Reply

      Hi Daniel,

      thanks for the feedback. I tried to make this course also valuable for beginners, so yes, I can understand that advanced developers might find the explanations too detailed at some places.

      There are several reasons why I use the await keyword at the end of a method:
      – Disposables
      – Getting correct and expected exception behavior
      – …

      From my experience it’s the best to use the keywords everywhere and then optimize where needed. Take a look at this great article by Stephen Cleary: http://blog.stephencleary.com/2016/12/eliding-async-await.html

      September 11, 2017 at 8:01 pm
  • Lais Gomes Reply

    Hi Thomas =)
    Your course is very cleanly, good for understand the world wpf.
    So, I’m developing app and with base in FriendOrganizer using TDD but i don’t know how test ShouldEnableSaveButtonIfChanges because, ever return false.

    public void ShouldEnableSaveButtonIfChanges()
    {
    sutViewModel.Load(_friendId);

    sutViewModel.InnerCatraca.Nome = “Digicon”;

    Assert.True(sutViewModel.SaveCommand.CanExecute(null));
    }

    I use ViewModel.HasChanges method
    In App, run perfectly the button enable and disable correctly.
    Can you help this?

    Thanks, congratulation.

    October 25, 2017 at 9:54 pm
    • Thomas Claudius Huber Reply

      Hi Lais,

      thanks for the feedback.

      To your test method:
      Is your Load method async? If yes, you need to await it and make the test method async and returning a Task. Like here:

      public async Task ShouldEnableSaveButtonIfChanges()
      {
      await sutViewModel.Load(_friendId);

      }

      Without seeing the rest of your code I can’t say anything more to this. If you’re using the EF-DataContext to track changes, you might have to mock that behavior in your test too. The simplest way is to debug the code and take a look at the location where the HasChanges property of the ViewModel is set.

      October 26, 2017 at 2:16 pm
  • mohsen Reply

    Hi Thomas, what is another way to force validation in new item instead of using Friend.FirstName=””/Meeting.Title=””?

    November 28, 2017 at 8:01 pm
    • Thomas Claudius Huber Reply

      Hi Mohsen,

      you can walk the properties with Reflection and raise the validation for all of them. On the Pluralsight course page, you find a discussion tab. When you scroll through the answers you’ll find a way there.

      December 8, 2017 at 8:50 pm
  • Prateek Reply

    Hi Thomas,
    The whole course has been very nicely explained by you.
    However, after the module ‘Communicating between the view models’, even after following your instructions, the FriendDetailView is not displaying details of the Selected Friend.
    I am stuck here.
    Could you please guide as to where I am going wrong ?

    Thank you,
    Rg.

    September 4, 2018 at 12:27 pm
    • Thomas Claudius Huber Reply

      Hi Prateek,

      please ask these questions directly on the course homepage and I’ll answer them there, so others can also read the answer

      Thanks,
      Thomas

      January 21, 2019 at 5:17 pm
  • Kyungmin Reply

    Hi Thomas,

    I have a question.

    In your course and code, before clearing friend wrapper collection(if it is not LookUpItem) you remove event handler in property
    But I don’t know why you remove event handler.

    Because you added new entities in collection, so no side effect exists even though we don’t remove event handler.

    Is it related with GC??

    January 4, 2019 at 3:32 am
    • Thomas Claudius Huber Reply

      Hi Kyungmin,

      please ask these questions directly on the course homepage and I’ll answer them there, so others can also read the answer

      Thanks,
      Thomas

      January 21, 2019 at 5:20 pm
  • shahriyar Reply

    hi
    Thank you for preparing this course
    can you update this course by .net core 3 ?
    is there any difference to make this solution in .net core 3 ?
    is there dependency injection built in .net core that is better than autofac ?

    can you create a course for learning new version of prism with wpf ?

    tank you

    October 3, 2019 at 5:02 pm
    • Thomas Claudius Huber Reply

      There is not really something different in .NET Core 3. Dependency Injection is not better than Autofac. :)

      January 7, 2020 at 12:04 am
  • Sebastian Matus Reply

    Hey Thomas, great course on pluralsight, I have a question in the Decoupling the UI parts module, in the Create the LookupDataService part. In there, you create the LookupDataService class and pass in the DbContext directly in the constructor, which is ok since you referenced the DataAccess project. My question is, how would one do this if the DbContext was behind a Web Service and can’t reference it directly. It pops to mind that I should just implement the method in the web service and make a request from the lookupdataservice, but it seems a little redundant.

    Thanks for your time.

    February 11, 2020 at 4:16 pm
    • Thomas Claudius Huber Reply

      Hey Sebastian, thank you. In case the DbContext is for example behind a Web API, you would use HttpContext in that LookupDataService class.
      You would always have a LookupDataService and other DataServices. Based on your architecture, these services access the database directly, or they call a Web API or they load the data from any other place (for example from a file).

      Cheers,
      Thomas

      February 14, 2020 at 7:36 pm
    • Daniel Frahn Reply

      I cannot image how to avoid redundancy. I am would build a decorator around the lookup data service which delegates the calls to the web service. Something like this:

      class LookupService : ILookupService
      {
      ctor(ILookupService inner);

      object GetSomeData() {
      var data = _inner.GetSomeData() // Web API call

      // Process data

      return data;
      }
      }

      February 15, 2020 at 9:36 am
  • Daniel Frahn Reply

    *I would

    February 15, 2020 at 9:39 am
    • Thomas Claudius Huber Reply

      Hi Daniel,

      you wouldn’t create a lookup service around the lookup service.

      If you build an architecture with a Web API, your LookupService would get an HttpClient in the constructor, so that it can call the Web API. That’s it.

      You either access a database from the service, or you access a Web API. In one case it gets a DbContext, in the other case it gets an HttpClient.

      I hope this helps,
      Thomas

      February 18, 2020 at 12:13 pm
  • Asmat Ullah Reply

    Hey Thomas, great course on pluralsight, I have a question in the ModelWrapper class in your course i have used the simple data types like Int , double and string etc. please tell us that how we can change the set and get method on class ModelWrapper that it can handled like this collection properties. Thanks in advance.
    public ICollection StoneInfoDetailList
    {
    get { return GetValue<ICollection>(); }
    set
    {
    SetValue(value);
    }
    }

    February 26, 2020 at 1:25 pm
    • Thomas Claudius Huber Reply

      Hi Asmat,

      if you have just an ICollection, you can’t detect if items in that collection change, or if the collection itself is changed (add, remove).

      Why do you want to use an ICollection?

      The best way to get started with a new requirement like this is to add one or more unit tests to the project that reflect what you want to do. When you have that, then we can think about how to implement it.

      Does this help?

      Cheers,
      Thomas

      February 26, 2020 at 5:25 pm
      • Asmat Ullah Reply

        Hey Thomas, thanks for your response i have one model class with name Item below is the detail of this class.

        public class Item
        {
        public Item()
        {
        StoneInfoDetailList = new Collection();
        }

        [Key]
        public long Id { get; set; }
        // [ForeignKey(“BusinessPartner”)]
        public long SupplierID { get; set; }
        // public virtual BusinessPartner BusinessPartner { set; get; }
        // [ForeignKey(“Category”)]
        public int CategoryID { get; set; }
        public virtual ItemType ItemType { get; set; }
        [ForeignKey(“ItemSubType”)]
        public int ItemSubTypeID { get; set; }
        public virtual ItemSubType ItemSubType { get; set; }
        [StringLength(300)]
        [Required]
        public string ItemName { get; set; }
        [ForeignKey(“Karat”)]
        public int KaratID { get; set; }
        public virtual Karat Karat { get; set; }
        public byte[] Image { get; set; }
        public string ManualBarcode { get; set; }
        public string BarCode { get; set; }
        public double StockQty { get; set; }
        public double LastAvilableQty { get; set; }
        public double ParchaseAmount { get; set; }
        public long Re_OrderQty { get; set; }
        public double NetWeight { get; set; }
        [StringLength(1000)]
        public string ItemDescription { get; set; }
        //public int StoneInfoID { get; set; }
        [StringLength(300)]
        public string BrandName { get; set; }
        [StringLength(20)]
        public string Color { get; set; }
        public string Size { get; set; }
        public string Others { get; set; }
        public double LabourCast { get; set; }
        public double RunningPrice { get; set; }
        public DateTime? InsertionDate { get; set; }
        [Timestamp]
        public byte[] RowVersion { get; set; }

        public ICollection StoneInfoDetailList { get; set; }
        }

        stoneinfodetail is another class for stone information recording one item can have more then one stone information.

        i have created the wrapper class with name “ItemWrapper” like that

        public class ItemWrapper:ModelWrapper
        {
        public ItemWrapper(Item model):base(model)
        {
        StoneInfoDetailList = new Collection();
        }
        public long Id { get { return Model.Id; } }

        public long SupplierID
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        public string SupplierName { get; set; }
        // public long SupplierID { get; set; }
        // public long CategoryID { get; set; }
        public int CategoryID
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        public string Category { get; set; }

        // public virtual ItemType ItemType { get; set; }
        public ItemType ItemType
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public int ItemSubTypeID { get; set; }
        public int ItemSubTypeID
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public virtual ItemSubType ItemSubType { get; set; }

        public ItemSubType ItemSubType
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public string ItemName { get; set; }
        public string ItemName
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public int KaratID { get; set; }

        public int KaratID
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public virtual Karat Karat { get; set; }
        public Karat Karat
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public byte[] Image { get; set; }
        public byte[] Image
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public string ManualBarcode { get; set; }
        public string ManualBarcode
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public string BarCode { get; set; }
        public string BarCode
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public double StockQty { get; set; }
        public double StockQty
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public double LastAvilableQty { get; set; }
        public double LastAvilableQty
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public double ParchaseAmount { get; set; }
        public double ParchaseAmount
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public long Re_OrderQty { get; set; }
        public long Re_OrderQty
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public double NetWeight { get; set; }
        public double NetWeight
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public string ItemDescription { get; set; }
        public string ItemDescription
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public string BrandName { get; set; }
        public string BrandName
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public string Color { get; set; }
        public string Color
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public string Size { get; set; }
        public string Size
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public string Others { get; set; }
        public string Others
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public double LabourCast { get; set; }
        public double LabourCast
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public double RunningPrice { get; set; }
        public double RunningPrice
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        // public DateTime? InsertionDate { get; set; }
        public DateTime InsertionDate
        {
        get { return GetValue(); }
        set
        {
        SetValue(value);
        }
        }
        //public ICollection StoneInfoDetailList { get; set; }
        public ICollection StoneInfoDetailList
        {
        get { return GetValue<ICollection>(); }
        set
        {
        SetValue(value);
        }
        }
        }

        Now when i am doing this StockItem = new ItemWrapper(item);

        exception is occuring which is “System.Reflection.TargetException: ‘Non-static method requires a target.'”

        Please help us to solve this exception. Thanks

        February 27, 2020 at 6:18 am
        • Thomas Claudius Huber Reply

          Hi Asmat,

          could you post a project to GitHub that reproduces this issue? Then I can have a look at it.

          Thanks,
          Thomas

          February 27, 2020 at 8:45 pm
  • Matthew South Reply

    Hello Thomas, I have a question regarding model wrapping. I’m currently working on a project with another senior developer who is a little more experienced than me in MVVM, we started the project and now I’m working on a module on my own. In this module I wrapped 3 model classes for validation and when it was time to merge to the main branch of the project, my co-worker said that I shouldn’t wrap any classes because it was code duplication, which it kinda is because I have to write all the properties of my model class into my model wrapper. I tried to justify saying that it was that or having to validate each model property on the viewmodel anyway, but it was pointless to argue. What do you think about this? What are the pros and cons of wrapping and not wrapping.

    Thanks for your time.

    April 7, 2020 at 5:20 pm
    • Thomas Claudius Huber Reply

      Hi Matthew,

      like you mentioned, in the end you have to put the properties either in your ViewModel or in the ModelWrapper. The ModelWrapper is actually a ViewModel for the Model. :-)

      The pros of creating a ModelWrapper is that you can have the model specific stuff like property change notification and validation in the ModelWrapper and you don’t have to polute your ViewModel with this. What if you want to use the same model in multiple ViewModels? Then you have to redo change notification and validation everywhere without a ModelWrapper. But with a ModelWrapper, you just use the ModelWrapper. That’s the big advantage. Beside this, an advantage is also that you can generate ModelWrappers quite easy, as they contain just model specific stuff. You don’t have to worry about overwriting code of screen-specific ViewModels.

      Happy coding,
      Thomas

      April 18, 2020 at 6:21 pm
      • Matthew South Reply

        Hello Thomas, thank you for the response. Just as a follow up I’ll tell you what we ended up doing:

        Since our project is just a client and our back end is behind a web service, we ended up ditching the models and using only a modified version of the model wrapper. No model, just viewmodels (not really we still use just models for objects that do not need vaidation).

        That’s it.

        May 8, 2020 at 8:34 pm

Leave a Reply to Kyungmin Cancel reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.