Improving entity and library reuse

In this recipe, we will decouple the query and entity definitions from the database communication to illustrate how they can be leveraged separately.

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 Entity and Library Reuse 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 adding a new unit test named QueryTests to the test project. We will make a test that connects to the database and retrieves a couple of records with a reusable query using the following code:
    using System;
    using System.Linq;
    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 ShouldReturnRecordsFromTheDatabase()
        {
          //Arrange
          var init = new Initializer();
          var context = new
            BlogContext(Settings.Default.BlogConnection);
          init.InitializeDatabase(context);
          IBlogRepository repo = new BlogRepository(context);
          
          //Act
          var items = repo.Set<Blog>().FilterByBlogName("Test");
    
          //Assert
          Assert.AreEqual(2, items.Count());
        }
    
        [TestMethod]
        public void
          ShouldReturnRecordsFromAnotherSourceWithTheSameQuery()
        {
          //Arrange
          var anotherDatasource = new SomeOtherDataSource();
    
          //Act
          var items =
            anotherDatasource.Blogs.FilterByBlogName("Test");
    
          //Assert
          Assert.AreEqual(1,items.Count());
        }
    
      }
    }
  2. Add another C# class named SomeOtherDataSource to the test project with the following code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using BusinessLogic;
    
    namespace Test
    {
      public class SomeOtherDataSource
      {
        public IQueryable<Blog> Blogs
        {
          get
          {
            return new List<Blog>()
            {
              new Blog()
              {
                Creationdate = DateTime.Now,
                Rating = 1,
                Id = 1,
                ShortDescription = "Test",
                Title = "Not This one"
              },
            new Blog()
            {
              Creationdate = DateTime.Now,
              Rating = 1,
              Id = 1,
              ShortDescription = "Test",
              Title = "Test"
            }
    
          }.AsQueryable();
        }
      
      }
      }
    }
  3. Add an initializer to the DataAccess project Database folder with the following code to set up data:
    using System;
    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"
          });
          context.Set<Blog>().Add(new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Testing",
            Title = "Test Blog 2"
          });
          context.Set<Blog>().Add(new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Testing",
            Title = "not Blog"
          });
        context.SaveChanges();
        }
      }
    }
  4. 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; }
      }
    }
  5. Add a Mapping folder to the DataAccess 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");
        }
    
      }
    }
  6. 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();
        }
      }
    }
  7. Add a new folder named Queries to the BusinessLogic project, and add a new C# class to it named BlogQueries with the following code:
    using System.Linq;
    
    namespace BusinessLogic.Queries
    {
      public static class BlogQueries
      {
        public static IQueryable<Blog> FilterByBlogName
          (this IQueryable<Blog> items, string name)
        {
          return items.Where(x => x.Title.Contains(name));
        }
      }
    }
  8. Modify the Program.cs of the project ConsoleApp with the following code:
    using System;
    using BusinessLogic.Queries;
    
    namespace ConsoleApp
    {
      class Program
      {
        static void Main(string[] args)
        {
          var repo = new InMemoryRepository();
          var items = repo.Blogs.FilterByBlogName("Test");
    
          foreach (var blog in items)
          {
            Console.WriteLine(blog.Title);
          }
          Console.ReadLine();
        }
      }
    }
  9. Add a new C# class to the ConsoleApp project named InMemoryRepository with the following code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using BusinessLogic;
    
    namespace ConsoleApp
    {
      internal class InMemoryRepository
      {
        internal IQueryable<Blog> Blogs
        {
          get
          {
            return new List<Blog>()
            {
              new Blog()
                {
                  Creationdate = DateTime.Now,
                  Rating = 1,
                  Id = 1,
                  ShortDescription = "Test",
                  Title = "Not This one"
                },
                new Blog()
                {
                  Creationdate = DateTime.Now,
                  Rating = 1,
                  Id = 1,
                  ShortDescription = "Test",
                  Title = "Test"
                }
    
            }.AsQueryable();
          }
        }
      }
    }
  10. Run our test and it will work. Run the console application and it will work as well.

How it works...

We will start our solution by defining a test which encapsulates the functionality we are trying to complete. This will serve as our marker that we have accomplished the goal. The test, in this case, is that we can not only execute the defined queries on the database, but also use them against other collections of the proper type.

We initialized the database to seed records to filter, and we set up another data source that provides in memory lists of data to filter. We defined the blog entity, the mapping, and the DbContext so our test can function against the database. This will ensure that we have not broken the database communication by adding this level of reuse to the application.

The next piece that we move to is a console application that references the BusinessLogic project without referencing the DataAccess. This demonstrates that we have separated our functions correctly and that we can leverage it from multiple applications without any problems.

There's more...

This level of reuse should extend to applications that hit the same database but with different delivery mechanisms. There are several things to keep in mind here.

Schema and contract

This solution should only be used when we cannot define a service or we have tight control over the usage in both applications. The preferred way to share a database is to define a service that does the data access. We would then be sharing schema and contract details instead of sharing types and assemblies.

NuGet

If we need to share the assemblies between solutions directly, then there are some tools which will make this easier. One of those options is to define a NuGet package and host it internally in your company on a private NuGet repository. This will ensure updates are distributed and that everyone goes to the same place to get the reference instead of pulling and compiling their own updates, or even worse, changing and compiling them.

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

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