Creating one-to-many maps

This recipe will take us through the process of configuring the relationship between one object and many other objects.

Getting ready

We will be using 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 the data and updating it.

Open the Improving One-To-Many References solution in the included source code examples.

How to do it...

Let us get connected to the database using the following steps:

  1. We start by adding a new unit test named MappingTest to the Test project. We make a test that connects to the database and retrieves an object. This will test the configuration and ensure that the model matches the database schema. Use the following code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using BusinessLogic;
    using DataAccess;
    using DataAccess.Database;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Test.Properties;
    using System.Data.Entity;
    namespace Test
    {
      [TestClass]
      public class MappingTest
      {
        [TestMethod]
        public void ShouldReturnABlogWithPosts()
        {
          //Arrange
          Database.SetInitializer(new Initializer());
          var context = new BlogContext(Settings.Default.BlogConnection);
          //Act
          var blog = context.Blogs.Include(x => x.Posts).FirstOrDefault();
          //Assert
          Assert.IsNotNull(blog);
          Assert.IsNotNull(blog.Posts);
        }
      }
    }
  2. In the BusinessLogic project, add a new C# class named Blog to the following code:
    using System;
    using System.Collections.Generic;
    using DataAccess;
    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 AuthorDetail AuthorDetail { get; set; }
        public ICollection<Post> Posts { get; set; } 
      }
    }
  3. In the BusinessLogic project, add a new C# class named Post to the following code:
    using System;
    namespace BusinessLogic
    {
      public class Post
      {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public DateTime PostedDate { get; set; }
        public Blog Blog { get; set; }
      }
    }
  4. 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);
          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");
          this.HasRequired(x => x.AuthorDetail);
          this.HasMany(x => x.Posts).WithRequired(x => x.Blog).WillCascadeOnDelete();
        }
      }
    }
  5. Add a new C# class named PostMapping to the Mapping folder with the following code:
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.ModelConfiguration;
    using BusinessLogic;
    namespace DataAccess.Mappings
    {
      public class PostMapping : EntityTypeConfiguration<Post>
      {
        public PostMapping()
        {
        this.ToTable("Posts");
        this.HasKey(x => x.Id);
        this.Property(x => x.Id).HasColumnName("PostId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        this.Property(x => 
          x.Content).HasColumnName("Body").IsMaxLength();
        this.Property(x => x.PostedDate).HasColumnName("PostedDate");
        this.Property(x => x.Title).HasColumnName("Title").IsMaxLength();
        }
      }
    }
  6. Modify the BlogContext class, with the following code, to add the collections and mappings for our objects:
    using System;
    using System.Data.Entity;
    using System.Linq;
    using BusinessLogic;
    using DataAccess.Mappings;
    namespace DataAccess
    {
      public class BlogContext : DbContext
      {
        public BlogContext(string connectionString)
        : base(connectionString)
        {
        }
        protected override void OnModelCreating(DbModelBuilder 
          modelBuilder)
        {
          modelBuilder.Configurations.Add(new BlogMapping());
          modelBuilder.Configurations.Add(new AuthorDetailMapping());
          modelBuilder.Configurations.Add(new PostMapping());
          base.OnModelCreating(modelBuilder);
        }
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
        public DbSet<AuthorDetail> AuthorDetails { get; set; } 
        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. Run our test, and see how it works.

How it works...

We start off with a test that ensures that the Posts property does not come back null from the database after initializing data to it. This will allow us to ensure that our code accomplishes the intended behaviour.

The HasMany() method is what we are focusing on in this recipe. This method allows us to establish one-to-many relationships in the code. Here, the collection of Posts that we added to Blog is ICollection<T> for usage. All the collections of objects on the "many" side need to be housed this way. This allows Entity Framework to return DbSet<T>, but we do not have to spread that Entity Framework-specific type (and the dependencies it has) throughout our code.

The WithRequired() method sets up the dependent side of the relationship, and forces the object relationship to exist. This is mapped to a foreign key column in the database that is not null. This will give an update error if an object is not provided, to prevent the attempt to insert bad data.

The WillCascadeOnDelete() method allows us to specify that the related objects are in context. When a delete on this object happens, the mapped relationship will be deleted as well. This only applies to objects that are previously loaded by the context and are still in the context tracker. If you need the database side cascades, you must set the property on the foreign key.

There's more...

When dealing with one-to-many relationships, there are many varieties, and configuring them takes several methods and practices.

More fluent configurations

More fluent configurations are as follows:

  • WithOptional(): This allows for an option named, one-to-many relationship.
  • Map(): This allows for the configuration of relationships using columns not exposed in the object model.
  • HasForeignKey(): This allows for the use of a named foreign key (in the database to be used for a relationship), instead of a coded configuration.

More than one way to improve

Often, there is more than one way to work through the configuration in a code, as there is to set up a data structure. This variability will also lead to differing opinions on how to define these configurations. We take the "configuration" approach of consolidating configuration to the central objects in the domain model. This allows more configurations to be viewed from those key maps. This has the added benefit of showing you the object interaction at the hubs of the application. You can define the same one-to-many relationship by using HasRequired(x=>x.Blog) with Many(x=>x.Posts), from PostMapping. This, however, would start spreading out the configuration and makes it harder to maintain.

See also

In this chapter:

  • Creating many-to-many maps
..................Content has been hidden....................

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