none
Multiple UOW with WPF? RRS feed

  • Question

  • I'm using the term UOW loosely to just mean the container of the objects. 

    This ADO.Net Blog  shows a UOW implementation for Northwind where at the end they use the UOW to add to an Order_Detail. After reading some articles on persistence for the UOW like this one I am finding it difficult to efficiently structure a WPF app in a way where the UOW can be persisted only as long as necessary. One issue that I bumped my head into was the lookup data in the app which has a different persistence than the transactional data that is the subject of update.

    Using the Northwind DB as an example, an Order entity is a good candidate for limited persistence. If an update were done to an Order entity I believe it would be good practice to dispose of the UOW after the update so as to force EF to grab the latest from the DB and not use cache. If you developed your context like this it could be an issue.

    Public Class NorthwindContext
        Inherits ObjectContext
        Public Property Orders As ObjectSet(Of Order)
        Public Property Order_Details As ObjectSet(Of Order_Detail)
        Public Property Categories As ObjectSet(Of Category)
        Public Property Products() As ObjectSet(Of Product)
        Public Property Customers() As ObjectSet(Of Customer)

    Assume that Customers, Products, and Categories are static for this app. Why throw them all out each time you update an order? It seems wasteful to me. They are not really part of the transaction unit of work like Orders and Order_Details.

    Does this mean that when you plan your UOW for WPF you should consider persistence? Does it also mean you may have multiple UOW? I have never seen an example where multiple UOW were used. But for a Northwind demo app wouldn’t you want to load your static lists typically used for combo boxes (Categories, Products, and Customers) just once and have them around for the life of the application?  Then have your transactional entities (Orders and Order_Details) grouped so that they can be refreshed at key transaction points (SaveAll). This would mean separate UOW or data layers for each group which I have yet to see in any examples.  Or am I missing something?

    Public Class NorthwindContext_Transactional
        Inherits ObjectContext
        Public Property Orders As ObjectSet(Of Order)
        Public Property Order_Details As ObjectSet(Of Order_Detail)
    End Class
    Public Class NorthwindContext_Static
        Inherits ObjectContext
        Public Property Categories As ObjectSet(Of Category)
        Public Property Products() As ObjectSet(Of Product)
        Public Property Customers() As ObjectSet(Of Customer)
    End Class

    • Moved by Sheldon _Xiao Monday, June 18, 2012 8:43 AM (From:Windows Presentation Foundation (WPF))
    Thursday, June 14, 2012 6:40 PM

