EF and N-Tiers

Even if it’s really easier with Self-Tracking entities and EF4, a difficult point remains in N-Tiers scenarios.

In a previous post, I explained how with T4, I can generate all my WCF service. Now, what about the client tiers?

I explained how can I use LINQ queries and translate them into WCF service methods.

Now my objective is to have a real context on the client tier. I already did it for ADO .NET Data Services using T4, I now have one (still with T4 of course) for WCF.

One of my customers told me that he didn’t like the fact that with ADO .NET Data Services, when you want to access to the context collection, it implies a REST query by default. He wanted to use the cache by default.

With my context, I have this and I use AsQueryable extension method to generate a query on the server.

[TestClass]
public class UnitTest
{
    [TestMethod]
    public void TestService()
    {
        INorthwindService context = new NorthwindClientContext();
        Assert.AreNotEqual(0, context.GetCustomers().Count);
   

    [TestMethod]
    public void TestFilter()
    {
        INorthwindService context = new NorthwindClientContext();
        Assert.AreEqual(1, context.GetCustomers(null, "it.CustomerID=’ALFKI’", null, null, null).Count);
   

    [TestMethod]
    public void TestLINQWhere()
    {
        using (var context = new NorthwindClientContext())
        {
            Assert.AreEqual(1, (from c in context.Customers.AsQueryable()
                                where c.CustomerID == "ALFKI"
                                select c).Count());
        }
   

    [TestMethod]
    public void TestLINQWhere2()
    {
        using (var context = new NorthwindClientContext())
        {
            var customerID = "ALFKI";
            Assert.AreEqual(1, (from c in context.Customers.AsQueryable()
                                where c.CustomerID == customerID
                                select c).Count());
        }
   

    [TestMethod]
    public void TestLINQWhere3()
    {
        using (var context = new NorthwindClientContext())
        {
            var customer = new Customer { CustomerID = "ALFKI" };
            Assert.AreEqual(1, (from c in context.Customers.AsQueryable()
                                where c.CustomerID == customer.CustomerID
                                select c).Count());
        }
    }  

    [TestMethod]
    public void TestLINQWhere4()
    {
        using (var context = new NorthwindClientContext())
        {
            Assert.AreNotEqual(0, (from o in context.Orders.AsQueryable()
                                   where o.OrderDate > new DateTime(1997, 1, 31)
                                   select o).Count());
        }
    }

   
[TestMethod]
    public void TestLINQWhere5()
    {
        using (var context = new NorthwindClientContext())
        {
            var d = new DateTime(1997, 1, 31);
            Assert.AreNotEqual(0, (from o in context.Orders.AsQueryable()
                                   where o.OrderDate > d
                                   select o).Count());
        }
    }  

    [TestMethod]
    public void TestLINQTake()
    {
        using (var context = new NorthwindClientContext())
        {
            Assert.AreEqual(2, (from c in context.Customers.AsQueryable()
                                select c).Take(2).Count());
        }
   

    [TestMethod]
    public void TestFirstAndInclude()
    {
        using (var context = new NorthwindClientContext())
        {
            var order = (from o in context.Orders.AsQueryable().Include(Order.CUSTOMER_NAME).Include(Order.ORDERDETAILS_NAME)
                         where o.ShipCity == "PARIS"
                         orderby o.OrderDate
                         select o).Skip(2).First();
            Assert.IsNotNull(order.Customer);
            Assert.AreEqual(1, context.Customers.Count);
            Assert.AreEqual(order.Customer, context.Customers[0]);
            Assert.AreEqual(1, context.Orders.Count);
            Assert.AreEqual(order, context.Orders[0]);
            Assert.AreNotEqual(0, order.OrderDetails);
        }
   

    [TestMethod]
    public void NoTracking()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.NoTracking;
            var l = context.Customers.AsQueryable().ToList();
            Assert.AreEqual(0, context.Customers.Count);
            Assert.AreNotEqual(0, l.Count);
        }
   

    [TestMethod]
    public void AppendOnly()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.AppendOnly;
            var l = context.Customers.AsQueryable().ToList();
            Assert.AreEqual(l.Count, context.Customers.Count);
        }
   

    [TestMethod]
    public void AppendOnly2()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.AppendOnly;
            var c = new Customer { CustomerID = "ALFKI" };
            context.Customers.Attach(c);
            context.Customers.AsQueryable().ToList();
            Assert.IsNull(c.CompanyName);
            Assert.AreEqual(0, c.ChangeTracker.ModifiedProperties.Count);
        }
   

    [TestMethod]
    public void OverwriteChanges()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.OverwriteChanges;
            var l = context.Customers.AsQueryable().ToList();
            Assert.AreEqual(l.Count, context.Customers.Count);
        }
   

    [TestMethod]
    public void OverwriteChanges2()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.OverwriteChanges;
            var c = new Customer { CustomerID = "ALFKI" };
            context.Customers.Attach(c);
            context.Customers.AsQueryable().ToList();
            Assert.IsNotNull(c.CompanyName);
            Assert.AreEqual(0, c.ChangeTracker.ModifiedProperties.Count);
        }
    }

