Validating collection properties

In this recipe, we will learn to define restrictions on the number of related objects that are defined.

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 the data and updating it.

Open the Improving Collection Property Validation solution in the included source code examples.

How to do it...

Let's get connected to the database using the following steps:

  1. We start by adding a new unit test named ValidationTests to the test project. We make a test that connects to the database and adds an object. This will test whether the configuration and our validation code are properly separated:
    using System;
    using System.Collections.Generic;
    using System.Data.Entity.Validation;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using BusinessLogic;
    using DataAccess;
    using DataAccess.Database;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Test.Properties;
    using System.Data.Entity;
    
    namespace Test
    {
      [TestClass]
      public class ValidationTest
      {
    
        [TestMethod]
        [ExpectedException(typeof(DbEntityValidationException))]
        public void ShouldErrorOnBelowMinimumPosts()
        {
          //Arrange
          var init = new Initializer();
          var context = new BlogContext(Settings.Default.BlogConnection);
          init.InitializeDatabase(context);
          var blog = new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Test",
            Title = "Test",
            Posts = new List<Post>()
            {
              new Post()
            }.ToArray()
          };
    
          //Act
          context.Set<Blog>().Add(blog);
          context.SaveChanges();
    
          //Assert
          Assert.Fail("Didn't Error");
        }
    
        [TestMethod]
        [ExpectedException(typeof(DbEntityValidationException))]
        public void ShouldErrorOnAboveMaximumPosts()
        {
          //Arrange
          var init = new Initializer();
          var context = new BlogContext(Settings.Default.BlogConnection);
          init.InitializeDatabase(context);
          var blog = new Blog()
          {
            Creationdate = DateTime.Now,
            ShortDescription = "Test",
            Title = "Test",
            Posts = new List<Post>()
            {
              new Post(),
              new Post(),
              new Post(),
              new Post()
            }.ToArray()
          };
    
          //Act
          context.Set<Blog>().Add(blog);
          context.SaveChanges();
    
          //Assert
          Assert.Fail("Didn't Error");
        }
    
      }
    
    }
  2. Add an initializer to the DataAccess project in the Database folder with the following code to set up the 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",
            Posts = new Post[]{new Post()}
          });
          context.SaveChanges();
        }
      }
    }
  3. In the BusinessLogic project, add a new C# class named Blog with the following code:
    using System;
    using System.Collections.Generic;
    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; }
    
        [MaxLength(3, ErrorMessage = "Cannot have more than 3 posts"),MinLength(2, ErrorMessage = "Must Have at least 2 posts")]
    
        public Post[] Posts { get; set; }
      }
    }
  4. Add another C# class named Post to the BusinessLogic project with the following code:
    namespace BusinessLogic
    {
      public class Post
      {
        public int Id { get; set; }
      }
    }
  5. Add a Mapping folder to the DataAccess project, and 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. Add another mapping C# class named PostMapping to the 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.HasKey(x => x.Id);
    
          this.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    
        }
      }
    }
  7. Modify the BlogContext class to contain the new mappings 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());
          modelBuilder.Configurations.Add(new PostMapping());
          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();
        }
      }
    }
  8. Run our test, and see how it works.

How it works...

We start by defining a test that codifies our intent to limit the maximum number of posts to 3, and the minimum number to 2. This will allow us to test our implementation for completeness, and will make sure that we only write the code required to accomplish the goals.

The Blog object has a collection of Post objects that is restricted by the MaxLength and MinLength attributes. This shows that we can layer on validation attributes, and they will both get enforced. This enables us to compose a complex set of restrictions that are simple yet powerful when used in combination.

The mapping and blog context allow us to specify the structure restrictions through the mappings, and wire them into the blog context for communication.

There's more...

When business rules require that certain sets of data have relationships with x number of minimum or maximum objects. Here are some best practices:

Limiting reusability

If we specify the restriction that all blogs must have at least two, but no more than three posts, then we have limited our reusability of the blog. If anyone wants to use this object outside our environment, then we need to spend some design time thinking about how our data validation will affect them, whether positively and negatively.

Validating the internal structure

The length specification does not verify the internal structure of the post, and does not cover any of the post validation. It only restricts the number of objects contained. This will be of limited use, but is powerful.

See also

In this chapter:

  • Creating custom property validation
..................Content has been hidden....................

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