All replies

  • Maybe you should ask help in EF forum or SQL forum.

    NEU_ShieldEdge

    Monday, June 18, 2012 6:16 AM
  • Maybe you should ask help in EF forum or SQL forum.

    NEU_ShieldEdge


    Looks like it was moved from WPF to EF forum. My thought process was that this topic was more critical to the WPF developer who happened to be be using EF.  Sorry for the confusion.
    Monday, June 18, 2012 12:48 PM
  • Hi pretzelb,

    Glad to see you again! : )

    I'm not familiar with WPF, but in my opinion, without UOW is also fine. Because ObjectContext allows us to group related operations into a unit of work, we already getted UOW functionality.

    You can download this Entity Framework sample for reference. This is an end-to-end sample WPF application that illustrates the new Entity Framework 4.0 features.  Illustrates common design patterns (such as Repository, Unit of Work, and Model View View-Model (MVVM)) that promote testability and maintainability of code.

    Best Regards 


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, June 19, 2012 6:04 AM
    Moderator
  • Hello Allen

    I found that sample a few days ago. It does indeed provide a great tutorial on common design patterns. But there are at least 2 cases where this falls apart.

    1. What if employee table had a trigger on the DB which was set off by saving changes via the application?
    2. What if mulitple users were updating the same data?

    In both cases I cannot see an effective way to refresh the data that was just updated by the app. From the link I referenced earlier, supposedly the best method is to dispose of the context and reload it. Or the other method I've seen is to always create a new instance of the context but this is never done with a client app sample. It would look like this:

    public Customer GetCustomer(string id)
    {
        using (var ctx = new NorthwindEntities())
        {
            return ctx.Customers.Include("Orders")
            .Where(c => c.CustomerID == id)
            .SingleOrDefault();
        }
    }

    With the structure of the sample app the way it is now, if you updating something on the employee tab and wanted to dispose and refresh, you would be reloading the data on the departments tab and on the "loyalty board" list on the far left, neither of which really required a refresh.

    I see samples like this often but I am not sure that is really how people implement the EF for a client app. If you do need a data refresh after a save command I keep wondering if the solutions most often used are:

    1. Keep the single large context like EmployeeEntities and the single large UoW like the UnitOfWork class in the sample and dispose of the context and reload everything even if only a portion of the context needs to be refreshed.
    2. Split the context into separate classes by their need for a refresh and create separate UoW classes for each.

    I have yet to see any example of option "2".

    Tuesday, June 19, 2012 10:18 PM
  • Hi pretzelb,

    We can create a timestamp column in each of the database tables to control multiple users modifying the same record. I'm not clear about the purpose of refresh context, if it is for load the newst data, I suggest you to use Local Property. The Local property of DbSet provides simple access to the entities of the set that are currently being tracked by the context and have not been marked as Deleted.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, June 20, 2012 9:14 AM
    Moderator
  • I had two scenarios in mind when I was thinking of why a "refresh" of the context would be necessary.

    1. The underlying DB is one that previously existed prior the developer using EF for a data layer. That DB is not something you can alter or change. And that DB has triggers on tables that you will update with your applicaiton. From what I've read and what I've seen, any change that happens outside the context (like a trigger) is not recognized by the context. The context instead only reads the DB initially and holds the entities in cache. In the case of Northwind, if you had a trigger on Order_Detail that updated a field on Order, that change would never been seen by the context unless you forced a refresh.
    2. The application is used by multiple users and updates made by one user are not seen by other users again because of the cache. If you and I are both using a Northwind app and we both start working on the same order, we would never see the added Order_Detail records each other made because our context would not refresh at any point unless we forced it.

    I wonder if the approach is wrong in the Entity Framework sample that you linked. For example, to get a list of employees they use this code:

            ''' <summary>
            ''' Gets all employees for the company
            ''' </summary>
            ''' <returns>Enumerable of all employees</returns>  
            Public Function GetAllEmployees() As IEnumerable(Of Employee) Implements IEmployeeRepository.GetAllEmployees
                ' NOTE: Some points considered during implementation of data access methods:
                '    -  ToList is used to ensure any data access related exceptions are thrown
                '       during execution of this method rather than when the data is enumerated.
                '    -  Returning IEnumerable rather than IQueryable ensures the repository has full control
                '       over how data is retrieved from the store, returning IQueryable would allow consumers
                '       to add additional operators and affect the query sent to the store.
                Return Me.context.Employees.ToList()
            End Function

    This is common in samples I see. But unless I misunderstand this would mean that if you and I were using the app then we would not see additions made by the other person until we restarted the app.

    Instead I wonder if a web client approach should always be used. I see some examples like this:

            public Customer GetCustomer(string id)
            {
                using (var ctx = new NorthwindEntities())
                {
                    return ctx.Customers.Include("Orders")
                        .Where(c => c.CustomerID == id)
                        .SingleOrDefault();
                }
            }

    I think this results in a new context each time a call is made which would force a fresh read to the DB. Using this logic we would be able to see additions made by other users (and trigger results) if a fresh call was made to this method.

    I suppose a third option is what I mentioned first in my question but I have yet to see an example of a UoW class for entities that do not require a refresh and another UoW for those that do need a refresh.

    Wednesday, June 20, 2012 7:56 PM
  • Hi pretzelb,

    I'm not familar with WPF, but I think I have understood your meaning. I wrote a test project to simulate multiple users operate one database table. Below are the steps.

    1. Create a database table 'Test'(id int primary key identity, name nvarchar(50), number int).

    2. Insert 4 records into the table.

    3. Create a Console Application and write the code below.

    static void Main(string[] args)
            {
                using (TestEntities context = new TestEntities())
                {
                    IEnumerable<Testing> testList = context.Testings;
                    foreach (var v in testList)
                    {
                        Console.WriteLine(v.id);
                    }
                    Console.Read();
                }
            }

    4. Add a breakpoint at the line of 'foreach'.

    5. Run the application and the it stops at the line of 'foreach'. Now, We can see the testList.Count() is 4 in watch window.

    6. Add a new record into the table from database, this is simulate another user insert a record into the table. Now, there're 5 records in this table.

    7. Continue running the application, we can see, there're 5 records being printed out.

    So, I think you needn't to force the context refresh.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Thursday, June 21, 2012 6:53 AM
    Moderator
  • Hi Allen

    I took your sample and altered it below. I think this shows the behavior I am trying to explain.

    The key point is the creation of the new context. In the sample code it is at the top of the 'using' section. In a client app I think this would be the same as initialization, meaning that the context would remain from the start of the client until the user closed the app. I think this is what you would see if you used the EF sample app we referenced earlier.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using System.Data.Entity.ModelConfiguration;
    using System.Collections.ObjectModel;
    namespace ConsoleTest_Csharp
    {
        class Program
        {
            static void Main(string[] args)
            {
                // prime db with data
                Database.SetInitializer(new TestContextInitializer());
                // assume this is the start of a client app
                using (TestEntities context = new TestEntities())
                {
                    IEnumerable<Testing> testList = context.Testings;
                    Console.WriteLine("Before any changes the DB should have 4 records");
                    foreach (var v in testList)
                    {
                        Console.WriteLine(" * " + v.id + " " + v.Name + " " + v.Number);
                    }
                    Console.WriteLine("Hit enter to continue");
                    Console.ReadKey(true);
                    //now simulate user adding record via client
                    context.Testings.Add(new Testing { id = 5, Name = "I AmfromClient", Number = 5 });
                    context.SaveChanges();
                    IEnumerable<Testing> testList2 = context.Testings; 
                    Console.WriteLine("Record added in client so now 5 records");
                    foreach (var v in testList2)
                    {
                        Console.WriteLine(" * " + v.id + " " + v.Name + " " + v.Number);
                    }
                    Console.WriteLine("Now go to DB and either add new record or update all number values to 0.");
                    Console.WriteLine("When done come back and hit any key.");
                    Console.ReadKey(true);
                    //now simulate user adding record via client
                    IEnumerable<Testing> testList3 = context.Testings;
                    Console.WriteLine("Updates should not be seen because context has no idea");
                    foreach (var v in testList3)
                    {
                        Console.WriteLine(" * " + v.id + " " + v.Name + " " + v.Number);
                    }
                    Console.WriteLine("Hit enter to continue");
                    Console.ReadKey(true);
                    //now show what happens when context is refreshed
                    TestEntities newcontext = new TestEntities();
                    IEnumerable<Testing> testlist4 = newcontext.Testings;
                    Console.WriteLine("Now we grab new copy of context to do refresh");
                    foreach (var v in testlist4)
                    {
                        Console.WriteLine(" * " + v.id + " " + v.Name + " " + v.Number);
                    }
                    Console.ReadKey(true);
                }
                // assumed end of client app - user closes the app
            }
        }
        public class Testing
        {
            public int id { get; set; }
            public string Name { get; set; }
            public int Number { get; set; }
        }
        public class TestEntities : DbContext
        {
            public DbSet<Testing> Testings { get; set; }
            protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
            {
            }
        }
        public class TestContextInitializer : DropCreateDatabaseAlways<TestEntities>
        {
            protected override void Seed(TestEntities context)
            {
                context.Testings.Add(new Testing{ id=1, Name="John Doe", Number=5});
                context.Testings.Add(new Testing{ id=2, Name="Bob Smith", Number=5});
                context.Testings.Add(new Testing{ id=3, Name="Sally Jones", Number=5});
                context.Testings.Add(new Testing{ id=4, Name="Doug Hanson", Number=5});
                context.SaveChanges();
            }
        }
    }

    Thursday, June 21, 2012 6:37 PM
  • Hi pretzelb,

    Commonly, we often define the DbContext at the top of the 'using' section, but doesn't instantiate there. DbContext is intended to be a short-lived object. It is recommended to create a new DataContext for a new unit of work (MSDN documentation).

    However, it is not an absolute answer. Sometimes, we may need a DbContext to be kept longer. How to manage the lifetime of DbContext is all determined by our detailed application scenario. Rick Strahl (MVP) has a brilliant blog article discussing this topic: http://www.west-wind.com/weblog/posts/246222.aspx. This article is related to DataContext, but it is also adapt to DbContext. Here's tip about How to decide on a lifetime for your ObjectContext: http://blogs.msdn.com/b/alexj/archive/2009/05/07/tip-18-how-to-decide-on-a-lifetime-for-your-objectcontext.aspx, I think it is also helpful. Please refer to the links.

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us


    Friday, June 22, 2012 7:08 AM
    Moderator
  • Hi pretzelb,

    Any update about this issue? If you need further help, please feel free to let me know. I will be more than happy to be of assistance. :)

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Monday, June 25, 2012 3:20 AM
    Moderator
  • I still think some of the examples that were referenced fail to accomodate the situations I pointed out (triggers or multiple users). I do see others articles which agree with me and point out that it is an issue, yet none provide a fully featured demo. The fully featured demos all appear to assume that no triggers exist and only one user will use the app at a time. I admit triggers might be rare when using EF but multiple users of a client doesn't seem very rare.

    Ideally I'd like to see a demo using a client that is set up to accomodate the persistence issues of multiple users.

    Let me review those articles again. I bet I can find something to mark this complete. Sorry for the delay.

    Tuesday, June 26, 2012 1:54 AM
  • Hi pretzelb,

    I will help you to search if there's a demo about the specific scenario, and let you know the searching result at once. :)

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, June 27, 2012 6:28 AM
    Moderator
  • Hi pretzelb,

    I've found there's a forum for architecture, I think it may help you with this issue, please try to post here. I hope you can get a satisfied solution. : )

    Best Regards


    Allen Li [MSFT]
    MSDN Community Support | Feedback to us

    Friday, June 29, 2012 9:43 AM
    Moderator