    [TestMethod]
    public void PreserveChanges()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.PreserveChanges;
            var l = context.Customers.AsQueryable().ToList();
            Assert.AreEqual(l.Count, context.Customers.Count);
        }
   

    [TestMethod]
    public void PreserveChanges2()
    {
        using (var context = new NorthwindClientContext())
        {
            context.MergeOption = MergeOption.PreserveChanges;
            var c = new Customer { CustomerID = "ALFKI" };
            context.Customers.Attach(c);
            context.Customers.AsQueryable().ToList();
            Assert.IsNull(c.CompanyName);
            Assert.AreNotEqual(0, c.ChangeTracker.ModifiedProperties.Count);
            Assert.IsTrue(c.ChangeTracker.ModifiedProperties.Contains("CompanyName"));
        }
   

    [TestMethod]
    public void Save()
    {
        using (var context = new NorthwindClientContext())
        {
            //Add
            var psCount = context.Products.AsQueryable().Count();
            var p = new Product { ProductName = "p" };
            context.Products.Add(p);
            context.SaveChanges();
            Assert.AreNotEqual(0, p.ProductID);
            Assert.AreEqual(ObjectState.Unchanged, p.ChangeTracker.State);
            Assert.AreEqual(psCount + 1, context.Products.AsQueryable().ToList().Count); 

            //Update
            p.ProductName = "p2";
            Assert.AreEqual(1, p.ChangeTracker.ModifiedProperties.Count);
            Assert.AreEqual("ProductName", p.ChangeTracker.ModifiedProperties[0]);
            context.SaveChanges();
            context.MergeOption = MergeOption.NoTracking;
            var p2 = context.Products.AsQueryable().Where(pr => pr.ProductID == p.ProductID).First();
            Assert.AreNotEqual(p, p2);
            Assert.AreEqual("p2", p2.ProductName); 

            //Remove
            context.Products.Remove(p);
            Assert.AreEqual(ObjectState.Deleted, p.ChangeTracker.State);
            context.SaveChanges();
            Assert.AreEqual(psCount, context.Products.AsQueryable().ToList().Count);
        }
   

    [TestMethod]
    public void Save2()
    {
        using (var context = new NorthwindClientContext())
        {
            var csCount = context.Categories.AsQueryable().Count();
            var c1 = new Category { CategoryName = "cn" };
            context.Categories.Add(c1);
            var psCount = context.Products.AsQueryable().Count();
            var p1 = new Product { ProductName = "p1" };
            var p2 = new Product { ProductName = "p2" };
            context.Products.Add(p1);
            context.Products.Add(p2);
            context.SaveChanges();
            using (var context2 = new NorthwindClientContext())
            {
                Assert.AreEqual(csCount + 1, context2.Categories.AsQueryable().Count());
                Assert.AreEqual(psCount + 2, context2.Products.AsQueryable().Count());
            }
            context.Categories.Remove(c1);
            context.Products.Remove(p1);
            p2.ProductName = "p2Bis";
            context.SaveChanges();
            using (var context2 = new NorthwindClientContext())
            {
                Assert.AreEqual(csCount, context2.Categories.AsQueryable().Count());
                Assert.AreEqual(psCount + 1, context2.Products.AsQueryable().Count());
                Assert.AreEqual("p2Bis", context2.Products.Last().ProductName);
            }
            context.Products.Remove(p2);
            context.SaveChanges();
            using (var context2 = new NorthwindClientContext())
            {
                Assert.AreEqual(psCount, context2.Products.AsQueryable().Count());
            }
        }
   

    [TestMethod]
    public void TestSaveManyToMany()
    {
        CustomerDemographic cd;
        using (var context = new NorthwindClientContext())
        {
            cd = new CustomerDemographic { CustomerTypeID = "CD", CustomerDesc = "cd desc" };
            context.CustomerDemographics.Add(cd);
            context.SaveChanges();
        }
        using (var context = new NorthwindClientContext())
        {
            var c = context.Customers.AsQueryable().First();
            c.CustomerDemographics.Add(cd);
            Assert.AreEqual(ObjectState.Unchanged, cd.ChangeTracker.State);
            Assert.AreEqual(1, context.CustomerDemographics.Count);
            Assert.AreEqual(1, cd.Customers.Count);
            context.SaveChanges();
        }
        using (var context = new NorthwindClientContext())
        {
            var c = context.Customers.AsQueryable().Include(Customer.CUSTOMERDEMOGRAPHICS_NAME).First();
            Assert.AreNotEqual(0, context.CustomerDemographics.Count);
            Assert.AreNotEqual(0, cd.Customers.Count);
            context.CustomerDemographics.Remove(context.CustomerDemographics.First(cd2 => cd2.CustomerTypeID.TrimEnd() == "CD"));
            context.SaveChanges();
        }
   

    [TestMethod]
    public void TestAddViaCollection()
    {
        using (var context = new NorthwindClientContext())
        {
            int psCount;
            using (var context2 = new NorthwindClientContext())
            {
                psCount = context2.Products.AsQueryable().Count();
            }
            var c = context.Categories.AsQueryable().First();
            var p = new Product { ProductName = "pn" };
            c.Products.Add(p);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(1, context.Products.Count);
            Assert.AreEqual(context.Products[0], p);
            Assert.AreEqual(p.Category, c);
            Assert.AreEqual(p.CategoryID, c.Id);
            Assert.AreEqual(ObjectState.Added, p.ChangeTracker.State);
            context.SaveChanges();
            using (var context2 = new NorthwindClientContext())
            {
                Assert.AreEqual(psCount + 1, context2.Products.AsQueryable().Count());
            }
            Assert.AreEqual(1, c.Products.Count);
            context.Products.Remove(p);
            Assert.AreEqual(0, c.Products.Count);
            context.SaveChanges();
        }
   

    [TestMethod]
    public void TestAddViaCollection2()
    {
        using (var context = new NorthwindClientContext())
        {
            int psCount;
            using (var context2 = new NorthwindClientContext())
            {
                psCount = context2.Products.AsQueryable().Count();
            }
            var c = context.Categories.AsQueryable().First();
            var p = new Product { ProductName = "pn", Category = c };
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(1, context.Products.Count);
            Assert.AreEqual(context.Products[0], p);
            Assert.AreEqual(p.Category, c);
            Assert.AreEqual(p.CategoryID, c.Id);
            Assert.AreEqual(ObjectState.Added, p.ChangeTracker.State);
            context.SaveChanges();
            using (var context2 = new NorthwindClientContext())
            {
                Assert.AreEqual(psCount + 1, context2.Products.AsQueryable().Count());
            }
            Assert.AreEqual(1, c.Products.Count);
            context.Products.Remove(p);
            Assert.AreEqual(0, c.Products.Count);
            context.SaveChanges();
        }
   

    [TestMethod]
    public void TestAddViaCollection3()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Category { CategoryName = "cn" };
            context.Categories.Add(c);
            var p = new Product { ProductName = "pn", Category = c };
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(1, context.Products.Count);
            Assert.AreEqual(context.Products[0], p);
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(c.Id, p.CategoryID);
            Assert.AreEqual(1, context.Categories.Count);
            Assert.AreEqual(c, context.Categories[0]);
            context.SaveChanges();
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(c.Id, p.CategoryID);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(p, c.Products[0]);
            context.Categories.Remove(c);
            Assert.IsNull(p.Category);
            Assert.IsNull(p.CategoryID);
            Assert.AreEqual(ObjectState.Modified, p.ChangeTracker.State);
            Assert.AreEqual(1, p.ChangeTracker.ModifiedProperties.Count);
            Assert.AreEqual("CategoryID", p.ChangeTracker.ModifiedProperties[0]);
            context.SaveChanges();
            context.Products.Remove(p);
            context.SaveChanges();
        }
   

    [TestMethod
    public void TestAddViaCollection4()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Category { CategoryName = "cn" };
            var p = new Product { ProductName = "pn", Category = c };
            context.Categories.Add(c);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(1, context.Products.Count);
            Assert.AreEqual(context.Products[0], p);
            Assert.AreEqual(p.Category, c);
            Assert.AreEqual(p.CategoryID, c.Id);
            Assert.AreEqual(1, context.Categories.Count);
            Assert.AreEqual(c, context.Categories[0]);
        }
   

    [TestMethod]
    public void TestAddViaCollection5()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Customer { CustomerID = "A" };
            var cd = new CustomerDemographic();
            c.CustomerDemographics.Add(cd);
            context.Customers.Attach(c);
            Assert.AreEqual(1, context.Customers.Count);
            Assert.AreEqual(1, context.CustomerDemographics.Count);
            Assert.AreEqual(c, context.Customers[0]);
            Assert.AreEqual(cd, context.CustomerDemographics[0]);
            Assert.AreEqual(1, cd.Customers.Count);
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void TestEntitySetAttachWithSameKey()
    {
        using (var context = new NorthwindClientContext())
        {
            context.Categories.Attach(new Category { Id = 1 });
            context.Categories.Attach(new Category { Id = 1 });
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void TestEntitySetAddWithSameKey()
    {
        using (var context = new NorthwindClientContext())
        {
            context.Customers.Add(new Customer { CustomerID = "C" });
            context.Customers.Add(new Customer { CustomerID = "C" });
        }
   

    [TestMethod]
    public void TestEntitySetAddWithSameKeyIdentity()
    {
        using (var context = new NorthwindClientContext())
        {
            context.Categories.Add(new Category { Id = 0 });
            context.Categories.Add(new Category { Id = 0 });
        }
   

 
   [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void TestNavigationPropertyManyAttachWithSameKey()
    {
        using (var context = new NorthwindClientContext())
        {
            var cd = new CustomerDemographic { CustomerTypeID = "CD" };
            context.CustomerDemographics.Attach(cd);
            context.Customers.Attach(new Customer { CustomerID = "C" });
            cd.Customers.Attach(new Customer { CustomerID = "C" });
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void TestNavigationPropertyManyAddWithSameKey()
    {
        using (var context = new NorthwindClientContext())
        {
            var cd = new CustomerDemographic { CustomerTypeID = "CD" };
            context.CustomerDemographics.Attach(cd);
            context.Customers.Attach(new Customer { CustomerID = "C" });
            cd.Customers.Add(new Customer { CustomerID = "C" });
        }
   

    [TestMethod]
    public void MultipleAddAttachOfSameObject()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Customer { CustomerID = "C" };
            context.Customers.Add(c);
            context.Customers.Add(c);
            context.Customers.Attach(c);
            new CustomerDemographic { CustomerTypeID = "CD" }.Customers.Add(c);
            Assert.AreEqual(1, context.Customers.Count);
        }
   

    [TestMethod]
    public void MultipleRelationAddAttachOfSameObject()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Customer { CustomerID = "C" };
            var cd = new CustomerDemographic { CustomerTypeID = "CD" };
            c.CustomerDemographics.Add(cd);
            c.CustomerDemographics.Add(cd);
            c.CustomerDemographics.Attach(cd);
            Assert.AreEqual(1, c.CustomerDemographics.Count);
            Assert.AreEqual(1, cd.Customers.Count);
        }
   

    [TestMethod]
    public void DeleteCascade()
    {
        using (var context = new NorthwindClientContext())
        {
            var o = new Order();
            var od = new OrderDetail { Order = o };
            Assert.AreEqual(ObjectState.Detached, o.ChangeTracker.State);
            Assert.AreEqual(ObjectState.Detached, od.ChangeTracker.State);
            context.Orders.Add(o);
            Assert.AreEqual(ObjectState.Added, o.ChangeTracker.State);
            Assert.AreEqual(ObjectState.Added, od.ChangeTracker.State);
            context.Orders.Remove(o);
            Assert.AreEqual(ObjectState.Detached, o.ChangeTracker.State);
            Assert.AreEqual(ObjectState.Detached, od.ChangeTracker.State);
        }
   

 
   [TestMethod]
    public void DeleteCascade2()
    {
        using (var context = new NorthwindClientContext())
        {
            var o = new Order();
            var od = new OrderDetail { Order = o, Product = context.Products.AsQueryable().First(), Discount = 0, Quantity = 1, UnitPrice = 10 };
            context.Orders.Add(o);
            context.SaveChanges();
            var od2 = new OrderDetail { Order = o };
            context.Orders.Remove(o);
            Assert.AreEqual(ObjectState.Deleted, o.ChangeTracker.State);
            Assert.AreEqual(ObjectState.Deleted, od.ChangeTracker.State);
            Assert.AreEqual(ObjectState.Detached, od2.ChangeTracker.State);
            context.SaveChanges();
        }
   

    [TestMethod]
    public void AttachRelationInLoad()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Category { Id = 1 };
            context.Categories.Attach(c);
            var p = new Product { CategoryID = 1 };
            context.Products.Attach(p);
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(p, c.Products[0]);
        }
   

    [TestMethod]
    public void AttachRelationInLoad2()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Category { Id = 1 };
            context.Categories.Add(c);
            var p = new Product { CategoryID = 1 };
            context.Products.Add(p);
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(p, c.Products[0]);
        }
   

    [TestMethod]
    public void AttachRelationInLoad3()
    {
        using (var context = new NorthwindClientContext())
        {
            var p = new Product { CategoryID = 1 };
            context.Products.Attach(p);
            var c = new Category { Id = 1 };
            context.Categories.Attach(c);
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(p, c.Products[0]);
        }
   

    [TestMethod]
    public void AttachRelationInLoad4()
    {
        using (var context = new NorthwindClientContext())
        {
            var p = new Product { CategoryID = 1 };
            context.Products.Add(p);
            var c = new Category { Id = 1 };
            context.Categories.Add(c);
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(p, c.Products[0]);
        }
   

    [TestMethod]
    public void AttachRelationInLoad5()
    {
        using (var context = new NorthwindClientContext())
        {
            context.Products.AsQueryable().ToList();
            var c = context.Categories.AsQueryable().First();
            Assert.AreEqual(context.Products.Where(p => p.CategoryID == c.Id).Count(), c.Products.Count);
            foreach (var p in context.Products.Where(p => p.CategoryID == c.Id))
            {
                Assert.AreEqual(c, p.Category);
                Assert.IsTrue(c.Products.Contains(p));
            }
        }
   

    [TestMethod]
    public void AttachRelationInLoad6()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = context.Categories.AsQueryable().First();
            context.Products.AsQueryable().ToList();
            Assert.AreEqual(context.Products.Where(p => p.CategoryID == c.Id).Count(), c.Products.Count);
            foreach (var p in context.Products.Where(p => p.CategoryID == c.Id))
            {
                Assert.AreEqual(c, p.Category);
                Assert.IsTrue(c.Products.Contains(p));
            }
        }
   

    [TestMethod]
    public void AttachRelationInLoad7()
    {
        using (var context = new NorthwindClientContext())
        {
            context.Products.AsQueryable().ToList();
            var c = context.Categories.AsQueryable().Include(Category.PRODUCTS_NAME).First();
            Assert.AreEqual(context.Products.Where(p => p.CategoryID == c.Id).Count(), c.Products.Count);
            foreach (var p in context.Products.Where(p => p.CategoryID == c.Id))
            {
                Assert.AreEqual(c, p.Category);
                Assert.IsTrue(c.Products.Contains(p));
            }
        }
   

    [TestMethod]
    public void AttachRelationInLoad8()
    {
        using (var context = new NorthwindClientContext())
        {
            context.Products.AsQueryable().ToList();
            var c = new Category { Id = 1 };
            context.Categories.Attach(c);
            var c2 = context.Categories.AsQueryable().Include(Category.PRODUCTS_NAME).First();
            Assert.AreEqual(c, c2);
            Assert.AreEqual(context.Products.Where(p => p.CategoryID == c.Id).Count(), c.Products.Count);
            foreach (var p in context.Products.Where(p => p.CategoryID == c.Id))
            {
                Assert.AreEqual(c, p.Category);
                Assert.IsTrue(c.Products.Contains(p));
            }
        }
   

    [TestMethod]
    public void AttachRelationInLoad9()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Category { CategoryName = "cn" };
            var p = new Product { ProductName = "pn", Category = c };
            context.Categories.Add(c);
            Assert.AreEqual(1, context.Products.Count);
            context.SaveChanges();
            Product p2;
            using (var context2 = new NorthwindClientContext())
            {
                var c2 = context2.Categories.AsQueryable().Include(Category.PRODUCTS_NAME).Where(c3 => c3.Id == c.Id).First();
                p2 = new Product { ProductName = "pn2" };
                c2.Products.Add(p2);
                context2.SaveChanges();
            }
            var c4 = context.Categories.AsQueryable().Include(Category.PRODUCTS_NAME).Where(c3 => c3.Id == c.Id).First();
            Assert.AreEqual(c, c4);
            Assert.IsTrue(c.Products.Contains(p));
            Assert.IsFalse(c.Products.Contains(p2));
            var p3 = c.Products.First(p4 => p4.ProductID == p2.ProductID);
            Assert.IsTrue(c.Products.Contains(p3));
            Assert.IsTrue(context.Products.Contains(p3));
            context.Products.Remove(p3);
            context.Products.Remove(p);
            context.Categories.Remove(c);
            Assert.AreEqual(c.Id, p.CategoryID);
            Assert.AreEqual(c.Id, p3.CategoryID);
            context.SaveChanges();
        }
   

    [TestMethod]
    public void AttachRelationInLoad10()
    {
        using (var context = new NorthwindClientContext())
        {
            var p = context.Products.AsQueryable().First();
            context.Orders.Attach(new Order());
            var od = context.OrderDetails.AsQueryable().Where(od2 => od2.ProductID == p.ProductID).First();
            var p2 = (from p3 in context.Products.AsQueryable().Include(Product.CATEGORY_NAME).Include(string.Concat(Product.ORDERDETAILS_NAME, ".", OrderDetail.ORDER_NAME))
                      where p3.ProductID == p.ProductID
                      select p3).First();
            Assert.AreEqual(p, p2);
            Assert.IsNotNull(p.Category);
            Assert.AreEqual(1, context.Categories.Count);
            Assert.AreEqual(p.OrderDetails.Count, context.OrderDetails.Count);
            foreach (var od2 in p.OrderDetails)
                Assert.IsNotNull(od2.Order);
            Assert.AreEqual(p.OrderDetails.Select(od3 => od3.Order).Distinct().Count() + 1, context.Orders.Count);
            Assert.AreNotEqual(0, context.Orders.Count);
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void AddSameEntityInDifferentContext()
    {
        var p = new Product();
        using (var context1 = new NorthwindClientContext())
        {
            context1.Products.Add(p);
            using (var context2 = new NorthwindClientContext())
            {
                context2.Products.Add(p);
            }
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void AddSameEntityFromServiceInDifferentContext()
    {
        Product p;
        using (var context1 = new NorthwindClientContext())
        {
            p = context1.Products.AsQueryable().First();
            using (var context2 = new NorthwindClientContext())
            {
                context2.Products.Add(p);
            }
        }
   

    [TestMethod]
    public void AddSameEntityInDifferentContextWithDispose()
    {
        var p = new Product();
        using (var context1 = new NorthwindClientContext())
        {
            context1.Products.Add(p);
        }
        using (var context2 = new NorthwindClientContext())
        {
            context2.Products.Add(p);
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void AddSameEntityInDifferentContextWithDisposeAndConflicts()
    {
        var c = new Category { Id = 1 };
        var p1 = new Product { ProductID = 1, Category = c };
        var p2 = new Product { ProductID = 2 , CategoryID = 1};
        using (var context1 = new NorthwindClientContext())
        {
            context1.Categories.Attach(c);
            context1.Products.Attach(p2);
            Assert.AreEqual(2, context1.Products.Count);
            Assert.AreEqual(2, c.Products.Count);
            Assert.AreEqual(c, p2.Category);
        }
        using (var context2 = new NorthwindClientContext())
        {
            var p3 = new Product { ProductID = 1 , CategoryID = 1};
            context2.Products.Attach(p3);
            context2.Categories.Attach(c);
        }
    } 

    [TestMethod]
    public void LoadFromOneToMany()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = context.Categories.AsQueryable().First();
            Assert.AreEqual(0, c.Products.Count);
            Assert.AreEqual(0, context.Products.Count);
            var ps = c.LoadProducts();
            Assert.AreNotEqual(0, ps.Count);
            Assert.AreEqual(ps.Count, c.Products.Count);
            Assert.AreEqual(ps.Count, context.Products.Count);
            foreach (var p in ps)
                Assert.AreEqual(c, p.Category);
        }
   

    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void LoadFromOneToManyDetached()
    {
        Category c;
        using (var context = new NorthwindClientContext())
        {
            c = context.Categories.AsQueryable().First();
        }
        var ps = c.LoadProducts();
   

    [TestMethod]
    public void LoadFromManyToOne()
    {
        using (var context = new NorthwindClientContext())
        {
            var p = context.Products.AsQueryable().First();
            Assert.IsNull(p.Category);
            Assert.AreEqual(0, context.Categories.Count);
            var c = p.LoadCategory();
            Assert.IsNotNull(c);
            Assert.AreEqual(c, p.Category);
            Assert.AreEqual(1, context.Categories.Count);
            Assert.AreEqual(1, c.Products.Count);
            Assert.AreEqual(p, c.Products[0]);
        }
   

    [TestMethod]
    public void LoadFromManyToMany()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = (from cust in context.Customers.AsQueryable()
                     where cust.CustomerID == "VINET"
                     select cust).First();
            Assert.AreEqual(0, c.CustomerDemographics.Count);
            Assert.AreEqual(0, context.CustomerDemographics.Count);
            c.LoadCustomerDemographics();
            Assert.AreNotEqual(0, c.CustomerDemographics.Count);
            Assert.AreEqual(c.CustomerDemographics.Count, context.CustomerDemographics.Count);
        }
   

    [TestMethod]
    public void LoadFromOneToOne()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = context.Customers.AsQueryable().First();
            Assert.IsNull(c.Member);
            Assert.AreEqual(0, context.Members.Count);
            var m = c.LoadMember();
            Assert.IsNotNull(c.Member);
            Assert.AreEqual(1, context.Members.Count);
            Assert.AreEqual(c, m.Customer);
        }
   

    [TestMethod]
    public void OfType()
    {
        using (var context = new NorthwindClientContext())
        {
            var employeesInActivity = context.Employees.AsQueryable().OfType<EmployeeInActivity>().ToList();
            Assert.AreEqual(employeesInActivity.Count, context.Employees.Count);
            Assert.AreEqual(employeesInActivity.Count, context.EmployeeInActivities.Count);
            Assert.AreEqual(0, context.OutEmployees.Count);
        }
   

    [TestMethod]
    public void AddIntoBaseEntitySet()
    {
        using (var context = new NorthwindClientContext())
        {
            var firedEmployee = new FiredEmployee();
            context.Employees.Attach(firedEmployee);
            Assert.AreEqual(1, context.Employees.Count);
            Assert.AreEqual(1, context.OutEmployees.Count);
            Assert.AreEqual(1, context.FiredEmployees.Count);
        }
    }

    [TestMethod]
    public void GetAllEntitiesWithInheritance()
    {
        using (var context = new NorthwindClientContext())
        {
            var l = context.Employees.AsQueryable().ToList();
            Assert.AreEqual(l.Count, context.Employees.Count);
            Assert.AreEqual(l.OfType<OutEmployee>().Count(), context.OutEmployees.Count);
            Assert.AreEqual(l.OfType<FiredEmployee>().Count(), context.FiredEmployees.Count);
            Assert.AreNotEqual(0, context.FiredEmployees.Count);
        }
    } 

 

    [TestMethod]
    public void Detach()
    {
        using (var context = new NorthwindClientContext())
        {
            var c = new Category { Id = 1 };
            context.Categories.Attach(c);
            var p = new Product { CategoryID = 1 };
            context.Products.Attach(p);
            Assert.AreEqual(c, p.Category);
            context.Categories.Detach(c);
            Assert.IsNull(p.Category);
            Assert.AreEqual(c.Id, p.CategoryID);
        }
   

    [TestMethod]
    public void UpdateFK()
    {
        using (var context = new NorthwindClientContext())
        {
            var c1 = new Category { Id = 1 };
            context.Categories.Attach(c1);
            var c2 = new Category { Id = 2 };
            context.Categories.Attach(c2);
            var p = new Product { CategoryID = 1 };
            context.Products.Attach(p);
            Assert.AreEqual(c1, p.Category);
            Assert.AreEqual(1, c1.Products.Count);
            p.CategoryID = 2;
            Assert.AreEqual(c2, p.Category);
            Assert.AreEqual(0, c1.Products.Count);
            Assert.AreEqual(1, c2.Products.Count);
        }
    }
}

I used this EDM for my tests:

image

Moreover, with self-tracking default template the generated SQL update includes all columns even if only one property changed. With my implementation the update will contain only the modified column. Note that this is not true for complex types. If one complex type’s property changes, all the columns mapped on this complex type will be included in the update. It is an EF limitation.

To realize it, I add a list of string ModifiedProperties in ObjectChangeTracker class (I update the self-tracking types T4 template). Then I also change the self-tracking context T4 template. I replace:

context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);

by

context.ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
var ose = context.ObjectStateManager.GetObjectStateEntry(entity);
ose.SetModified();
foreach (var propertyName in entity.ChangeTracker.ModifiedProperties) 
       ose.SetModifiedProperty(propertyName);

If you want to learn more about my templates, I will speak about this for next French Microsoft Tech Days in Paris and for the confoo in Montreal (also in French).

This entry was posted in 10511, 7671, 7674, 7675. Bookmark the permalink.

5 Responses to EF and N-Tiers

  1. bao says:

    cool, where can i download your templates?

  2. Mike says:

    Hi,
    Thx for sharing this superb piece of code!!
    Is there any chance to get the Database used in the Sample? (MyNTiersNorthwindDB)

    Best regards, Mike.

  3. Mike says:

    Hi Matthieu,

    thx, i got it working, and learned much.

    Now i have a issue with doing count(*) on the client side.
    It seems that when i do a context.sampledata.asQueryable().Count() EF fetch all Records before counting them which takes very long time. I think the problem is the translation from MyQuerable to the Service call or do i somenthing wrong?

    Best regards, Mike.

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>