Friday 16 September 2011

Building MVC Application using Dependency Injection with MVCScaffolding and Ninject

Tools used when writing this article: MvcScaffolding ver 1.0.0, EntityFramework ver 4.1.10331.0, Ninject ver 2.2.1.4 and Ninject.MVC3 ver 2.2.2.0.

We can build an MVC application with Dependency Injection on its controllers quickly with MVCScaffolding and Ninject extension for MVC3 framework (Ninject.MVC3). In fact, when we install Ninject.MVC3, we will get both Ninject and Ninject.MVC3 packages. We would also need Entity Framework installed in our project to complete this exercise.

To check all installed packages in the project with NuGet:
PM> Get-Package

Assuming we already have MvcScaffolding and Entity Framework installed (installing MvcScaffolding will automatically add EF to the project if it not exists yet), what's missing is Ninject.MVC3.
To install Ninject.MVC3, do this:
PM> Install-Package Ninject.MVC3
If successful, it will add two packages to the project (Ninject and Ninject.MVC3).
It will also add 'NinjectMVC3.cs' class under 'App_Start' folder.

Prepare our model class, in this case I use a model called 'Team'. Then run the scaffolder:
PM> Scaffold Controller Team -Repository
This will add a database context file (if not exist yet), a cs file 'Models\TeamRepository.cs', a controller 'Controllers\TeamsController.cs' and some CRUD views.

Here is the generated content of 'TeamRepository.cs' file (it includes 'ITeamRepository' interface and 'TeamRepository' class on the same file):
public class TeamRepository : ITeamRepository
    {
        MvcScaffoldTestContext context = new MvcScaffoldTestContext();

        public IQueryable<Team> All
        {
            get { return context.Teams; }
        }

        public IQueryable<Team> AllIncluding(params Expression<Func<Team, object>>[] includeProperties)
        {
            IQueryable<Team> query = context.Teams;
            foreach (var includeProperty in includeProperties) {
                query = query.Include(includeProperty);
            }
            return query;
        }

        public Team Find(int id)
        {
            return context.Teams.Find(id);
        }

        public void InsertOrUpdate(Team team)
        {
            if (team.TeamId == default(int)) {
                // New entity
                context.Teams.Add(team);
            } else {
                // Existing entity
                context.Entry(team).State = EntityState.Modified;
            }
        }

        public void Delete(int id)
        {
            var team = context.Teams.Find(id);
            context.Teams.Remove(team);
        }

        public void Save()
        {
            context.SaveChanges();
        }
    }

    public interface ITeamRepository
    {
        IQueryable<Team> All { get; }
        IQueryable<Team> AllIncluding(params Expression<Func<Team, object>>[] includeProperties);
        Team Find(int id);
        void InsertOrUpdate(Team team);
        void Delete(int id);
        void Save();
    }

and part of 'TeamController.cs' class:
  private readonly ITeamRepository teamRepository;

  // If you are using Dependency Injection, you can delete the following constructor
        public TeamsController() : this(new TeamRepository())
        {
        }

        public TeamsController(ITeamRepository teamRepository)
        {
   this.teamRepository = teamRepository;
        }

        . . .

Next, we need to uncomment this on 'TeamController.cs':
/*public TeamsController() : this(new TeamRepository())
{
}*/

Finally, bind our interface to the concrete class on 'NinjectMVC3.cs':
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ITeamRepository>().To<TeamRepository>();
}   
This means whenever our application encounters 'ITeamRepository' interface, it will refer to/use 'TeamRepository' class.

That's all that we need to do!

If you missed the last step, you will get this error:

Friday 2 September 2011

Entity Framework Code First - a Model that has Multiple Members Referring to another Single Model

Says we want to create a ‘Match’ model that will have two ‘Teams’.

The first thing that we need to do is to add two properties on the ‘Match’ class. In this example, we name them ‘HostTeamId’ and ‘GuestTeamId’ (line 9 & 12).

Then we need to add two virtual members to the class which are ‘HostTeam’ and ‘GuestTeam’ (line 15 & 18). I like to specify the ‘ForeignKey’ attribute explicitly to make it more readable even tough it may work if I omit those (not sure, I haven’t tried on this multiple references case).
public class Match
    {
        public int MatchId { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        public int HostTeamId { get; set; }

        [Required]
        public int GuestTeamId { get; set; }

        [ForeignKey("HostTeamId")]
        public virtual Team HostTeam { get; set; }

        [ForeignKey("GuestTeamId")]
        public virtual Team GuestTeam { get; set; }

    }

Then we also need to add virtual properties on the ‘Team’ class to indicate relationship with the ‘Match’ class.
public class Team
    {
        public int TeamId { get; set; }

        [Required]
        public string Name { get; set; }

        public string City { get; set; }

        public DateTime Founded { get; set; }

        public virtual ICollection<Player> Players { get; set; }
        public virtual ICollection<Match> HomeMatches { get; set; }
        public virtual ICollection<Match> AwayMatches { get; set; }
    }

Finally we need to implement the override method ‘OnModelCreating’ on our data context class. We need to specify both two referring members of ‘Match’ class ('HostTeam' and 'GuestTeam') with correct relationship to 'Team' class. In this case I use 'WithMany' and 'HasForeignKey' methods.
. . .
        public DbSet<MvcScaffoldTest.Models.Team> Teams { get; set; }

        public DbSet<MvcScaffoldTest.Models.Match> Matches { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Match>()
                    .HasRequired(m => m.HostTeam)
                    .WithMany(t => t.HomeMatches)
                    .HasForeignKey(m => m.HostTeamId)
                    .WillCascadeOnDelete(false);

            modelBuilder.Entity<Match>()
                    .HasRequired(m => m.GuestTeam)
                    .WithMany(t => t.AwayMatches)
                    .HasForeignKey(m => m.GuestTeamId)
                    .WillCascadeOnDelete(false);
        }
        . . .