Handling explicit loading

In this recipe, we will look at how we can explicitly load object graphs to avoid multiple calls to the database. We will also leverage Lambda statements and Language Integrated Query to bring these to bear on our queries.

Getting ready

We will be using the NuGet package manager to install the Entity Framework 4.1 assemblies.

The package installer can be found at http://nuget.org.

We will also be using a database for connecting to and updating data.

Open the Improving Explicit Loading solution in the included source code examples.

How to do it...

Carry out the following steps in order to accomplish this recipe.

  1. We will start by writing a test which will query the database with a set of included objects and return the object graph. Use the following code:
    using System.Data.Entity;
    using BusinessLogic;
    using BusinessLogic.Queries;
    using DataAccess;
    using DataAccess.Database;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Test.Properties;
     
    namespace Test
    {
      [TestClass]
      public class QueryTests
      {
        [TestMethod]
        public void ShouldFilterTestData()
        {
          //Arrange
          Database.SetInitializer(new Initializer());
          var repository = new BlogRepository
            (new BlogContext(Settings.Default.BlogConnection));
     
          //Act
          Blog item = repository.Set<Blog>().GetBlogById(1);
     
          //Assert
          Assert.IsNotNull(item);
          Assert.IsNotNull(item.Author);
          Assert.IsNotNull(item.Posts);
          Assert.IsTrue(item.Posts.Count != 0);
        }
     
      }
     
    }
  2. We will then add a folder to the DataAccess project named Database and a new C# file named Initializer with the following code:
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using BusinessLogic;
     
    namespace DataAccess.Database
    {
     
      public class Initializer : DropCreateDatabaseAlways
        <BlogContext>
      {
        public Initializer()
        {
          
        }
        protected override void Seed(BlogContext context)
        {
          context.Set<Blog>().Add(new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Testing",
            Title = "Test Blog",
            Author = new Author() { Name = "Testing" },
            Posts = new List<Post>() { new Post() { Content =
              "Test content"} }
          });
          context.Set<Blog>().Add(new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Testing",
            Title = "Test Blog 2",
            Author = new Author() { Name = "Testing" },
            Posts = new List<Post>() { new Post() { Content =
              "Test content" } }
          });
          context.Set<Blog>().Add(new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Testing",
            Title = "not Blog",
            Author = new Author() { Name = "Testing" },
            Posts = new List<Post>() { new Post() { Content =
              "Test content" } }
          });
          context.SaveChanges();
        }
      }
    }
  3. In the BusinessLogic project, add a new C# class named Blog with the following code:
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.Text.RegularExpressions;
    
    namespace BusinessLogic
    {
      public class Blog
      {
        public int Id { get; set; }
        public DateTime Creationdate { get; set; }
        public string ShortDescription { get; set; }
        public string Title { get; set; }
        public double Rating { get; set; }
      }
    }
  4. Add a Mapping folder to the Data Access project and then add a BlogMapping class to the folder with the following code:
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.ModelConfiguration;
    using BusinessLogic;
    namespace DataAccess.Mappings
    {
      public class BlogMapping : EntityTypeConfiguration<Blog>
      {
        public BlogMapping()
        {
          this.ToTable("Blogs");
          this.HasKey(x => x.Id);
          this.Property(x => x.Id)
            .HasDatabaseGeneratedOption
              (DatabaseGeneratedOption.Identity)
                .HasColumnName("BlogId");
    
           this.Property(x => x.Title)
             .IsRequired().HasMaxLength(250);
           this.Property(x => x.Creationdate)
             .HasColumnName("CreationDate").IsRequired();
           this.Property(x => x.ShortDescription)
             .HasColumnType("Text").IsMaxLength()
                .IsOptional().HasColumnName("Description");
        }
    
      }
    }
  5. Modify the BlogContext class to contain the new mappings for Blogs with the following code:
    using System;
    using System.Data.Entity;
    using System.Linq;
    using BusinessLogic;
    using DataAccess.Mappings;
    
    namespace DataAccess
    {
      public class BlogContext : DbContext, IUnitOfWork
      {
        public BlogContext(string connectionString)
          : base(connectionString)
        {
    
        }
    
        protected override void OnModelCreating
          (DbModelBuilder modelBuilder)
        {
          modelBuilder.Configurations.Add(new BlogMapping());
          base.OnModelCreating(modelBuilder);
        }
    
        public IQueryable<T> Find<T>() where T : class
        {
          return this.Set<T>();
        }
    
        public void Refresh()
        {
         this.ChangeTracker.Entries().ToList()
           .ForEach(x=>x.Reload());
        }
    
        public void Commit()
        {
          this.SaveChanges();
        }
      }
    }
  6. We then want to add a Queries folder to the DataAccess project and a new C# class named BlogQueries with the following code:
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    using System.Linq.Expressions;
     
    namespace BusinessLogic.Queries
    {
      public static class BlogQueries
      {
        static readonly List<Expression<Func<Blog,object>>>
          Includes = new List<Expression<Func<Blog, object>>>()
        {
           x=>x.Posts,
          x=>x.Author
         };
     
        public static Blog GetBlogById(this IQueryable<Blog>
           items, int id)
        {
          var query = Includes.Aggregate(items,
            (current, include) => current.Include(include));
         return query.FirstOrDefault(x => x.Id == id);
        }
      }
    }
  7. Run our test and everything will pass.

How it works...

We will start by defining our test so we have a definition of what success means, as well as a safety net for future refactoring. We would have a hard time with this kind of logic without a test that was repeatable to make these changes.

The next thing that we need to do is to set up our context, our objects, and our mappings. This gives us the structure needed to call the database for these explicit loading tests. We also need to set up an initializer to insert testable data into the database for us.

Once we have set up those things, we can write our query library. We will write this in the same way we normally would. The major difference is that in the query library we can set up a standard set of includes so we can apply these to each query. This will allow any query to include the object graph and give consistent expectations to the other developers leveraging our library. The other advantage of this is that we can define these includes in one place, and make them type safe.

There's more...

We can leverage some more Language Integrated Query features and Lambda statements here. For more details see the following sections.

Lambda statements

Lambda statements are simply the newest syntax for delegates. They give a standard language and syntax for short-lived and non-reusable methods. If we need to reuse these methods, we need to wrap them in a larger execution that will be named into a method or property.

LINQ aggregate

While the syntax is a bit difficult to read, the power of aggregation is difficult to ignore. We can take a list of anything and apply an accumulator function over every member of the sequence. For instance, taking a list of includable objects and accumulating them all in a query.

Expression of function of T

When we are building our application, it would be very easy to use functions instead of expressions and functions. However, this would not give us the power of deferred execution, or the ability to compose more to the expression tree before applying it. We want these benefits and therefore we will need to use expression of function.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset