Quantcast
Channel: Christos S. – chsakell's Blog
Viewing all 43 articles
Browse latest View live

ASP.NET MVC Solution Architecture – Best Practices

$
0
0

Choosing the right architecture for Web Applications is a must, especially for large scale ones. Using the default Visual Studio ASP.NET MVC Web Application project templates, adding controllers with Scaffolding options, just to bootstrap your application and create pages and data in just a few minutes, sounds awesome for sure, but let’s be honest it’s not always the right choise. Peeking all the default options, keeping business, data and presentation logic in the same project will impact several factors in your solutions, such as scalability, usability or testability. In this post, will see how to keep things clean, creating a highly loosely coupled ASP.NET MVC Solution, where Data Access, Business and Presentation layers are defined in the right manner. To do this, we ‘ll make use of several patterns and frameworks, some of those are presented below.

  1. Entity Framework Code First development
  2. Generic Repository Pattern
  3. Dependency Injection using Autofac framework
  4. Automapper

The architecture we will try to build in this post is summarized in the following diagram.
mvc-architecture-01
Let’s start. Assuming we want to built an e-shop Web Application called “Store”, create a blank solution with the same name.

Models

Add a class library project to the solution, named Store.Model. This library is where we ‘ll keep all of our domain objects. Entity Framework will count on them in order to build the database but we are not going to configure Code First using DataAnnotations attributes on this project. Instead, we are going to put all the Code First configuration in specific Configuration classes using the Fluent API. Add a folder named Models and add the following two simple classes.

public class Gadget
    {
        public int GadgetID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Image { get; set; }

        public int CategoryID { get; set; }
        public Category Category { get; set; }
    }
public class Category
    {
        public int CategoryID { get; set; }
        public string Name { get; set; }
        public DateTime? DateCreated { get; set; }
        public DateTime? DateUpdated { get; set; }

        public virtual List<Gadget> Gadgets { get; set; }

        public Category()
        {
            DateCreated = DateTime.Now;
        }
    }

I preffered to keep the namespace Store.Model instead of the namespace Store.Model.Models

Data Access Layer and Repositories

mvc-architecture-02
The purpose of this layer, is the direct access to the database. It’s the only layer responsible to communicate with the database. If some other layer wants to access the database, then this will be done through some of the classes (repositories) we will define in this project. This will be strictly the only way Add a new class library project named Store.Data and make sure you add a reference to the previous created project, Store.Model. Install Entity Framework using the Nuget Package Manager. First thing we wanna do, is to define the Entity Type Configurations for our domain objecs. Add a folder named Configuration with the following two classes that inherits the EntityTypeConfiguration class.

public class GadgetConfiguration: EntityTypeConfiguration<Gadget>
    {
        public GadgetConfiguration()
        {
            ToTable("Gadgets");
            Property(g => g.Name).IsRequired().HasMaxLength(50);
            Property(g => g.Price).IsRequired().HasPrecision(8, 2);
            Property(g => g.CategoryID).IsRequired();
        }
    }
public class CategoryConfiguration : EntityTypeConfiguration<Category>
    {
        public CategoryConfiguration()
        {
            ToTable("Categories");
            Property(c => c.Name).IsRequired().HasMaxLength(50);
        }
    }

There isn’t any difficult configuration to explain, the point is just to understand where to put the right objects. The next thing we will do is to create the DbContext class that will be responsible to access the database. Add the following class under the root of the current project.

public class StoreEntities : DbContext
    {
        public StoreEntities() : base("StoreEntities") { }

        public DbSet<Gadget> Gadgets { get; set; }
        public DbSet<Category> Categories { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new GadgetConfiguration());
            modelBuilder.Configurations.Add(new CategoryConfiguration());
        }
    }

We want to seed the database when our application fire up for the first time, so add the following class to the root of the project as well.

public class StoreSeedData : DropCreateDatabaseIfModelChanges<StoreEntities>
    {
        protected override void Seed(StoreEntities context)
        {
            GetCategories().ForEach(c => context.Categories.Add(c));
            GetGadgets().ForEach(g => context.Gadgets.Add(g));

            context.Commit();
        }

        private static List<Category> GetCategories()
        {
            return new List<Category>
            {
                new Category {
                    Name = "Tablets"
                },
                new Category {
                    Name = "Laptops"
                },
                new Category {
                    Name = "Mobiles"
                }
            };
        }

        private static List<Gadget> GetGadgets()
        {
            return new List<Gadget>
            {
                new Gadget {
                    Name = "ProntoTec 7",
                    Description = "Android 4.4 KitKat Tablet PC, Cortex A8 1.2 GHz Dual Core Processor,512MB / 4GB,Dual Camera,G-Sensor (Black)",
                    CategoryID = 1,
                    Price = 46.99m,
                    Image = "prontotec.jpg"
                },
                new Gadget {
                    Name = "Samsung Galaxy",
                    Description = "Android 4.4 Kit Kat OS, 1.2 GHz quad-core processor",
                    CategoryID = 1,
                    Price = 120.95m,
                    Image= "samsung-galaxy.jpg"
                },
                new Gadget {
                    Name = "NeuTab® N7 Pro 7",
                    Description = "NeuTab N7 Pro tablet features the amazing powerful, Quad Core processor performs approximately Double multitasking running speed, and is more reliable than ever",
                    CategoryID = 1,
                    Price = 59.99m,
                    Image= "neutab.jpg"
                },
                new Gadget {
                    Name = "Dragon Touch® Y88X 7",
                    Description = "Dragon Touch Y88X tablet featuring the incredible powerful Allwinner Quad Core A33, up to four times faster CPU, ensures faster multitasking speed than ever. With the super-portable size, you get a robust power in a device that can be taken everywhere",
                    CategoryID = 1,
                    Price = 54.99m,
                    Image= "dragon-touch.jpg"
                },
                new Gadget {
                    Name = "Alldaymall A88X 7",
                    Description = "This Alldaymall tablet featuring the incredible powerful Allwinner Quad Core A33, up to four times faster CPU, ensures faster multitasking speed than ever. With the super-portable size, you get a robust power in a device that can be taken everywhere",
                    CategoryID = 1,
                    Price = 47.99m,
                    Image= "Alldaymall.jpg"
                },
                new Gadget {
                    Name = "ASUS MeMO",
                    Description = "Pad 7 ME170CX-A1-BK 7-Inch 16GB Tablet. Dual-Core Intel Atom Z2520 1.2GHz CPU",
                    CategoryID = 1,
                    Price = 94.99m,
                    Image= "asus-memo.jpg"
                },
                // Code ommitted 
            };
        }
    }

I have ommitted some of the Gadges objects for brevity but you can always download the solution project at the bottom of this post. Now let’s create the Heart of this project. Add a folder named Infrastructure. In order to use the Repository Pattern in the right manner, we need to define a well designed infrastructure. From the bottom to the top all instances will be available through injected interfaces. And the first instance we will require, guess what.. will be an instance of the StoreEntities. So let’s create a factory Interface responsible to initialize instances of this class. Add an interface named IDbFactory into the Infrastructure folder.

public interface IDbFactory : IDisposable
    {
        StoreEntities Init();
    }

You can see that this interface inherits the IDisposable one, so the Concrete class that will implement the IDbFactory interface, must also implement the IDisposable one. To do this in a clean way, add a Disposable class that will implement the IDisposable interface. Then any class that will implement the IDbFactory interface, will just want to inherit this very class.

public class Disposable : IDisposable
    {
        private bool isDisposed;

        ~Disposable()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if (!isDisposed && disposing)
            {
                DisposeCore();
            }

            isDisposed = true;
        }

        // Ovveride this to dispose custom objects
        protected virtual void DisposeCore()
        {
        }
    }

I have highlighted the DisposeCore virtual method cause this method will make others classes inherit this one, to dispose their own objects in the way the need to. Now add the implementation class of the IDbFactory interface.

public class DbFactory : Disposable, IDbFactory
    {
        StoreEntities dbContext;

        public StoreEntities Init()
        {
            return dbContext ?? (dbContext = new StoreEntities());
        }

        protected override void DisposeCore()
        {
            if (dbContext != null)
                dbContext.Dispose();
        }
    }

It’s time to create a generic IRepository interface where we will declare the default operations that our repositories will support. Here I added some that i thought are the most used ones, but you can extend those operations as you wish.

public interface IRepository<T> where T : class
    {
        // Marks an entity as new
        void Add(T entity);
        // Marks an entity as modified
        void Update(T entity);
        // Marks an entity to be removed
        void Delete(T entity);
        void Delete(Expression<Func<T, bool>> where);
        // Get an entity by int id
        T GetById(int id);
        // Get an entity using delegate
        T Get(Expression<Func<T, bool>> where);
        // Gets all entities of type T
        IEnumerable<T> GetAll();
        // Gets entities using delegate
        IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
    }

Notice that the CRUD operations are commented as Mark to do something... This means that when a repository implentation adds, updates or removes an entity, does not send the command to the database at that very moment. Instead, the caller (service layer) will be responsible to send a Commit command to the database through a IUnitOfWork injected instance. For this to be done will use a pattern called UnitOfWork. Add the following two files into the Infrastructure folder.

public interface IUnitOfWork
    {
        void Commit();
    }
public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbFactory dbFactory;
        private StoreEntities dbContext;

        public UnitOfWork(IDbFactory dbFactory)
        {
            this.dbFactory = dbFactory;
        }

        public StoreEntities DbContext
        {
            get { return dbContext ?? (dbContext = dbFactory.Init()); }
        }

        public void Commit()
        {
            DbContext.Commit();
        }
    }

In the same way we used the Disposable class we are going to use an abstract class that has virtual implementations of the IRepository interface. This base class will be inherited from all specific repositories and hence will implement the IRepository interface. Add the following class.

public abstract class RepositoryBase<T> where T : class
    {
        #region Properties
        private StoreEntities dataContext;
        private readonly IDbSet<T> dbSet;

        protected IDbFactory DbFactory
        {
            get;
            private set;
        }

        protected StoreEntities DbContext
        {
            get { return dataContext ?? (dataContext = DbFactory.Init()); }
        }
        #endregion

        protected RepositoryBase(IDbFactory dbFactory)
        {
            DbFactory = dbFactory;
            dbSet = DbContext.Set<T>();
        }

        #region Implementation
        public virtual void Add(T entity)
        {
            dbSet.Add(entity);
        }

        public virtual void Update(T entity)
        {
            dbSet.Attach(entity);
            dataContext.Entry(entity).State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            dbSet.Remove(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = dbSet.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
                dbSet.Remove(obj);
        }

        public virtual T GetById(int id)
        {
            return dbSet.Find(id);
        }

        public virtual IEnumerable<T> GetAll()
        {
            return dbSet.ToList();
        }

        public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
        {
            return dbSet.Where(where).ToList();
        }

        public T Get(Expression<Func<T, bool>> where)
        {
            return dbSet.Where(where).FirstOrDefault<T>();
        }

        #endregion
    
    }

Since the implementations marked as virtual, any repository can ovveride a specific operation as required. And now the Concrete repositories: Add a new folder named Repositories and add the following two classes:

public class GadgetRepository : RepositoryBase<Gadget>, IGadgetRepository
    {
        public GadgetRepository(IDbFactory dbFactory)
            : base(dbFactory) { }
    }

    public interface IGadgetRepository : IRepository<Gadget>
    {

    }
public class CategoryRepository : RepositoryBase<Category>, ICategoryRepository
    {
        public CategoryRepository(IDbFactory dbFactory)
            : base(dbFactory) { }

        public Category GetCategoryByName(string categoryName)
        {
            var category = this.DbContext.Categories.Where(c => c.Name == categoryName).FirstOrDefault();

            return category;
        }

        public override void Update(Category entity)
        {
            entity.DateUpdated = DateTime.Now;
            base.Update(entity);
        }
    }

    public interface ICategoryRepository : IRepository<Category>
    {
        Category GetCategoryByName(string categoryName);
    }

You can see that the GadgetRepository supports the default operations using the default behavior and of course that’s OK. On the other hand, you can see an example where a specific repository requires to either extend it’s operations (GetCategoryByName) or overried the default ones (Update). Usually you add a repository for each of your Model classes, hence each repository of type T, is responsible to manipulate a specific DbSet through the DbContext.Set<T>. We are done implementing the Data Access layer so we can procceed to the next one.

Service Layer

mvc-architecture-03
What operations do you want to expose your MVC Controllers? Where is the business logic is going to be implemented? Yeap.. you have guessed right, in this very layer. Add a new class library project named Store.Service. You will have to add references to the two previous created projects, Store.Model and Store.Data. Notice I haven’t told you yet to install Entity Framework to this project.. and I am not going to, cause any database operation required will be done through the injected repositories we created before. Add the first Service file to this project.

// operations you want to expose
    public interface IGadgetService
    {
        IEnumerable<Gadget> GetGadgets();
        IEnumerable<Gadget> GetCategoryGadgets(string categoryName, string gadgetName = null);
        Gadget GetGadget(int id);
        void CreateGadget(Gadget gadget);
        void SaveGadget();
    }

    public class GadgetService : IGadgetService
    {
        private readonly IGadgetRepository gadgetsRepository;
        private readonly ICategoryRepository categoryRepository;
        private readonly IUnitOfWork unitOfWork;

        public GadgetService(IGadgetRepository gadgetsRepository, ICategoryRepository categoryRepository, IUnitOfWork unitOfWork)
        {
            this.gadgetsRepository = gadgetsRepository;
            this.categoryRepository = categoryRepository;
            this.unitOfWork = unitOfWork;
        }

        #region IGadgetService Members

        public IEnumerable<Gadget> GetGadgets()
        {
            var gadgets = gadgetsRepository.GetAll();
            return gadgets;
        }

        public IEnumerable<Gadget> GetCategoryGadgets(string categoryName, string gadgetName = null)
        {
            var category = categoryRepository.GetCategoryByName(categoryName);
            return category.Gadgets.Where(g => g.Name.ToLower().Contains(gadgetName.ToLower().Trim()));
        }

        public Gadget GetGadget(int id)
        {
            var gadget = gadgetsRepository.GetById(id);
            return gadget;
        }

        public void CreateGadget(Gadget gadget)
        {
            gadgetsRepository.Add(gadget);
        }

        public void SaveGadget()
        {
            unitOfWork.Commit();
        }

        #endregion
    
    }

The first and the last highlighted code lines reminds you why we created the IUnitOfWork interface. If we wanted to create a gadget object though this service class, we would write something like this..

// init a gadget object..
gadgetService.CreateGadget(gadget);
gadgetService.SaveGadget();

The other highlighted code lines denotes that any required repository for this service, will be injected through it’s constructor. This will be done through a Dependency Container we will setup in the MVC project’s start up class, using the Autofac framework. In the same way I created the GadgetService.cs file.

 // operations you want to expose
    public interface ICategoryService
    {
        IEnumerable<Category> GetCategories(string name = null);
        Category GetCategory(int id);
        Category GetCategory(string name);
        void CreateCategory(Category category);
        void SaveCategory();
    }

    public class CategoryService : ICategoryService
    {
        private readonly ICategoryRepository categorysRepository;
        private readonly IUnitOfWork unitOfWork;

        public CategoryService(ICategoryRepository categorysRepository, IUnitOfWork unitOfWork)
        {
            this.categorysRepository = categorysRepository;
            this.unitOfWork = unitOfWork;
        }

        #region ICategoryService Members

        public IEnumerable<Category> GetCategories(string name = null)
        {
            if (string.IsNullOrEmpty(name))
                return categorysRepository.GetAll();
            else
                return categorysRepository.GetAll().Where(c => c.Name == name);
        }

        public Category GetCategory(int id)
        {
            var category = categorysRepository.GetById(id);
            return category;
        }

        public Category GetCategory(string name)
        {
            var category = categorysRepository.GetCategoryByName(name);
            return category;
        }

        public void CreateCategory(Category category)
        {
            categorysRepository.Add(category);
        }

        public void SaveCategory()
        {
            unitOfWork.Commit();
        }

        #endregion
    }

And we are done with the service layer as well. Let’s procceed with the last one, the ASP.NET MVC Web Application.

Presentation Layer

mvc-architecture-04
Add a new ASP.NET Web Application named Store.Web choosing the empty template with the MVC option checked. We need to add references to all of the previous class library projects and an Entity Framework installation via Nuget Packages as well. You may be wondering, are we going to write any Entity Framework related queries in this project? Not at all, we need though some of it’s namespaces so we can setup the database configurations for our application, such as the database initializer. And since we started with this, open Global.asax.cs file and add the following line of code to setup the seed initializer we created in the Store.Data project.

protected void Application_Start()
        {
            // Init database
            System.Data.Entity.Database.SetInitializer(new StoreSeedData());

            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

You will also need to create a connection string element to define where you want the database to be created. Add the following element in the Web.config file and changed it accoarding to your development enviroment requirements.

  <connectionStrings>
    <add name="StoreEntities" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=StoreDb;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

We have made such an effort in the previous steps to create repositories and services but now it’s the time to make them work all together. If you recall, all services contructors have repositories interfaces that must be injected to. The services themeselfs will later be injected in to the controllers conustructors and this is how our application will work. To achieve this, we need to setup Dependancy Injection and for thish reason I decided to use Autofac. Make sure you install Autofac ASP.NET MVC 5 Integration through Nuget Packages.
autofac
Create a Bootstrapper.cs file under the Start_App folder and paste the following code.

public static void Run()
        {
            SetAutofacContainer();
        }

        private static void SetAutofacContainer()
        {
            var builder = new ContainerBuilder();
            builder.RegisterControllers(Assembly.GetExecutingAssembly());
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
            builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerRequest();

            // Repositories
            builder.RegisterAssemblyTypes(typeof(GadgetRepository).Assembly)
                .Where(t => t.Name.EndsWith("Repository"))
                .AsImplementedInterfaces().InstancePerRequest();
            // Services
            builder.RegisterAssemblyTypes(typeof(GadgetService).Assembly)
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces().InstancePerRequest();

            IContainer container = builder.Build();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        }
    }

The code itself is self explanatory. I hope you have followed along with me and you have named your repository and service classes as I did, cause otherwise, this is not gonna work. There are two importants things left to complete the tutotiral. The first one to define ViewModel classes and set Automapper to map domain entities to viewmodels and backwards. The second one is to see how to setup CSS Bootstrap in our web application. I suppose most of you, install bootstrap from Nuget Packages and start adding css and script references to your project. Here though we will follow a different approach.

CSS Bootstrap

First of all download Boostrap distribution from the official site. Add three folders to your application named css, fonts and js respectively. In the css folder paste the bootstrap.css file from what you have downloaded, in the fonts folder paste everything is inside the respective fonts folder and in the js folder, just paste the bootstrap.js file. We are going to use Bundling and Minification for bootstrap and to achieve that you need to install Microsoft ASP.NET Web Optimazation Framework through Nuget Packages.
web-optimazation
When you finish installing this, add a new class named BundleConfig into the App_Start folder as follow:

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bootstrap/js").Include("~/js/bootstrap.js", "~/js/site.js"));
            bundles.Add(new StyleBundle("~/bootstrap/css").Include("~/css/bootstrap.css", "~/css/site.css"));

            BundleTable.EnableOptimizations = true;
        }
    }

As you can see I have also referenced site.js and site.css javascript and css files. Those files can host any bootstrap css customazations you wanna do or any javascript related code. Feel free to add the respective files and leave them empty. Now we need to declare that we want MVC to use bundling and minication, so add the following line into the Global.asax.cs file.

protected void Application_Start()
        {
            // Init database
            System.Data.Entity.Database.SetInitializer(new StoreSeedData());

            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            // Autofac and Automapper configurations
            Bootstrapper.Run();
        }

Notice that I have also called the Bootstrapper.Run() function that will setup the Autofac’s configuration we made before. This function will also configure Automapper, something we gonna see in a bit. Let’s finish with Bootrap for now. We will need a Layout to use for our application, so go and create a Shared folder under the Views folder and add a new item of type MVC 5 Layout Page (Razor) named _Layout.cshtml.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>@ViewBag.Title</title>
    <!-- Bootstrap -->
    @Styles.Render("~/bootstrap/css")
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/
    html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/
    respond.min.js"></script>
    <![endif]-->
</head>
<body>
    <nav id="myNavbar" class="navbar navbar-default navbar-inverse navbar-fixed-top" role="navigation">
        <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbarCollapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("Store", "Index", "Home", new { }, new { @class = "navbar-brand" })
            </div>
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="navbarCollapse">
                <ul class="nav navbar-nav">
                    <li class="active">
                        @Html.ActionLink("Tablets", "Index", "Home", new { category = "Tablets" }, null)
                    </li>
                    <li class="active">
                        @Html.ActionLink("Laptops", "Index", "Home", new { category = "Laptops" }, null)
                    </li>
                    <li class="active">
                        @Html.ActionLink("Mobiles", "Index", "Home", new { category = "Mobiles" }, null)
                    </li>
                </ul>
            </div>
    </nav>
    @RenderBody()
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    @Scripts.Render("~/bootstrap/js")
</body>
</html>

The page will probably complain that cannot resolve Razor syntax so you have to add the following using statement in the web.config file which is under the Views folder (not application’s web.config). Following is part of that file..

<namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="Store.Web" />
        <add namespace="System.Web.Optimization" />
      </namespaces>

Automapper

In a real application your domain objects will probably have a lot of properties but you only want to display some of them in the browser. More over when posting back to server, for example when creating objects though a form element, you also want to post only a few of the domain object’s properties. For this reason you define ViewModel objects and use them instead of the real domain ones. Make sure you install Automapper from Nuget Packages.
automapper
Add a new folder named ViewModels with the following classes.

public class GadgetViewModel
    {
        public int GadgetID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Image { get; set; }

        public int CategoryID { get; set; }
    }
public class CategoryViewModel
    {
        public int CategoryID { get; set; }
        public string Name { get; set; }

        public List<GadgetViewModel> Gadgets { get; set; }
    }
public class GadgetFormViewModel
    {
        public HttpPostedFileBase File { get; set; }
        public string GadgetTitle { get; set; }
        public string GadgetDescription { get; set; }
        public decimal GadgetPrice { get; set; }
        public int GadgetCategory { get; set; }
    }

When your ViewModel classes have properties named as the respective domain objects, Automapper is smart enough to make the mapping through default conventions. Otherwise you have to set the mapping manualy by yourself. Notice the last class I have added, the GadgetFormViewModel. We can make a convetion to add a “Form” word before “ViewModel” so that we know that this type of view model, is posted back to server through a form element. Let’s now configure the mappings. Add a new folder Mappings and add the following class file.

public class AutoMapperConfiguration
    {
        public static void Configure()
        {
            Mapper.Initialize(x =>
            {
                x.AddProfile<DomainToViewModelMappingProfile>();
                x.AddProfile<ViewModelToDomainMappingProfile>();
            });
        }
    }

We haven’t created the required profiles yet but we will in a bit. What I wanted to show you is that you can create as many Automapper profiles you want and then add them into Mapper.Initialize function. Here we will define two profiles, one to map domain models to ViewModels and another one for backwards. Add the following classes in the same folder as the previous.

public class DomainToViewModelMappingProfile : Profile
    {
        public override string ProfileName
        {
            get { return "DomainToViewModelMappings"; }
        }

        protected override void Configure()
        {
            Mapper.CreateMap<Category,CategoryViewModel>();
            Mapper.CreateMap<Gadget, GadgetViewModel>();
        }
    }
public class ViewModelToDomainMappingProfile : Profile
    {
        public override string ProfileName
        {
            get { return "ViewModelToDomainMappings"; }
        }

        protected override void Configure()
        {
            Mapper.CreateMap<GadgetFormViewModel, Gadget>()
                .ForMember(g => g.Name, map => map.MapFrom(vm => vm.GadgetTitle))
                .ForMember(g => g.Description, map => map.MapFrom(vm => vm.GadgetDescription))
                .ForMember(g => g.Price, map => map.MapFrom(vm => vm.GadgetPrice))
                .ForMember(g => g.Image, map => map.MapFrom(vm => vm.File.FileName))
                .ForMember(g => g.CategoryID, map => map.MapFrom(vm => vm.GadgetCategory));
        }
    }

For the Domain -> ViewModels mapping we didn’t need to setup anything. Automapper will use the default conventions and that’s fine. For our GadgetFormViewModel -> Gadget mapping though, we set manually the configuration as shown above. The last thing remained to finish with Automapper is to add the following line in the Bootstrapper class.

public static class Bootstrapper
    {
        public static void Run()
        {
            SetAutofacContainer();
            //Configure AutoMapper
            AutoMapperConfiguration.Configure();
        }
// Code ommitted 

Controllers and Views

We are almost finished. Add a new MVC Controller named HomeController and paste the following code.

public class HomeController : Controller
    {
        private readonly ICategoryService categoryService;
        private readonly IGadgetService gadgetService;

        public HomeController(ICategoryService categoryService, IGadgetService gadgetService)
        {
            this.categoryService = categoryService;
            this.gadgetService = gadgetService;
        }

        // GET: Home
        public ActionResult Index(string category = null)
        {
            IEnumerable<CategoryViewModel> viewModelGadgets;
            IEnumerable<Category> categories;

            categories = categoryService.GetCategories(category).ToList();

            viewModelGadgets = Mapper.Map<IEnumerable<Category>, IEnumerable<CategoryViewModel>>(categories);
            return View(viewModelGadgets);
        }
    }

Now you can see in action why we have made such an effort to setup Repositories, Services, Autofac and Automapper. Services will be injected in the controller for each request and their data will be mapped to ViewModels before send to the Client. Right click in the Index action and add a View named Index with the following code. I must mention here, that the gadgets objects we use, have image references to a folder named images in the Web Application project. You can use your images or just download this project at the end of this post.

@model IEnumerable<Store.Web.ViewModels.CategoryViewModel>

@{
    ViewBag.Title = "Store";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<p>

</p>
<div class="container">
    <div class="jumbotron">

        @foreach (var item in Model)
        {
            <div class="panel panel-default">
                <div class="panel-heading">
                    @*@Html.DisplayFor(modelItem => item.Name)*@
                    @Html.ActionLink("View all " + item.Name, "Index", new { category = item.Name }, new { @class = "pull-right" })
                    @using (Html.BeginForm("Filter", "Home", new { category = item.Name }, FormMethod.Post, new { @class = "navbar-form" }))
                    {
                        @Html.TextBox("gadgetName", null, new { @class = "form-control", placeholder = "Search in " + item.Name })
                    }


                </div>
                @foreach (var gadget in item.Gadgets)
                {
                    @Html.Partial("Gadget", gadget)
                }
                <div class="panel-footer">
                    @using (Html.BeginForm("Create", "Home", FormMethod.Post,
                            new { enctype = "multipart/form-data", @class = "form-inline" }))
                    {
                        @Html.Hidden("GadgetCategory", item.CategoryID)
                        <div class="form-group">
                            <label class="sr-only" for="file">File</label>
                            <input type="file" class="form-control" name="file" placeholder="Select picture..">
                        </div>
                        <div class="form-group">
                            <label class="sr-only" for="gadgetTitle">Title</label>
                            <input type="text" class="form-control" name="gadgetTitle" placeholder="Title">
                        </div>
                        <div class="form-group">
                            <label class="sr-only" for="gadgetName">Price</label>
                            <input type="number" class="form-control" name="gadgetPrice" placeholder="Price">
                        </div>
                        <div class="form-group">
                            <label class="sr-only" for="gadgetName">Description</label>
                            <input type="text" class="form-control" name="gadgetDescription" placeholder="Description">
                        </div>
                        <button type="submit" class="btn btn-primary">Upload</button>
                    }
                </div>
            </div>
        }

    </div>

</div>

Two things to notice here. The first one is that we need to create a Partial view to diplay a GadgetViewModel object and the second one is the Form’s control element’s names. You can see that they much our GadgetFormViewModel properties. Under the Shared folder create the following Partial view for displaying a GadgetViewModel object.

@model Store.Web.ViewModels.GadgetViewModel

<div class="panel-body">
    <div class="media">
        <a class="pull-left" href="#">
            <img class="media-object" src="../../images/@Model.Image" />
        </a>
        <div class="media-body">
            <h3 class="media-heading">
                @Model.Name
            </h3>
            <p>@Model.Description</p>
        </div>
    </div>
</div>

In the Index.cshtml page I have added search and filter functionality and Create gadget as well. To achieve that you need to add the following Action methods to the HomeController.

public ActionResult Filter(string category, string gadgetName)
        {
            IEnumerable<GadgetViewModel> viewModelGadgets;
            IEnumerable<Gadget> gadgets;

            gadgets = gadgetService.GetCategoryGadgets(category, gadgetName);

            viewModelGadgets = Mapper.Map<IEnumerable<Gadget>, IEnumerable<GadgetViewModel>>(gadgets);

            return View(viewModelGadgets);
        }

        [HttpPost]
        public ActionResult Create(GadgetFormViewModel newGadget)
        {
            if (newGadget != null && newGadget.File != null)
            {
                var gadget = Mapper.Map<GadgetFormViewModel, Gadget>(newGadget);
                gadgetService.CreateGadget(gadget);

                string gadgetPicture = System.IO.Path.GetFileName(newGadget.File.FileName);
                string path = System.IO.Path.Combine(Server.MapPath("~/images/"), gadgetPicture);
                newGadget.File.SaveAs(path);

                gadgetService.SaveGadget();
            }

            var category = categoryService.GetCategory(newGadget.GadgetCategory);
            return RedirectToAction("Index", new { category = category.Name });
        }

I am sure that at this point you understand the purpose of all the above code so I won’t explain anything. You need to add a Filter page so right click in the Filter action and create the following View.

@model IEnumerable<Store.Web.ViewModels.GadgetViewModel>

@{
    ViewBag.Title = "Filter";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container">
    <div class="jumbotron">

        @foreach (var item in Model)
        {
            <div class="panel panel-default">
                <div class="panel-heading">
                    @Html.Label(item.Name)
                </div>
                @Html.Partial("Gadget", item)
            </div>
        }

    </div>
</div>

mvc-architecture-05
You can filter gadgets by category or search gadgets in a specific category. That’s it, we have finished creating a highly decoupled architecture that could support large scale MVC applications.

Github

I have decided to move all of my code to my Github account so you can download most of the projects that we have seen on this blog from there. You can download this project from here. I hope you enjoyed this post as much as I did.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small


ASP.NET MVC Session state Performance Issue

$
0
0

I cannot recall any real Web Application that doesn’t make use of the Session State feature, the one that is capable to store data that are available across multiple requests from the same browser. More over, in this very modern times, Web Applications tends to make extensive use of Ajax requests to the server, in order to avoid page refreshes. So the question here is, have you ever noticed performance issues while making multiple ajax requests to an ASP.NET MVC action when using Session data?

Weeks ago I read a nice ASP.NET MVC related post which I liked but when I reached the comments area I found viewers complaining about the performance of the post’s solution project after a certain point in application’s cycle. I was currius to find out what was happening cause on the first view everything seemed to be good. So I downloaded the solution and after searching a little bit.. boom! There it was, hidding behind the Session State feature.

Put Session into the Context

Before demonstrating the real problem with coding, let’s find out the basics on how Session works. When a new request is arrived to the server for the first time, which means no session cookie is contained, the server will create a new session identifier accessible through the

System.Web.HttpContext.Current.Session.SessionID

Though, this does not mean that from now on all requests back to server will contain a session cookie. Instead, this will happend only if a specific request store some data in the Session. In other words, ASP.NET Framework adds the session cookie to the response at the first time some data is stored in the session. So how is this related to performance issues? Well, normally ASP.NET can process multiple requests from the same browser concurrently which means for example, multiple Ajax requests can be processed simultaneously.
mvc-session-01
The above schema works only when no session cookie is contained in browser’s request or in other words, server hasn’t yet stored any data into the Session. If server does store some data into the session and hence adds a session cookie in the response, then all subsequent requests using the same session cookie are queued and proccessed sequentially.
mvc-session-02
This is actually a normal behavior, cause consider for example that multiple requests may modify or read the same Session key value simultaneously. That would certainly result in inconsistent data.

Code time

Create an ASP.NET Empty Web Application checking the MVC option and add the following HomeController.

[OutputCache(NoStore = true, Duration = 0)]
    public class HomeController : Controller
    {
        public List<string> boxes = new List<string>() { "red", "green", "blue", "black", "gray", "yellow", "orange" };
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }

        public string GetBox()
        {
            Random rnd = new Random();
            int index = rnd.Next(0, boxes.Count);

            return boxes[index];
        }

        public ActionResult StartSession()
        {
            System.Web.HttpContext.Current.Session["Name"] = "Chris";

            return RedirectToAction("Index");
        }
    }

The controller has a GetBox which returns a random color from a list of colors defined in the class. We will use it’s value to add a div element in the view having the background-color property set accoarding the returned value. More over, has a StartSession action which simply stores a session value and hence it’s the point that the server adds a session cookie into the response. You will need to add the Index view so right click in the index method, add it and paste the following code.

<!DOCTYPE html>
<html lang="en" ng-app="asyncApp">
<head>
    <title>Troubleshooting ASP.NET MVC Performance Issues</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
    <link href="~/Content/Site.css" rel="stylesheet" />
</head>
<body ng-controller="asyncCtrl" ng-init="getBoxes()">
    <nav role="navigation" class="navbar navbar-default navbar-fixed-top">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" data-target="#navbarCollapse" data-toggle="collapse" class="navbar-toggle">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>
            <!-- Collection of nav links and other content for toggling -->
            <div id="navbarCollapse" class="collapse navbar-collapse">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">Performace testing</a></li>
                    <li>
                        @Html.ActionLink("Start Session", "StartSession")
                    </li>
                    <li>
                        <a class="links" ng-click="getBoxes()">Not resolved</a>
                    </li>
                    <li>
                        <a class="links" ng-click="getBoxes(true)">Resolved</a>
                    </li>
                    <li>
                        <form class="navbar-form">
                            <label class="checkbox" style="margin-top:5px">
                                @Html.CheckBox("isSessionNewChk", Session.IsNewSession, new { @disabled = "disabled" })
                                Is Session New
                            </label>
                        </form>
                    </li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">{{boxes.length}} Boxes</a></li>
                </ul>
            </div>
        </div>
    </nav>
    <br /><br /><br />
    <div class="container">
        <div class="row">
            <div id="boxesContainer" ng-repeat="color in boxes track by $index">
                <div class="box" ng-class="color" />
            </div>
        </div>
        <br />
        <div class="row">
            <div id="timeOccured" ng-show="showResults" class="alert" ng-class="isResolved()" ng-bind="timeElapsed"></div>
        </div>
    </div>
    <script src="~/Scripts/app.js"></script>
</body>
</html>

Add a Content folder in the root of your application and create the following css stylesheet.


.box {
    height: 15px;
    width: 15px;
    margin: 2px;
    float:left;
}

#timeOccured {
    clear:both;
}

.links {
    cursor:pointer;
}

.red {
    background-color: red;
}

.green {
    background-color: green;
}

.blue {
    background-color: blue;
}

.black {
    background-color: black;
}

.gray {
    background-color: gray;
}

.yellow {
    background-color: yellow;
}

.orange {
    background-color: orange;
}

You will have noticed that I make use of simple AngularJS code but that’s OK if you aren’t familiar with it. Add a Scripts folder and create the following app.js javascript file.

angular.module('asyncApp', [])
    .value('mvcuri', 'http://localhost:49588/home/getbox')
    .value('mvcurisessionresolved', 'http://localhost:49588/SessionResolved/getbox')
    .controller('asyncCtrl', function ($http, $scope, mvcuri, mvcurisessionresolved) {

        $scope.boxes = [];
        $scope.showResults = false;
        var uri;

        $scope.getBoxes = function (resolved) {
            var start = new Date();
            var counter = 300;

            if (resolved)
                uri = mvcurisessionresolved;
            else
                uri = mvcuri;

            // Init variables
            $scope.boxes = [];
            $scope.showResults = false;
            $scope.timeElapsed = '';

            for (var i = 0; i < 300; i++) {
                $http.get(uri)
                    .success(function (data, status, headers, config) {
                        $scope.boxes.push(data);
                        counter--;

                        if (counter == 0) {
                            var time = new Date().getTime() - start.getTime();
                            $scope.timeElapsed = 'Time elapsed (ms): ' + time;
                            $scope.showResults = true;
                        }
                    })
                        .error(function (error) {
                            $scope.timeElapsed = error.Message;
                        }).finally(function () {
                        });
            }
        };

        $scope.isResolved = function () {
            return uri == mvcuri ? 'alert-danger' : 'alert-success';
        }

    });

Make sure you replace the localhost:port with yours. Before explain all application’s functionality let’s add a new MVC controller named SessionResolvedController.

[OutputCache(NoStore = true, Duration = 0)]
    public class SessionResolvedController : Controller
    {
        public List<string> boxes = new List<string>() { "red", "green", "blue", "black", "gray", "yellow", "orange" };

        public string GetBox()
        {
            Random rnd = new Random();
            int index = rnd.Next(0, boxes.Count);

            return boxes[index];
        }
    }

At this point you should be able to fire your application.
mvc-session-03
So what is happening here? When the application starts, 300 Ajax calls are sent to the HomeController’s GetBox action and foreach color returned a respective box is added to the view.

<body ng-controller="asyncCtrl" ng-init="getBoxes()">

<div id="boxesContainer" ng-repeat="color in boxes track by $index">
     <div class="box" ng-class="color" />
 </div>

Every time you refresh the Page you will notice that the Is Session New checkbox is checked, which means that there isn’t yet a session cookie sent to the browser. All Ajax requests are processed concurrently as expected and the time elapsed to complete all the ajax calls is displayed in a red box under all boxes. The Start Session button will simply store a session value as follow.

System.Web.HttpContext.Current.Session["Name"] = "Chris";

Press the button and notice the difference in the elapsed time for displaying all the 300 boxes. Also notice that the checkbox isn’t checked anymore.
mvc-session-04
Now that there is a Session cookie in each request to the server, all requests are proccessed sequentially as described previously. That’s why we need twice the time we needed before storing a session variable. You can repeat the same ajax requests without refreshing the page by clicking the Not Resolved button.

Solution

At the moment, if you press the Resolved button you will get the same results calling the SessionResolvedController’s GetBox() action.

<a class="links" ng-click="getBoxes(true)">Resolved</a>

Notice that we don’t read or modify any Session state data when calling the GetBox() actions, so the question is, is there anything that we can do to prevent our requests being processed sequentially despite the fact that they contain a cookie session? The answer is YES and it’s hidding in the SessionState attribute that can be applied in the controller’s class as follow:

[SessionState(SessionStateBehavior.Disabled)]
    public class SessionResolvedController : Controller
    {
        public List<string> boxes = new List<string>() { "red", "green", "blue", "black", "gray", "yellow", "orange" };

        public string GetBox()
        {
            Random rnd = new Random();
            int index = rnd.Next(0, boxes.Count);

            return boxes[index];
        }
    }

Applying this attribute to the controller will make all requests targeting this controller processed concurrently. Build, run your application and repeat what we did before. Notice that the Resolved button now finishes at the half time which was the time needed when no session cookie existed in the request.

I run the application without debugging in order to get better results. If you run with debugging you will get higher delays but that should not be a problem, the differences will be still noticeable.

mvc-session-05
And following is a complete demonstration of what we have done till now. When I fired the application for the first time, it tooked 928 milliseconds to complete all 300 Ajax requests. Then I clicked to create a Session cookie and that caused an important performance issue increasing the delay to 2201 milliseconds! When I requested the data through the SessionResolvedController the delay was almost the same when there wasn’t any Session cookie.
mvc-session-07
I have also used the

[OutputCache(NoStore = true, Duration = 0)]

to the MVC Controllers which will result to ask the browser (client) not to store the content retrieved from the server. This way the test is even more reliable.

Food for thought

So is this feature solves all our problems? No. If you apply this attribute you won’t be able to access any Session data in the respective controller, which means if you try so you will get a big fat Null reference exception.
mvc-session-06
I would recommend to use a Web API layer for all Ajax requests and leave MVC Controllers as are. The problem here has architectural design nature, not technical. ASP.NET MVC framework is smart enough to understand if requests must be processed concurrently or sequentially. Use Session state data when rendering a View or posting something to an MVC Controller’s action and make your ajax requests to a Web API layer.

That’s it, we are done describing a common perfomance issue using the Session state feature. I hope you enjoyed the post. You can download the project from here.


AngularJS feat. Web API – Enable Session State

$
0
0

Have you ever tried to use the Session object while using the Web API? If you did so, you must be dissapointed seeing that

System.Web.HttpContext.Current.Session

was always null and hence, you were unable to use it. This post is the second part of the 3 part Series about Web API feat. AngularJS. You can read the first post here, where we saw how to setup an ASP.NET MVC project to work with AngularJS. We built a GadgetStore application where user could add gadgets to a cart and then proceed with the checkout and order process. We also set that all server data used by AngularJS come from Web API Controllers and only. What we left though undone, is two basic things:

  • On a Page refresh, user looses all cart items since they are currently stored in javascript objects
  • No Authentication logic exists in GadgetStore application

This post will solve the first of the above problems, that is make sure that when the user refreshes the page doesn’t loose his cart items. In order to achieve this, we need to make our Web API Controllers support Session State. Let’s start.

Theory

Session State is not enabled by default in Web API and that’s fine cause doing so would break it’s HTTP Stateless nature. However, there are times that you need to enable Session State and there are two ways to achieve it. The first one is to enable it globally for all API Controllers which is something I wouldn’t recommend since it breaks the HTTP stateless concept for the entire Web API layer. The second one is to enable Session State for specific routes only, by using a simple trick that is move the Web API routes definitions in the same place where you define your MVC routes. There, using the MapHttpRoutemethod to define your API routes, you can set a custom HttpControllerRouteHandler to each of your routes. For those routes you want to enable Session State, the overrided GetHttpHandler method of this custom HttpControllerRouteHandler implementation, must return an instance of an HttpControllerHandler that implements the IRequiresSessionState interface.
web-api-session-01

Back to code

Download and open the starting solution from here. This is the GadgetStore application we built in the first part of these series. Create a folder name Infrastructure in the Store inside the MVC Project and add the following two classes:

public class SessionEnabledControllerHandler : HttpControllerHandler, IRequiresSessionState
    {
        public SessionEnabledControllerHandler(RouteData routeData)
            : base(routeData)
        { }
    }

 

public class SessionEnabledHttpControllerRouteHandler : HttpControllerRouteHandler
    {
        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new SessionEnabledControllerHandler(requestContext.RouteData);
        }
    }

Now switch to the WebApiConfig.cs file and comment out the Web API route definition as follow:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            // Moved to RouteConfig.cs to enable Session
            /*
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
             */
        }
    }

Now switch to RouteConfig where the MVC routes are defined and paste the following code:

public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            #region Web API Routes

            // Web API Session Enabled Route Configurations
            routes.MapHttpRoute(
                name: "SessionsRoute",
                routeTemplate: "api/sessions/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            ).RouteHandler = new SessionEnabledHttpControllerRouteHandler(); ;

            // Web API Stateless Route Configurations
            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            #endregion

            #region MVC Routes
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
            #endregion
        }
    }

You also need to add reference to System.Web.Http namespace for this to work. I made up a trick here, that allows you to use all of your controllers both as Session State enabled or not. The first Web API route (highlighted) makes the controller Session State enabled while the second one doesn’t. So if you have a controller named ItemsController and you want to use Session inside it’s actions, the all requests to this controller must be in the following form:

http://localhost:61691/api/sessions/items

On the other hand, if you want to use the default Stateless behavior, then you can send requests matching the default route:

http://localhost:61691/api/items

Now let’s see what changes we need to make in order not to loose the cart items of our AngularJS application. What we have to do, is every time the user adds or removes an item to/from the cart, save it’s state in the Session. If you remember, the javascript’s cart holds an array of items where each item declares gadget’s ammount added and it’s properties as well.

addProduct: function (id, name, price, category) {
            var addedToExistingItem = false;
            for (var i = 0; i < cartData.length; i++) {
                if (cartData[i].GadgetID == id) {
                    cartData[i].count++;
                    addedToExistingItem = true;
                    break;
                }
            }
            if (!addedToExistingItem) {
                cartData.push({
                    count: 1, GadgetID: id, Price: price, Name: name, CategoryID: category
                });
            }
        }

We need the same model at server side so for start, add the following class inside the Models folder.

public class CartItem
    {
        public int Count { get; set; }
        public int GadgetID { get; set; }
        public decimal Price { get; set; }
        public string Name { get; set; }
        public int CategoryID { get; set; }
    }

Let’s create now the API Controller that will hold the Session cart items. Add an Empty Web API Controller named TempOrdersController and paste the following code:

public class TempOrdersController : ApiController
    {
        // GET: api/TempOrders
        public List<CartItem> GetTempOrders()
        {
            List<CartItem> cartItems = null;

            if (System.Web.HttpContext.Current.Session["Cart"] != null)
            {
                cartItems = (List<CartItem>)System.Web.HttpContext.Current.Session["Cart"];
            }

            return cartItems;
        }

        // POST: api/TempOrders
        [HttpPost]
        public HttpResponseMessage SaveOrder(List<CartItem> cartItems)
        {
            if (!ModelState.IsValid)
            {
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            System.Web.HttpContext.Current.Session["Cart"] = cartItems;

            return new HttpResponseMessage(HttpStatusCode.OK);
        }
    }

Now let’s see all javascript changes we need to do inside the AngularJS application. First off all, locate the cartCmp.js file and add a new function to the cart factory:

storeCart.factory('cart', function () {
    var cartData = [];

    return {
        addProduct: function (id, name, price, category) {
            var addedToExistingItem = false;
            for (var i = 0; i < cartData.length; i++) {
                if (cartData[i].GadgetID == id) {
                    cartData[i].count++;
                    addedToExistingItem = true;
                    break;
                }
            }
            if (!addedToExistingItem) {
                cartData.push({
                    count: 1, GadgetID: id, Price: price, Name: name, CategoryID: category
                });
            }
        },
        removeProduct: function (id) {
            for (var i = 0; i < cartData.length; i++) {
                if (cartData[i].GadgetID == id) {
                    cartData.splice(i, 1);
                    break;
                }
            }
        },
        getProducts: function () {
            return cartData;
        },
        pushItem: function (item) {
            cartData.push({
                count: item.Count, GadgetID: item.GadgetID, Price: item.Price, Name: item.Name, CategoryID: item.CategoryID
            });
        }
    };
});

// Code omitted

Open gadgetStore.js and for start add a new constant value which defines the TempController’s Url.

angular.module('gadgetsStore')
	.constant('gadgetsUrl', 'http://localhost:61691/api/gadgets')
	.constant('ordersUrl', 'http://localhost:61691/api/orders')
	.constant('categoriesUrl', 'http://localhost:61691/api/categories')
    .constant('tempOrdersUrl', 'http://localhost:61691/api/sessions/temporders')
	.controller('gadgetStoreCtrl', function ($scope, $http, $location, gadgetsUrl, categoriesUrl, ordersUrl, tempOrdersUrl, cart) {
// Code omitted

Notice that we chose the Session Enabled route for the TempOrdersController, so that we can use System.Web.HttpContext.Current.Session. While at the same file, add the following two scope functions:

$scope.saveOrder = function () {
	        var currentProducts = cart.getProducts();

	        $http.post(tempOrdersUrl, currentProducts)
			    .success(function (data, status, headers, config) {
			    }).error(function (error) {
			    }).finally(function () {
			    });
	    }

	    $scope.checkSessionGadgets = function () {
	        $http.get(tempOrdersUrl)
            .success(function (data) {
                if (data) {
                    for (var i = 0; i < data.length; i++) {
                        var item = data[i];
                        cart.pushItem(item);
                    }
                }
            })
            .error(function (error) {
                console.log('error checking session: ' + error);
            });
	    }

The $scope.SaveOrder will be called each time the user adds or removes an item to cart and the $scope.checkSessionGadgets is the one that called when the user reaches our application’s page. This function is the one that actually solves the page refresh problem since it initiates the user’s cart. Switch to the MVC View /Home/Views/Index.cshtml and add the ng-init directive to the body element:

<body ng-controller='gadgetStoreCtrl' class="container" ng-init="checkSessionGadgets()">

The only thing remained to do is to save cart’s state each time the user changes it. Change the $scope.addProductToCart function as follow:

$scope.addProductToCart = function (product) {
        cart.addProduct(product.GadgetID, product.Name, product.Price, product.CategoryID);
        $scope.saveOrder();
    }

Same as above for removing an item from cart:

$scope.remove = function (id) {
        cart.removeProduct(id);
        $scope.saveOrder();
    }

We also need to make sure that when the user completes his order, also removes Session’s items. Switch again to the gadgetStore.js file and change the $scope.SendOrder function as follow:

$scope.sendOrder = function (shippingDetails) {
	        var order = angular.copy(shippingDetails);
	        order.gadgets = cart.getProducts();
	        $http.post(ordersUrl, order)
			.success(function (data, status, headers, config) {
			    $scope.data.OrderLocation = headers('Location');
			    $scope.data.OrderID = data.OrderID;
			    cart.getProducts().length = 0;
			    $scope.saveOrder();
			})
			.error(function (error) {
			    $scope.data.orderError = error;
			}).finally(function () {
			    $location.path("/complete");
			});
	    }

Now you are ready to build and run your application. What I want to show you is what AngularJS sends when the user adds 3 times the same item to cart:
web-api-session-02
Now if I add another different gadget to the cart..
web-api-session-03
Try to refresh the page and ensure that your cart items arent’ lost. We are done here with configuring our AngularJS – Web API Application support page refreshes. I hope you enjoyed the post as much as I did. Of course you can download the updated version of the GadgetStore application from here. The next post of these series will show you how to secure this application, so make sure you get subscribed and get notified!


AngularJS feat. Web API – Security

$
0
0

Preventing unauthorized access to Web API controller actions and applying a centralized security access, seems to be a tough and complicated task for many web developers. More over, there are many players entered into the game latetly, such as ASP.NET Identity or OWIN middleware and things can get pretty messed up if any of these concepts are not well understood.

This is the last part of the 3 Part Series about integrating Web API and AngularJS. What we have seen till now on the first and second post, is how to setup an MVC web application project work with AngularJS as the front-end and a Web API layer as the service layer. Second post’s objective was enabling Session State in to the Web API layer so that users don’t loose cart’s data at page refreshes. What we left undone though, is securing our application from unauthorized access which in our case that would be preventing unathenticated users from creating gadget orders. I ‘ll start adding new functionality to the GadgetStore project so in case you want to follow with me, you can download it from here. Let’s start!

I will break the Security integration into the following 3 parts starting from bottom to top.
web-api-security-01

ASP.NET Identity & Entity Framework

In our GadgetStore application we do use Entity Framework for database access but we don’t have any membership related Entities yet. In order to make Entity Framework act as the storage system of ASP.NET Identity we need to make use of two specific classes: IdentityUser and UserManager. Then we ‘ll change our StoreContext to inherit IdentityDbContext<IdentityUser> instead of simple DbContext. Mind that you don’t have to use the exact implementations of IdentityUser and UserManager as are, but you can create your own classes that derive from these two respectively. This is actually what we are gonna do right now. Starting from the Domain class library project install Microsoft ASP.NET Identity EntityFramework package using Nuget Packages. This will also install Microsoft ASP.NET Identity Core as follow.
web-api-security-02 Add an AppStoreUser class file and paste the following code.

public class AppStoreUser : IdentityUser
    {
        // Add any custom properties you wish here
    }

You can add any custom properties if you want, such as DateCreated, DateUpdated, etc. The default IdentityUser implementation has the following properties:

  1. ID: User’s unique id
  2. Email: User’s email address
  3. Claims: User’s claims collection
  4. Logins: User’s logins
  5. PasswordHash: User’s password hashed
  6. Roles: The roles that the user belongs to
  7. Phonenumber: User’s phone number
  8. SecurityStamp: A value that changes when user’s identity is altered
  9. Username: User’s username

Now switch to DataAccess class library project and install ASP.NET Identity Entity Framework package as well. UserIdentity class may have a special meaning for Entity Framework but that doesn’t mean it cannot have it’s own configuration. If you remember, we had a folder named Configurations inside this project, where we put all our Entity Framework Configurations. Let’s make the same for the previous class we added. Add a class named AppStoreUserConfiguration inside the configurations folder.

public class AppStoreUserConfiguration : EntityTypeConfiguration<AppStoreUser>
    {
        public AppStoreUserConfiguration()
        {
            ToTable("Users");
        }
    }

Generally, when Entity Framework tries to create User related entities in Database, it will also create the required membership schema. Those tables will be named IdentityUsers, IdentityUserClaims, etc.. In order to modify the table name definition for the IdentityUsers we created the above class. While at the same project open StoreContext.cs file and alter it as follow:

public class StoreContext : IdentityDbContext<AppStoreUser>
    {
        public StoreContext()
            : base("StoreContext", throwIfV1Schema: false) { }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
            modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
            modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });

            modelBuilder.Configurations.Add(new AppStoreUserConfiguration());
            modelBuilder.Configurations.Add(new CategoryConfiguration());
            modelBuilder.Configurations.Add(new OrderConfiguration());
        }

        public DbSet<Gadget> Gadgets { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<GadgetOrder> GadgetOrders { get; set; }

        public static StoreContext Create()
        {
            return new StoreContext();
        }
    }

The most important change to notice is that StoreContext class inherits IdentityDbContext and not DbContext anymore. Now let’s create the class that will inherit UserManager<T> where T is a UserIdentity class. We will place this class inside the Infrastructure folder in the Web project named Store. This means that you have to install ASP.NET Identity EntityFramework package in this project as well.

public class AppStoreUserManager : UserManager<AppStoreUser>
    {
        public AppStoreUserManager(IUserStore<AppStoreUser> store)
            : base(store)
        {
        }
    }

This class can be used to manage AppStoreUser instances, that is create, modify or even delete user entities. It isn’t though limited only to Entity Framework operations as will we ‘ll see later on this post.

OWIN Middleware

It’s time to setup an OWIN OAth2 Authorization Server in our application. Before starting adding the required components make sure you have the following Nuget Packages installed:

  1. Microsoft.Owin.Host.SystemWeb
  2. Microsoft.Owin
  3. Microsoft ASP.NET Web API 2.2 OWIN
  4. Microsoft.Owin.Security
  5. Microsoft.Owin.Security.OAth
  6. Microsoft.Owin.Security.Cookies (optional)
  7. Microsoft ASP.NET Identity Owin
  8. OWIN

web-api-security-03
If I were to describe what OWIN Middleware is, I would say that middleware is a black box of specific services that an application requires. In other words, you setup an OWIN middleware by selecting individual services you want to use and the specific provider for those services. In our case, we will use OWIN to enable Bearer Token Authentication in our application. The key point to understand here, is that OWIN raises it’s own events when a request arrives. When those events are raised, all respective providers will also respond respectively. Is the Store applicaton we will plug an OAuthAuthorizationServerProvider which will be called from OWIN middleware everytime a user tries to create an access token. Assuming you have all the prementioned packages installed, add a Startup.cs file at the root of the Store application.

using Microsoft.Owin;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

[assembly: OwinStartup(typeof(Store.Startup))]

namespace Store
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureStoreAuthentication(app);
        }
    }
}

In the App_Start folder add another file named Startup.cs.

using DataAccess;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Store.Infrastructure;

namespace Store
{
    public partial class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }

        public void ConfigureStoreAuthentication(IAppBuilder app)
        {
            // User a single instance of StoreContext and AppStoreUserManager per request
            app.CreatePerOwinContext(StoreContext.Create);
            app.CreatePerOwinContext<AppStoreUserManager>(AppStoreUserManager.Create);

            // Configure the application for OAuth based flow
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(10),
                AllowInsecureHttp = true
            };

            app.UseOAuthBearerTokens(OAuthOptions);
        }
    }
}

We want our application to use Bearer Token Authentication so we set a relative provider (not implemented yet). Anyone wants to create an access token will have to post it’s credentials on /Token URI. Let’s continue by adding two required methods into the AppStoreUserManager class.

using DataAccess;
using Domain;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;

namespace Store.Infrastructure
{
    public class AppStoreUserManager : UserManager<AppStoreUser>
    {
        public AppStoreUserManager(IUserStore<AppStoreUser> store)
            : base(store)
        {
        }

        public static AppStoreUserManager Create(IdentityFactoryOptions<AppStoreUserManager> options, IOwinContext context)
        {
            var manager = new AppStoreUserManager(new UserStore<AppStoreUser>(context.Get<StoreContext>()));

            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<AppStoreUser>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };

            // Password Validations
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = true,
                RequireUppercase = true,
            };

            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider = new DataProtectorTokenProvider<AppStoreUser>(dataProtectionProvider.Create("ASP.NET Identity"));
            }

            return manager;
        }

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(AppStoreUser user, string authenticationType)
        {
            var userIdentity = await CreateIdentityAsync(user, authenticationType);

            return userIdentity;
        }
    }
}

When OWIN requires an instance of a AppStoreUserManager it will call the create method. Inside a Web API Controller this will be done like this:

Request.GetOwinContext().GetUserManager<AppStoreUserManager>();

OAuthAuthorizationServerProvider

We need to add an implementation class of the OAuthAuthorizationServerProvider which will be used to grand access to Web API resources (Action methods). Inside the infrastructure folder add the following ApplicationOAuthProvider class.

using Domain;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OAuth;
using Microsoft.AspNet.Identity.Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;

namespace Store.Infrastructure
{
    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        private readonly string _publicClientId;

        public ApplicationOAuthProvider(string publicClientId)
        {
            if (publicClientId == null)
            {
                throw new ArgumentNullException("publicClientId");
            }

            _publicClientId = publicClientId;
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<AppStoreUserManager>();

            AppStoreUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                context.SetError("invalid_grant", "Invalid username or password.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await userManager.GenerateUserIdentityAsync(user, OAuthDefaults.AuthenticationType);
            AuthenticationProperties properties = new AuthenticationProperties(); 
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
        }

        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            if (context.ClientId == null)
            {
                context.Validated();
            }

            return Task.FromResult<object>(null);
        }
    }
}

When a user tries to login, he ‘ll post his credentials to /Token URI (not to some Login action). OWIN middleware will raise an event and will call GrantResourceOwnerCredentials of the provider. If the credentials are valid then an access token will be created and added to the response. All requests trying to access a protected resource, must provide this generated access token. You can now understand that the provider is the glue between the OWIN Middleware and ASP.NET Identity.
web-api-security-04
One last but not least thing to do which is also related to setting up the security logic, is to inform Web API that we want to use only Bearer token authentication. If you miss that part nothing will work as expected. Open WebApiConfig class and modify as follow:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            // Web API routes
            config.MapHttpAttributeRoutes();

        }
    }

The HostAuthenticationFilter is responsible to authenticate users based on the access token found inside the request. If no token found then the user is considered as unauthenticated and cannot access protected resources.
Before moving to the AngularJS Level, let’s restrict access to the OrdersController Web API Controller by adding an Authorize at the class level.

 [Authorize]
    public class OrdersController : ApiController
    {

We will need an AccountController that users can use to register new accounts. Add it inside the Controllers folder.

[Authorize]
    [RoutePrefix("api/Account")]
    public class AccountController : ApiController
    {
        //private const string LocalLoginProvider = "Local";
        private AppStoreUserManager _userManager;

        public AccountController()
        {
        }

        public AccountController(AppStoreUserManager userManager,
            ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
        {
            UserManager = userManager;
            AccessTokenFormat = accessTokenFormat;
        }

        public AppStoreUserManager UserManager
        {
            get
            {
                return _userManager ?? Request.GetOwinContext().GetUserManager<AppStoreUserManager>();
            }
            private set
            {
                _userManager = value;
            }
        }

        public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }


        // POST api/Account/Register
        [AllowAnonymous]
        [Route("Register")]
        public async Task<IHttpActionResult> Register(RegistrationModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var user = new AppStoreUser() { UserName = model.Email, Email = model.Email };

            IdentityResult result = await UserManager.CreateAsync(user, model.Password);

            if (!result.Succeeded)
            {
                return GetErrorResult(result);
            }

            return Ok();
        }


        protected override void Dispose(bool disposing)
        {
            if (disposing && _userManager != null)
            {
                _userManager.Dispose();
                _userManager = null;
            }

            base.Dispose(disposing);
        }

        #region Helpers

        private IAuthenticationManager Authentication
        {
            get { return Request.GetOwinContext().Authentication; }
        }

        private IHttpActionResult GetErrorResult(IdentityResult result)
        {
            if (result == null)
            {
                return InternalServerError();
            }

            if (!result.Succeeded)
            {
                if (result.Errors != null)
                {
                    foreach (string error in result.Errors)
                    {
                        ModelState.AddModelError("", error);
                    }
                }

                if (ModelState.IsValid)
                {
                    // No ModelState errors are available to send, so just return an empty BadRequest.
                    return BadRequest();
                }

                return BadRequest(ModelState);
            }

            return null;
        }
        #endregion
    }

AngularJS

Things in this level will be much more easier to understand, I assure you. Let me remind you the interface where the user used to create gadget orders:
web-api-security-05
If you run your application and try to create an order, you will get an unauthorized error since we restricted the access to the orders controller. We need to change two things in the angularjs $scope.sendOrder functions, which resides inside the gadgetsStore.js file. Let me also remind you that this file also defines the root controller gadgetsStoreCtrl of our AngularJS application.

$scope.sendOrder = function (shippingDetails) {
	        var token = sessionStorage.getItem(tokenKey);
	        console.log(token);

	        var headers = {};
	        if (token) {
	            headers.Authorization = 'Bearer ' + token;
	        }

	        var order = angular.copy(shippingDetails);
	        order.gadgets = cart.getProducts();
	        $http.post(ordersUrl, order, { headers: { 'Authorization': 'Bearer ' + token } })
			.success(function (data, status, headers, config) {
			    $scope.data.OrderLocation = headers('Location');
			    $scope.data.OrderID = data.OrderID;
			    cart.getProducts().length = 0;
			    $scope.saveOrder();
			    $location.path("/complete");
			})
			.error(function (data, status, headers, config) {
			    if (status != 401)
			        $scope.data.orderError = data.Message;
			    else {
			        $location.path("/login");
			    }
			}).finally(function () {
			});
	    }

This is how we send a Bearer Authorization header in AngularJS. At this point we haven’t written any code that stores the token into the sessionStorage but at least we will be directed to another location. In this method we used tokenKey and tokenUrl constants so make sure you add them at the beggining of the same file as follow:

angular.module('gadgetsStore')
	.constant('gadgetsUrl', 'http://localhost:61691/api/gadgets')
	.constant('ordersUrl', 'http://localhost:61691/api/orders')
	.constant('categoriesUrl', 'http://localhost:61691/api/categories')
    .constant('tempOrdersUrl', 'http://localhost:61691/api/sessions/temporders')
    .constant('registerUrl', '/api/Account/Register')
    .constant('tokenUrl', '/Token')
    .constant('tokenKey', 'accessToken')
	.controller('gadgetStoreCtrl', function ($scope, $http, $location, gadgetsUrl, categoriesUrl, ordersUrl, tempOrdersUrl, cart, tokenKey) {

At the same file we need two more methods: A $scope.logout function which simply removes the access token and hence the user get unauthenticated and the $scope.createAccount which redirects the user to a register template.

$scope.logout = function () {
	        sessionStorage.removeItem(tokenKey);
	    }
$scope.createAccount = function () {
	        $location.path("/register");
	    }

Redirection to /login and /register locations won’t do anything if we don’t tell so. In the mainApp.js file add the highlighted lines.

angular.module("gadgetsStore", ["storeFilters", "storeCart", "ngRoute", "chieffancypants.loadingBar","ngAnimate"])
			.config(function ($routeProvider) {
			    $routeProvider.when("/gadgets", {
			        templateUrl: "app/views/gadgets.html"
			    });
			    $routeProvider.when("/checkout", {
			        templateUrl: "app/views/checkout.html"
			    });
			    $routeProvider.when("/submitorder", {
			        templateUrl: "app/views/submitOrder.html"
			    });
			    $routeProvider.when("/complete", {
			        templateUrl: "app/views/orderSubmitted.html"
			    });
			    $routeProvider.when("/login", {
			        templateUrl: "app/views/login.html"
			    });
			    $routeProvider.when("/register", {
			        templateUrl: "app/views/register.html"
			    });
			    $routeProvider.otherwise({
			        templateUrl: "app/views/gadgets.html"
			    });
			});

We certainly need to create the login.html and register.html template views but first, let’s create the accountController that both of them will make use of. Add the following javascript file inside the app/controllers foldler.

angular.module("gadgetsStore")
    .controller('accountController', function ($scope, $http, $location, registerUrl, tokenUrl, tokenKey) {

        $scope.hasLoginError = false;
        $scope.hasRegistrationError = false;

        // Registration
        $scope.register = function () {

            $scope.hasRegistrationError = false;
            $scope.result = '';

            var data = {
                Email: $scope.registerEmail,
                Password: $scope.registerPassword,
                ConfirmPassword: $scope.registerPassword2
            };

            $http.post(registerUrl, JSON.stringify(data))
                    .success(function (data, status, headers, config) {
                        $location.path("/login");
                    }).error(function (data, status, headers, config) {
                        $scope.hasRegistrationError = true;
                        var errorMessage = data.Message;
                        console.log(data);
                        $scope.registrationErrorDescription = errorMessage;

                        if (data.ModelState['model.Email'])
                            $scope.registrationErrorDescription += data.ModelState['model.Email'];

                        if (data.ModelState['model.Password'])
                            $scope.registrationErrorDescription += data.ModelState['model.Password'];

                        if (data.ModelState['model.ConfirmPassword'])
                            $scope.registrationErrorDescription += data.ModelState['model.ConfirmPassword'];

                        if (data.ModelState[''])
                            $scope.registrationErrorDescription +=  data.ModelState[''];

                    }).finally(function () {
                    });
        }

        $scope.login = function () {
            $scope.result = '';

            var loginData = {
                grant_type: 'password',
                username: $scope.loginEmail,
                password: $scope.loginPassword
            };

            $http({
                method: 'POST',
                url: tokenUrl,
                data: $.param(loginData),
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                }
            }).then(function (result) {
                console.log(result);
                $location.path("/submitorder");
                sessionStorage.setItem(tokenKey, result.data.access_token);
                $scope.hasLoginError = false;
                $scope.isAuthenticated = true;
            }, function (data, status, headers, config) {
                $scope.hasLoginError = true;
                $scope.loginErrorDescription = data.data.error_description;
            });

        }

    });

I have highlighted only the login method cause it’s the most important. Notice that login method posts user’s credentials to the token Uri. Also notice the type of the Content-Type and the jQuery convertion we applied to the data, in order to serialize the object and make it suitable for use for this type of request. Omit either the data convertion or the Content-Type and nothing will work. We haven’t used jQuery at all till now so make sure you add a reference into the Index.cshtml page as follow (change if you have different version):

<script src="../../Scripts/angular.js" type="text/javascript"></script>
    <script src="../../Scripts/angular-route.js" type="text/javascript"></script>
    <script src="~/Scripts/angular-animate.js"></script>
    <script src="~/Scripts/jquery-1.9.0.js"></script>
    <script src="~/Scripts/loading-bar.js"></script>
    <script src="~/app/mainApp.js"></script>
    <script src="../../app/controllers/gadgetsStore.js" type="text/javascript"></script>
    <script src="../../app/filters/storeFilters.js" type="text/javascript"></script>
    <script src="../../app/controllers/gadgetsControllers.js" type="text/javascript"></script>
    <script src="../../app/components/cartCmp.js" type="text/javascript"></script>
    <script src="../../app/controllers/checkoutController.js" type="text/javascript"></script>
    <script src="~/app/controllers/accountController.js"></script>

Following are the login.html and register.html template views.

<div class="container" style="margin-top:30px" ng-controller="accountController">

    <div class="col-md-12">
        <div class="modal-dialog" style="margin-bottom:0">
            <div class="modal-content">
                <div class="panel-heading">
                    <h3 class="panel-title">Sign In</h3>
                </div>
                <div class="panel-body">
                    <form role="form">
                        <fieldset>
                            <div class="form-group">
                                <input class="form-control" placeholder="E-mail" name="email" type="email" ng-model="loginEmail" autofocus="">
                            </div>
                            <div class="form-group">
                                <input class="form-control" placeholder="Password" name="password" type="password" ng-model="loginPassword" value="">
                            </div>
                            <div class="alert alert-warning" ng-show="hasLoginError">
                                <a href="#" class="alert-link" ng-bind="loginErrorDescription">
                                </a>
                            </div>
                            <a href="" ng-click="login()" class="btn btn-sm btn-success">Login</a>
                            <a href="" ng-click="createAccount()" class="btn btn-sm btn-default">Create account</a>
                        </fieldset>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="container" style="margin-top:30px" ng-controller="accountController">

    <div class="col-md-12">
        <div class="modal-dialog" style="margin-bottom:0">
            <div class="modal-content">
                <div class="panel-heading">
                    <h3 class="panel-title">Registration Form</h3>
                </div>
                <div class="panel-body">
                    <form role="form">
                        <fieldset>
                            <div class="form-group">
                                <input class="form-control" placeholder="E-mail" name="email" type="email" ng-model="registerEmail" autofocus="">
                            </div>
                            <div class="form-group">
                                <input class="form-control" placeholder="Password" name="password" type="password" ng-model="registerPassword" value="">
                            </div>
                            <div class="form-group">
                                <input class="form-control" placeholder="Confirm Password" name="confirmPassword" type="password" ng-model="registerPassword2" value="">
                            </div>
                            <div class="alert alert-warning" ng-show="hasRegistrationError">
                                <a href="#" class="alert-link" ng-bind="registrationErrorDescription">
                                </a>
                            </div>
                            <a href="" ng-click="register()" class="btn btn-sm btn-success">Create account</a>
                        </fieldset>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Open the cartDetails.html and add the highlighted line so that user can logout when is already signed in.

<style>
    .navbar-right {
        float: right !important;
        margin-right: 5px;
    }

    .navbar-text {
        margin-right: 10px;
    }
</style>
<div class="navbar-right">
    <div class="navbar-text">
        <b>Your cart:</b> {{itemCount()}} item(s), {{total() | currency}}
    </div>
    <a href="#/checkout" class="btn btn-danger navbar-btn">Checkout</a>
    <a href="" ng-show="isUserAuthenticated()" ng-click="logout()" class="btn btn-default navbar-btn">Logout</a>
</div>

Build and run your application. Try to create an order and you will be redirected to the login page. Click the register button to and reate an account. You will be redirected to login page. Enter your credentials and continue with your order. You can create orders till you press the logout button.
Click here to see it in action (you may required to refresh the page when the tab opens).

Conclusion

I am really happy that these Web API feat. AngularJS series have come to an end cause we had the opportunity to see a lot of features concerning both Web API and AngularJS. Now that we have a project like this, we can focus more on individual concepts such as Web API Security or AngularJS project structure best practices. There are so many things we added this time and I hope I have mentioned all of these in the post. If you have problems running your application you can always download the tested and updated version from here. Make sure you get subscribed and get notified for newer posts!

AngularJS Source Code updated

I have slightly updated AngularJS code as far as concerned the Ajax calls to Web API actions. In the github project, you will notice that I moved all the Ajax calls to two new AngularJS services, accountService and storeService. So, here’s for example the accountService:

angular.module("gadgetsStore")
    .service('accountService', function ($http, registerUrl, tokenUrl, tokenKey) {

        this.register = function (data) {
            var request = $http.post(registerUrl, data);

            return request;
        }

        this.generateAccessToken = function (loginData) {
            var requestToken = $http({
                method: 'POST',
                url: tokenUrl,
                data: $.param(loginData),
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                }
            });

            return requestToken;
        }

        this.isUserAuthenticated = function () {
            var token = sessionStorage.getItem(tokenKey);

            if (token)
                return true;
            else
                return false;
        }

        this.logout = function () {
            sessionStorage.removeItem(tokenKey);
        }

    });

Now the login button of the accountController changed in something like this:

// Generate Token - Login
        $scope.login = function () {
            $scope.result = '';

            var loginData = {
                grant_type: 'password',
                username: $scope.loginEmail,
                password: $scope.loginPassword
            };

            accountService.generateAccessToken(loginData)
                .success(successLoginCallback)
                    .error(errorLoginCallback);

        }

.. having the following success and error callbacks:

var successLoginCallback = function (result) {
            console.log(result);
            $location.path("/submitorder");
            sessionStorage.setItem(tokenKey, result.access_token);
            $scope.hasLoginError = false;
            $scope.isAuthenticated = true;
        }

var errorLoginCallback = function (data, status, headers, config) {
            console.log(data);
            $scope.hasLoginError = true;
            $scope.loginErrorDescription = data.error_description;
        }

I believe it’s much cleaner now.


ASP.NET Web API feat. OData

$
0
0

OData is an open standard protocol allowing the creation and consumption of queryable and interoperable RESTful APIs. It was initiated by Microsoft and it’s mostly known to .NET Developers from WCF Data Services. There are many other server platforms supporting OData services such as Node.js, PHP, Java and SQL Server Reporting Services. More over, Web API also supports OData and this post will show you how to integrate those two.

Theory

OData is known for it’s powerfull query capabilities. I am sure that most of you have come across an HTTP REST request such as the following:

http://www.website.com/odata/Employees?$top=10&$orderby=name

The above request will retrieve the top 10 employees from the database ordered by name. The key point to remember for the moment, is that the result set will be executed at the database and not at the service layer nor at the client side. This allows clients, to encode and execute complex query instructions directly to the database without increasing web server’s memory or CPU usage. OData follows the REST architectural style and is used to expose data over HTTP services in an elegant way. Not only queries but also CRUD operations are supported, something that we will certainly show later on this post. One of it’s great features is that OData exposes metadata to be used by code generation tools, in order to create proxies for consuming it’s services. You can read more about OData here.

There are two ways to use OData in a Web API project. The first and the simplest one, is this where you just want to enable OData query syntax for your Web API controllers. The second one is where you want to use all the OData package capabilities by declaring the OData available serialization formats, the resources and the availables actions on that resources you want to expose. Enough with the theory, let’s move to practice.
webapi-odata-13

Enable OData Query syntax to a Web API Controller

To start with, create an empty solution named ODataWebAPI and add a class library project named ODataWebAPI.Data. We ‘ll use this project to boostrap a database using Code First Entity Framework. Make sure you install Entity Framework and MockData packages using the Nuget Package Manager. We ‘ll use the latter to create multiple records when seeding the database for the first time. Add the following 3 entity classes:

public class Employee
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string Surname { get; set; }
        public string Email { get; set; }

        public int AddressID { get; set; }
        public virtual Address Address { get; set; }

        public int CompanyID { get; set; }
        public virtual Company Company { get; set; }
    }

 

public class Address
    {
        public int ID { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
        public string State { get; set; }
        public string ZipCode { get; set; }
    }

 

public class Company
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public virtual List<Employee> Employees { get; set; }

        public Company()
        {
            Employees = new List<Employee>();
        }
    }

Now add the EntityTypeConfigurations for the above classes:

class EmployeeConfiguration : EntityTypeConfiguration<Employee>
    {
        public EmployeeConfiguration()
        {
            ToTable("Employees");
            Property(e => e.CompanyID).IsRequired();
            Property(e => e.AddressID).IsRequired();
        }
    }

 

public class CompanyConfiguration: EntityTypeConfiguration<Company>
    {
        public CompanyConfiguration()
        {
            ToTable("Companies");
        }
    }

One employee may have one specific address and one company may have many employees. Time to add the DbContext required class.

public class EntitiesContext : DbContext
    {
        public EntitiesContext()
            : base("EntitiesContext")
        {
            // Do not forget to set the connection string in Web.config..
            Configuration.ProxyCreationEnabled = false;
        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Company> Companies { get; set; }
        public DbSet<Address> Addresses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new EmployeeConfiguration());
            modelBuilder.Configurations.Add(new CompanyConfiguration());
        }
    }

I chose on purpose to build a schema such as the previous, cause using the MockData package, it will be easy to seed the database with as many records as we wish. MockData supports fake data for properties such as First name, Surname, Email, Address etc.. Add the following initializer class, which create 10 companies and 200 employees. All employees will be assigned to one of the created companies.

public class EntitiesInitializer : DropCreateDatabaseIfModelChanges<EntitiesContext>
    {
        protected override void Seed(EntitiesContext context)
        {
            // Companies
            GetCompanies().ForEach(c => context.Companies.Add(c));

            // Addresses
            GetAddresses().ForEach(a => context.Addresses.Add(a));

            // Employees
            GetEmployees().ForEach(e => context.Employees.Add(e));

            base.Seed(context);
        }

        private static List<Company> GetCompanies()
        {
            List<Company> _companies = new List<Company>();

            for (int i = 1; i <= 10; i++)
            {
                _companies.Add(new Company()
                    {
                        ID = i,
                        Name = MockData.Company.Name()
                    });
            }

            return _companies;
        }

        private static List<Address> GetAddresses()
        {
            List<Address> _addresses = new List<Address>();

            for (int i = 1; i <= 200; i++)
            {
                _addresses.Add(new Address()
                {
                    ID = i,
                    City = MockData.Address.City(),
                    Country = MockData.Address.Country(),
                    State = MockData.Address.State(),
                    ZipCode = MockData.Address.ZipCode()
                });
            }

            return _addresses;
        }

        private static List<Employee> GetEmployees()
        {
            List<Employee> _employees = new List<Employee>();

            for (int i = 1; i <= 200; i++)
            {
                _employees.Add(new Employee()
                {
                    ID = i,
                    FirstName = MockData.Person.FirstName(),
                    Surname = MockData.Person.Surname(),
                    AddressID = i,
                    CompanyID = new Random().Next(1, 10),
                    Email = MockData.Internet.Email(),
                });
            }

            return _employees;
        }
    }

Web API

Add a new Empty Web Application project named ODataWebAPI.API selecting the Web API template. For start add a reference to the ODataWebAPI.Data project and a connection string the EntitiesContext we created before. Alter it if required to reflect your development environment (make sure it takes one line only).

  <connectionStrings>
  <add name="EntitiesContext" providerName="System.Data.SqlClient" connectionString="Server=(localdb)\v11.0; Database=CompanyDB; Trusted_Connection=true; MultipleActiveResultSets=true" />
</connectionStrings>

Install Entity Framework from nuget packages and set the Database initializer at the Application Start event.

protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);

            // Init the database
            Database.SetInitializer(new EntitiesInitializer());
        }

Supposing you wanted to expose your Employees through an API Controller you would create an API Controller as follow:

public class EmployeesController : ApiController
    {
        private EntitiesContext _dbContext;

        public EmployeesController()
        {
            this._dbContext = new EntitiesContext();
        }

        public IEnumerable<Employee> GetEmployees()
        {
            try
            {
                return _dbContext.Employees.ToArray();
            }
            catch(Exception ex)
            {
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            _dbContext.Dispose();
        }
    }

Build, run your application and request all employees as you would normally have done for a common Web API Controller:

http://localhost:your_port/api/employees

Ensure that seed data have been successfully deployed to database.
webapi-odata-01
Now, if you try to make an OData request such as the following, the query will fail and bring the same data again.

http://localhost:46184/api/employees?$top=5

It only takes two steps to enable OData syntax querying for the specific Web API Controller:

  • Make action return IQuerable instead of IEnumerable
  • Apply Queryable attribute in the action

Let’s apply the above changes to the GetEmployees action:

[Queryable]
public IQueryable<Employee> GetEmployees()
  {
      return _dbContext.Employees;
  }

Now again, fire the application and request top 5 employess using OData syntax querying. Now try to order those 5 employees by Surname:

http://localhost:46184/api/employees?$top=5&$orderby=Surname

Check you employees data, find a specific one and try to locate the record using a filter operator.

http://localhost:46184/api/employees?$filter=Surname eq 'McLaughlin'

Ensure that both of the two OData queries work. But wait a minute. At the service side we only return _dbContext.Employees so how is this possible anyway? The answer is Deferred execution. Before start explaining the concept of deferred execution, open SQL Profiler and start a trace for the server where the CompanyDB database exists. Then put a breakpoint in the EmployeesController at the point where we return all employees and request top 5 employees as we did before. Notice that even when you pass this breakpoint, nothing has been executed yet.
webapi-odata-02
When you hit F5 and before the response is returned to client, you can see the query executed in database:

exec sp_executesql N'SELECT TOP (@p__linq__0)
    [Extent1].[ID] AS [ID],
    [Extent1].[FirstName] AS [FirstName],
    [Extent1].[Surname] AS [Surname],
    [Extent1].[Email] AS [Email],
    [Extent1].[AddressID] AS [AddressID],
    [Extent1].[CompanyID] AS [CompanyID]
    FROM [dbo].[Employees] AS [Extent1]
    ORDER BY [Extent1].[ID] ASC',N'@p__linq__0 int',@p__linq__0=5

So what is actually happening here, is that 2 different expression trees were merged at the Web API stack level before actually transformed by Entity Framework to SQL query and being executed at database.
webapi-odata-03
To complete the Enable OData Query syntax to a Web API Controller section, let’s see some of the basic operations enabled by applying the enabling OData syntax quering to our Web API Controller.

  • $filter: LINQ Where equivalent expression [/api/employees?$filter=Surname eq ‘Rice’]
  • $orderby: LINQ OrderBy equivalent expreesion [/api/employees?$top=5&orderby=Surname]
  • $top: LINQ Take equivalent expreesion [/api/employees?$top=5]
  • $skip: LINQ Skip equivalent expreesion [/api/employees?$top=5&$skip=10]
  • $select: LINQ Select equivalent expreesion [api/employees?$select=FirstName,Surname,Email]

Enable OData formats and Entity Data Models

If OData Query syntax is not enough for you and you want more complex operations through OData services such as accessing all employees assigned to a specific company or supporting CRUD operations, then you need to take a step further than we did before, and unlock the full OData capabilities by enabling OData formatting and Entity Data Models as well. First thing you need to do, is install Microsoft.AspNet.WebApi.OData package in to your API project.
webapi-odata-04
Next thing to do, is create a route for OData services by passing an Entity Data Model as one of it’s arguments. An Entity Data Model is a definition of your data you want to expose over the wire, the resources (EntitySets), the relationships between them (AssociationSets) and the available actions than can be invoked for those resources using OData services. Switch to the WebApiConfig.cs file and paste the following code:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapODataRoute("ODataRoute", "odata", GenerateEntityDataModel());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

        private static IEdmModel GenerateEntityDataModel()
        {
            ODataModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Employee>("Employees");
            builder.EntitySet<Address>("Addresses");
            builder.EntitySet<Company>("Companies");

            return builder.GetEdmModel();
        }
    }

We used the MapODataRoute extension method to register an OData route with a prefix odata, passing an Entity Data Model where we registered all of our Entities. You will not be able to use all OData capabilities through the default Web API Controller. Instead you need to create a controller that inherits EntitySetController<T,K> where T is the entity class you are exposing (e.g. Employee) and K is entity’s class primary key type (e.g. int). EntitySetController is an abstract class that derives from ODataController which gives you all the required functionality for OData services.

// Summary:
    //     Provides a convenient starting point for a controller that exposes an OData
    //     entity set. This is the synchronous version of System.Web.Http.OData.AsyncEntitySetController<TEntity,TKey>.

    [CLSCompliant(false)]
    [ODataNullValue]
    public abstract class EntitySetController<TEntity, TKey> : ODataController where TEntity : class
    {
        protected EntitySetController();

        public ODataPath ODataPath { get; }

        public ODataQueryOptions<TEntity> QueryOptions { get; }

        protected internal virtual TEntity CreateEntity(TEntity entity);

        public virtual void CreateLink(TKey key, string navigationProperty, Uri link);

        public virtual void Delete(TKey key);

        public virtual void DeleteLink(TKey key, string relatedKey, string navigationProperty);

        public virtual void DeleteLink(TKey key, string navigationProperty, Uri link);

        public virtual IQueryable<TEntity> Get();

        public virtual HttpResponseMessage Get(TKey key);

        protected internal virtual TEntity GetEntityByKey(TKey key);

        protected internal virtual TKey GetKey(TEntity entity);

        public virtual HttpResponseMessage HandleUnmappedRequest(ODataPath odataPath);

        public virtual HttpResponseMessage Patch(TKey key, Delta<TEntity> patch);

        protected internal virtual TEntity PatchEntity(TKey key, Delta<TEntity> patch);

        public virtual HttpResponseMessage Post(TEntity entity);

        public virtual HttpResponseMessage Put(TKey key, TEntity update);

        protected internal virtual TEntity UpdateEntity(TKey key, TEntity update);
    }

I have commented out the EmployeesController we created before and created the following implementation.

public class EmployeesController : EntitySetController<Employee, int>
    {
        private EntitiesContext _dbContext;

        public EmployeesController()
        {
            this._dbContext = new EntitiesContext();
        }

        [Queryable]
        public override IQueryable<Employee> Get()
        {
            return _dbContext.Employees;
        }

        protected override Employee GetEntityByKey(int key)
        {
            return _dbContext.Employees.Where(e => e.ID == key).FirstOrDefault();
        }

        // /odata/Employees(1)/Address
        public Address GetAddressFromEmployee(int key)
        {
            return _dbContext.Employees.Where(e => e.ID == key)
                .Select(e => e.Address).FirstOrDefault();
        }

        protected override Employee CreateEntity(Employee entity)
        {
            _dbContext.Employees.Add(entity);
            _dbContext.SaveChanges();
            return entity;
        }

        protected override Employee UpdateEntity(int key, Employee update)
        {
            if (!_dbContext.Employees.Any(e => e.ID == key))
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                    HttpStatusCode.NotFound,
                    new ODataError
                    {
                        ErrorCode = "NotFound.",
                        Message = "Employee " + key + " not found."
                    }));
            }

            update.ID = key;

            _dbContext.Employees.Attach(update);
            _dbContext.Entry(update).State = System.Data.Entity.EntityState.Modified;
            _dbContext.SaveChanges();
            return update;
        }

        protected override Employee PatchEntity(int key, Delta<Employee> patch)
        {
            var employee = _dbContext.Employees.FirstOrDefault(e => e.ID == key);
            if (employee == null)
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                    HttpStatusCode.NotFound,
                    new ODataError
                    {
                        ErrorCode = "NotFound.",
                        Message = "Employee " + key + " not found."
                    }));

            patch.Patch(employee);
            _dbContext.SaveChanges();

            return employee;
        }

        public override void Delete(int key)
        {
            var employee = _dbContext.Employees.Where(a => a.ID == key).FirstOrDefault();
            if (employee != null)
            {
                _dbContext.Employees.Remove(employee);
                _dbContext.SaveChanges();
            }
            else
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                    HttpStatusCode.NotFound,
                    new ODataError
                    {
                        ErrorCode = "NotFound.",
                        Message = "Employee " + key + " not found."
                    }));
        }

        // The base POST method will call to form the location header
        protected override int GetKey(Employee entity)
        {
            return entity.ID;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            _dbContext.Dispose();
        }
    }

Let’s explain one by one all the actions we added on this EntitySetController controller. Since we wanted to expose Employee data through this controller, we made it of type EntitySetController<Employee,int> I believe the Get method is self explanatory. You still need to apply the Querable attribute if you want to query Employees using the OData query syntax. We have overriden the GetEntityByKey(int key) method, so we can access individual employees. Check the example bellow:
webapi-odata-05
You can now access an individual employee by sending a request in the following format:

http://localhost:46184/odata/Employees(id)

If you use Fiddler you will also notice some metadata to be used by code generation tools. In the preceding example I put application/json as the accept header but this isn’t the only format OData supports. You can also request application/atom+xml:
webapi-odata-06
Let’s move forward and check the GetAddressFromEmployee(int key) which guess what, it fetches a specific employee’s address. Notice that this method isn’t an override but we added by ourselves in order to support a linking between an employee and it’s address, through OData querying. More over, there is a name convention you must follow for this kind of actions, that is first start with Get which declares that is an HTTP GET operation, followed by what exactly you want to bring, that is an employee Address, followed by a From and the controller’s entity Employee. Note that there must be a relation between an employee and an address otherwise nothing will happen. The following request will retrieve the address for the employee with id=55:

/odata/Employees(55)/Address

CreateEntity(Employee entity),UpdateEntity(int key, Employee update) and PatchEntity(int key, Delta patch) methods, work almost in the same way. Notice that there is no prefix such as POST, PUT or PATCH for all these methods which means that the base class has already those methods implemented for you. This means, that if you make a POST request to create an employee, the base POST method will capture your request and it will dispatch it to the CreateEntity(Employee entity). Before returning the result to the client it will call the GetKey(Employee entity) in order to get the created employee’s id and encapsulate it inside the response Location header. Update and Patch methods works quite similar. Let’s see a POST request in action:
webapi-odata-09
webapi-odata-07
webapi-odata-08
webapi-odata-10
If you want to delete a specific employee, simply send a DELETE HTTP request such as the following:

/odata/Employees(203)

Clients

I couldn’t resist not creating an AngularJS client and that’s because AngularJS makes accessing OData services a piece of cake. I won’t paste all the code on this post, since you can always download this solution from my github account. You will find that I have created two other EntitySetControllers, one for Companies and another for Addresses. For example, you will find that I created the following method in the CompaniesController so that you can access a company’s employees:

// Uses name convention
        // /odata/Companies(1)/Employees
        [Queryable]
        public IQueryable<Employee> GetEmployeesFromCompany([FromODataUri] int key)
        {
            return _dbContext.Employees.Where(e => e.CompanyID == key);
        }

More over, just to show you how extensible OData protocol can be using the Web API, I have created a custom action for CompaniesController, so that you can add an employee directly to a specific company. I defined the custom action in the Entity Data Model..

private static IEdmModel GenerateEntityDataModel()
        {
            ODataModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Employee>("Employees");
            builder.EntitySet<Address>("Addresses");
            var companies = builder.EntitySet<Company>("Companies");

            // Define a custom action on Companies
            var addCompanyEmployeeByIdAction = builder.Entity<Company>().Action("AddCompanyEmployee");
            addCompanyEmployeeByIdAction.Parameter<string>("name");
            addCompanyEmployeeByIdAction.Parameter<string>("surname");
            addCompanyEmployeeByIdAction.Parameter<string>("email");
            addCompanyEmployeeByIdAction.Parameter<int>("address");
            addCompanyEmployeeByIdAction.ReturnsFromEntitySet<Employee>("Employees");

            return builder.GetEdmModel();
        }

And the respective action in the Companies controller:

[HttpPost]
        public Employee AddCompanyEmployee([FromODataUri] int key, ODataActionParameters actionParams)
        {
            Employee employee = null;
            try
            {
                if (actionParams["name"] != null && actionParams["surname"] != null && actionParams["email"] != null && actionParams["address"] != null)
                {
                    string employeeName = actionParams["name"].ToString();
                    string employeeSurname = actionParams["surname"].ToString();
                    string employeeEmail = actionParams["email"].ToString();
                    int employeeAddress = Int32.Parse(actionParams["address"].ToString());

                    employee = new Employee()
                    {
                        FirstName = employeeName,
                        Surname = employeeSurname,
                        Email = employeeEmail,
                        AddressID = employeeAddress,
                        CompanyID = key
                    };

                    _dbContext.Employees.Add(employee);
                    _dbContext.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                    HttpStatusCode.NotFound,
                    new ODataError
                    {
                        ErrorCode = "Error",
                        Message = ex.Message
                    }));
            }

            return employee;
        }

Now if you wanted to invoke that method, you should sent a POST request to a URL such as this:

http://localhost:46184/odata/Companies(id)/AddCompanyEmployee

webapi-odata-12
From AngularJS pespective, let us see the $resource service first and then the controller.

angular.module('mainApp')
    .factory('employeeService', function ($resource) {
        var odataUrl = '/odata/Employees';
        return $resource('', {},
            {
                'getAll': { method: 'GET', url: odataUrl },
                'getTop10': { method: 'GET', url: odataUrl + '?$top=10' },
                'create': { method: 'POST', url: odataUrl },
                'patch': { method: 'PATCH', params: { key: '@key' }, url: odataUrl + '(:key)' },
                'getEmployee': { method: 'GET', params: { key: '@key' }, url: odataUrl + '(:key)' },
                'getEmployeeAdderss': { method: 'GET', params: { key: '@key' }, url: odataUrl + '(:key)' + '/Address' },
                'deleteEmployee': { method: 'DELETE', params: { key: '@key' }, url: odataUrl + '(:key)' }
            });
    })

 

angular.module('mainApp')
    .controller('appCtrl', function ($scope, employeeService, notificationFactory) {

        $scope.currentEmployee = {};

        // Get Top 10 Employees
        $scope.getTop10Employees = function () {
            (new employeeService()).$getTop10()
                .then(function (data) {
                    $scope.employees = data.value;
                    $scope.currentEmployee = $scope.employees[0];
                    $scope.setCurrentEmployeeAddress();
                    notificationFactory.success('Employeess loaded.');
                });
        };

        // Set active employee for patch update
        $scope.setEmployee = function (employee) {
            $scope.currentEmployee = employee;
            $scope.setCurrentEmployeeAddress();
        };

        $scope.setCurrentEmployeeAddress = function () {
            var currentEmployee = $scope.currentEmployee;

            return (new employeeService({
                "ID": currentEmployee.ID,
            })).$getEmployeeAdderss({ key: currentEmployee.ID })
            .then(function (data) {
                $scope.currentEmployee.City = data.City;
                $scope.currentEmployee.Country = data.Country;
                $scope.currentEmployee.State = data.State;
            });
        }

        // Update Selected Employee
        $scope.updateEmployee = function () {
            var currentEmployee = $scope.currentEmployee;
            console.log(currentEmployee.Email);
            if (currentEmployee) {
                return (new employeeService({
                    "ID": currentEmployee.ID,
                    "FirstName": currentEmployee.FirstName,
                    "Surname": currentEmployee.Surname,
                    "Email": currentEmployee.Email
                })).$patch({ key: currentEmployee.ID })
                .then(function (data) {
                    notificationFactory.success('Employee with ID ' + currentEmployee.ID + ' updated.')
                });
            }
        }

        $scope.deleteEmployee = function () {
            var currentEmployee = $scope.currentEmployee;

            return (new employeeService({
                "ID": currentEmployee.ID,
            })).$deleteEmployee({ key: currentEmployee.ID })
            .then(function (data) {
                notificationFactory.success('Employee with ID ' + currentEmployee.ID + ' removed.');
                $scope.getTop10Employees();
            });
        }
    });

When you run the application, the top 10 employees will be loaded on the left side. Selecting an employee from the table, will fill the form on the right side, where you can see employee’s information (name, surname, address, company etc.). You can PATCH update employee’s information or delete one.
webapi-odata-11
That’s it, we are done showing how to use OData with ASP.NET Web API. There are much more you can do with OData and that’s why I recommended you to navigate and study it’s official website. I hope you enjoyed the post. You can download the project we built from here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

BrowseDotNET: Manage your .NET Solutions and code snippets

$
0
0

BrowseDotNET is an open source ASP.NET MVC Web Application I created, that can help you manage your computer’s .NET solutions and code samples (programming language indepented) in one place. What does it solve? It’s common sense for developers to have dosens or hundrends of solutions projects in their personal computers with each of them providing a single solution to a specific problem. The thing is, that when the time comes to solve the same problem it’s quite difficult to find the respective solution among dosens and hence you are forced to seek over and over again the same solution on the internet. More over, you might have noticed that there are code snippets which you often use in your applications, but still you always have to get them from the internet.

BrowseDotNET features

BrowseDotNET came to solve these two important problems by providing three main features:
browsedotnet-01
This post’s objective is to show how to host and use BrowseDotNET in your personal computer. First let’s see the technologies, frameworks and techniques used for this application.

  1. ASP.NET MVC 5
  2. Entity Framework Code First development
  3. Code First Migrations
  4. Generic Repository pattern
  5. Dependency Injection using Autofac framework
  6. ASP.NET Identity
  7. Automapper
  8. FluentValidation API

Installation

Download BrowseDotNET source code from my Github account. You will find link at the bottom of this post. Build the application to restore packages. Rebuild. Open BrowseDotNET.Data and BrowseDotNET.Web and change the connection strings in their configuration files to reflect your development environment. Next, Open Package Manager Console and enter the following command while having BrowseDotNET.Data as the default project.

update-database -verbose

browsedotnet-02
That’s all you need to start BrowseDotNET. Fire the application and you will reach the starting page.
browsedotnet-03
First thing you need to do is create an account. You won’t be able to register solutions and code snippets unless you are an authorized user.
browsedotnet-04

Register single .NET Solution

From the top menu, select Register/Solution and fill all the relative fields. You will be asked to give information for solution’s to be registered file path, name and description and of course search term keys. On the right side, you will always have some hints to help you register either a solution or a code snippet.
browsedotnet-05

Open, Edit or Remove a registered solution

By the time you register a .NET solution, you will be able to open it from BrowseDotNET. From the top menu, select Open/Solution and view all your registered solutions. Of course you can filter your solution by typing into the table’s textbox.
browsedotnet-06

Multiple .NET Solutions registration (scan)

We all have more than one solution in our pc so I decided to create a scanning process to automate registration for you. All you have to do is enter a root directory and then check it’s subdirectories you want to be scanned for .net solutions. BrowseDotNET will search in these folders for .sln files and register them automatically. It will use solution’s file name as solution name and description but you can always edit them later. To start, select from the top menu Register/Scan Solutions and enter a valid root directory path.
browsedotnet-07
If the directory path is valid, you will be redirected to check child folders you want to be scanned.
browsedotnet-08
BrowseDotNET will do it’s magic and when finished it will show you registered solutions page.
browsedotnet-09

Code snippet registration

Select Register/Code Snippet from the top menu and fill the relative code snippet’s information. There are two tabs, one for snippet’s information and another to paste the code. You can paste code of any language you wish. One thing I want to point is the field Group. Use the same group name to group many code snippets together. For example you can register a C# code snippet and a jQuery code snippet under the same group. Later, you will be able to view them together by selecting this group. This is why I have used an autocomplete textbox for this field. It will help you find group names you have used before.
browsedotnet-10
As soon as you register the snippet you will be able to view it whenever you want. View all of your snippets by selecting Open/Snippets from the top menu.
browsedotnet-11
On the left menu you can select settings and set the available Solution Types for your solutions and the available programming languages for you Code Snippets.
browsedotnet-12
Type key terms on the top menu and search .NET Solutions or Code snippets by keys. You will be able to open directly the matched solutions and view the source of your snippets. You can type multiple key terms by comma separating them.
browsedotnet-13
Keys are really important to the search engine so try to be carefull when registering solutions or code snippets. For example if you register a .NET solution with a key mvc and use the same key for registering a code snippet, searching with that term will fetch you both results. Instead, if you use the key term mvc snippet for the snippet registration, BrowseDotNET would fetch again both results but the inner search engine will use two different key records from the database while only one record used on the first case.
browsedotnet-17
As soon as you start registering and opening .NET solutions from BrowseDotNET you will find the most recently used solutions on the first page.
browsedotnet-14

Hosting

You could host BrowseDotNET in your local IIS but unfortunately you cannot start a GUI application from IIS (unless you make serious configurations). What you can do is create a simple .bat file with the following command.

"c:\Program Files\IIS Express\iisexpress.exe" /path:"C:\Path_To_BrowseDotNET\BrowseDotNet.Web" /port:20185

The above command will host BrowseDotNET in iisexpress without running Visual Studio.

Source Code

There is a lot of source code to study in BrowseDotNET and I totally recommend you read ASP.NET MVC Solution Architecture – Best Practices and Web API powerful Custom Model Validation with FluentValidation in case you aren’t familiar with those concepts. FluentValidation is used particularly for validating ViewModels. Let’s see the example for validating the FilePath in SolutiongViewModel, the model which is posted back to server when the user registers a solution.

public class SolutionViewModel : IValidatableObject
    {
        public int ID { get; set; }

        [MaxLength(50)]
        public string Author { get; set; }

        [Required]
        [MaxLength(100)]
        [Display(Name = "Application")]
        public string Name { get; set; }

        [Required]
        [MaxLength(250)]
        [Display(Name = "Full Path (.sln)")]
        [RegularExpression(@"^(?:[\w]\:|\\)(\\[a-zA-Z_\-\s0-9\.]+)+\.(sln)$", ErrorMessage = "Invalid file path format.")]
        public string FilePath { get; set; }

        [Required]
        [MaxLength(300)]
        [DataType(DataType.MultilineText)]
        public string Description { get; set; }

        [MaxLength(200)]
        [DataType(DataType.Url)]
        public string Website { get; set; }

        [Required]
        [Display(Name = "Type")]
        public int SolutionTypeID { get; set; }

        [Display(Name = "Type")]
        public string SolutionTypeType { get; set; }

        [Required]
        [MaxLength(200)]
        [DataType(DataType.MultilineText)]
        public string Keys { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new SolutionViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }

And it’s Validator..

public class SolutionViewModelValidator : AbstractValidator<SolutionViewModel>
    {
        public SolutionViewModelValidator()
        {
            RuleFor(s => s.FilePath)
                .Must(IsValidPath)
                .WithMessage("Enter solution's full file path.");
        }

        private bool IsValidPath(string filePath)
        {
            bool _isValidSolutionPath = new bool();

            try
            {

                if (File.Exists(filePath) && Path.GetExtension(filePath).EndsWith(".sln"))
                    _isValidSolutionPath = true;
            }
            catch
            {
                _isValidSolutionPath = false;
            }

            return _isValidSolutionPath;
        }
    }

Data annotations used for SolutionViewModel in conjuction with jQuery Validation will prevent the user post a string that doesn’t represent a file path ending in .sln.
browsedotnet-15
But even if he does enter a file path that ends with .sln, the ModelState.IsValid will return false if the path posted doesn’t exist in user’s pc. That’s the magic of FluentValidation.
browsedotnet-16

Conclusion

BrowseDotNET is certainly a powerfull tool for .NET developers. The more solutions you have on your pc and the more code snippets you want to easily access, the most you will benefit from BrowseDotNET. Download the source code from here. I’ m certain that you ‘ll enjoy working with it. Happy coding!

Update

Some folks asked me to add a Run as admin button cause they needed to open Visual Studio solutions with administrator rights, so I added that to.
browsedotnet-18


ASP.NET Web API Unit Testing

$
0
0

Unit testing can be beneficial to many aspects in software develepment, from the lowest level that is the source code to the highest level and the end user’s experience. Writing automated tests helps finding defects earlier in the development lifecycle process which leads to fewer late nights or weekend work (happier developers). Since defects are resolved before production, less defects reach end users (happier clients). It also increases reliability of source code, since if the base code doesn’t change all tests should always return the same results. Last but not least, anyone that decides to write unit tests is also forced to write testable code which leads to better software development practices.
web-api-unit-testing

Web API Unit Testing

ASP.NET Web API stack has many aspects that firstly must be well understood before writing unit tests against it and that’s what makes it difficult. This post is a full stack Web API Unit testing tutorial which means will show you how to unit test all the layers and components exist in your Web API application. Let’s see what we are gonna see on this post:

I will break the post in two main sections. The first one will be the one where we ‘re gonna structure the application and the second one will be the actual Unit testing. For the first one I will follow the Generic repository pattern which I have already describe in this post. If you feel familiar with those concepts and you just want to read about how the unit testing is done, you can skip this step. Mind though that part of this section will be the Controller registration of a referenced library which has an important role in our Unit testing.

Section One: Structuring the Web API Application

Create a new blank solution named UnitTestingWebAPI and add the following projects:

  1. UnitTestingWebAPI.Domain: Class library (Contains Entity Models)
  2. UnitTestingWebAPI.Data: Class library (Contains Repositories)
  3. UnitTestingWebAPI.Services: Class library (Contains Services)
  4. UnitTestingWebAPI.API.Core: Class library (Contains Web API components such as Controllers, Filters, Message Handlers)
  5. UnitTestingWebAPI.API: Empty ASP.NET Web Application (Web application to host Web API)
  6. UnitTestingWebAPI.Tests: Class library (Contains the Unit Tests)

Switch to UnitTestingWebAPI.Domain and add the following classes:

public class Article
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Contents { get; set; }
        public string Author { get; set; }
        public string URL { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateEdited { get; set; }

        public int BlogID { get; set; }
        public virtual Blog Blog { get; set; }

        public Article()
        {
        }
    }
public class Blog
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string URL { get; set; }
        public string Owner { get; set; }
        public DateTime DateCreated { get; set; }
        public virtual ICollection<Article> Articles { get; set; }

        public Blog()
        {
            Articles = new HashSet<Article>();
        }
    }

Repository Layer

Switch to UnitTestingWebAPI.Data, install Entity Framework from Nuget packages, add a reference to UnitTestingWebAPI.Data and add the following classes (create the respective folder if required):

public class ArticleConfiguration : EntityTypeConfiguration<Article>
    {
        public ArticleConfiguration()
        {
            ToTable("Article");
            Property(a => a.Title).IsRequired().HasMaxLength(100);
            Property(a => a.Contents).IsRequired();
            Property(a => a.Author).IsRequired().HasMaxLength(50);
            Property(a => a.URL).IsRequired().HasMaxLength(200);
            Property(a => a.DateCreated).HasColumnType("datetime2");
            Property(a => a.DateEdited).HasColumnType("datetime2");
        }
    }
public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            ToTable("Blog");
            Property(b => b.Name).IsRequired().HasMaxLength(100);
            Property(b => b.URL).IsRequired().HasMaxLength(200);
            Property(b => b.Owner).IsRequired().HasMaxLength(50);
            Property(b => b.DateCreated).HasColumnType("datetime2");
        }
    }
public class BloggerEntities : DbContext
    {
        public BloggerEntities()
            : base("BloggerEntities")
        {
            Configuration.ProxyCreationEnabled = false;
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Article> Articles { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }
public class BloggerInitializer : DropCreateDatabaseIfModelChanges<BloggerEntities>
    {
        protected override void Seed(BloggerEntities context)
        {
            GetBlogs().ForEach(b => context.Blogs.Add(b));

            context.Commit();
        }

        public static List<Blog> GetBlogs()
        {
            List<Blog> _blogs = new List<Blog>();

            // Add two Blogs
            Blog _chsakellsBlog = new Blog()
            {
                Name = "chsakell's Blog",
                URL = "http://chsakell.com/",
                Owner = "Chris Sakellarios",
                Articles = GetChsakellsArticles()
            };

            Blog _dotNetCodeGeeks = new Blog()
            {
                Name = "DotNETCodeGeeks",
                URL = "dotnetcodegeeks",
                Owner = ".NET Code Geeks",
                Articles = GetDotNETGeeksArticles()
            };

            _blogs.Add(_chsakellsBlog);
            _blogs.Add(_dotNetCodeGeeks);

            return _blogs;
        }

        public static List<Article> GetChsakellsArticles()
        {
            List<Article> _articles = new List<Article>();

            Article _oData = new Article()
            {
                Author = "Chris S.",
                Title = "ASP.NET Web API feat. OData",
                URL = "http://chsakell.com/2015/04/04/asp-net-web-api-feat-odata/",
                Contents = @"OData is an open standard protocol allowing the creation and consumption of queryable 
                            and interoperable RESTful APIs. It was initiated by Microsoft and it’s mostly known to
                            .NET Developers from WCF Data Services. There are many other server platforms supporting
                            OData services such as Node.js, PHP, Java and SQL Server Reporting Services. More over, 
                            Web API also supports OData and this post will show you how to integrate those two.."
            };

            Article _wcfCustomSecurity= new Article()
            {
                Author = "Chris S.",
                Title = "Secure WCF Services with custom encrypted tokens",
                URL = "http://chsakell.com/2014/12/13/secure-wcf-services-with-custom-encrypted-tokens/",
                Contents = @"Windows Communication Foundation framework comes with a lot of options out of the box, 
                            concerning the security logic you will apply to your services. Different bindings can be
                            used for certain kind and levels of security. Even the BasicHttpBinding binding supports
                            some types of security. There are some times though where you cannot or don’t want to use
                            WCF security available options and hence, you need to develop your own authentication logic
                            accoarding to your business needs."
            };

            _articles.Add(_oData);
            _articles.Add(_wcfCustomSecurity);

            return _articles;
        }

        public static List<Article> GetDotNETGeeksArticles()
        {
            List<Article> _articles = new List<Article>();

            Article _angularFeatWebAPI = new Article()
            {
                Author = "Gordon Beeming",
                Title = "AngularJS feat. Web API",
                URL = "http://www.dotnetcodegeeks.com/2015/05/angularjs-feat-web-api.html",
                Contents = @"Developing Web applications using AngularJS and Web API can be quite amuzing. You can pick 
                            this architecture in case you have in mind a web application with limitted page refreshes or
                            post backs to the server while each application’s View is based on partial data retrieved from it."
            };

            _articles.Add(_angularFeatWebAPI);

            return _articles;
        }

        public static List<Article> GetAllArticles()
        {
            List<Article> _articles = new List<Article>();
            _articles.AddRange(GetChsakellsArticles());
            _articles.AddRange(GetDotNETGeeksArticles());

            return _articles;
        }
    }
public class Disposable : IDisposable
    {
        private bool isDisposed;

        ~Disposable()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if (!isDisposed && disposing)
            {
                DisposeCore();
            }

            isDisposed = true;
        }

        // Ovveride this to dispose custom objects
        protected virtual void DisposeCore()
        {
        }
    }
public interface IDbFactory : IDisposable
    {
        BloggerEntities Init();
    }
public class DbFactory : Disposable, IDbFactory
    {
        BloggerEntities dbContext;

        public BloggerEntities Init()
        {
            return dbContext ?? (dbContext = new BloggerEntities());
        }

        protected override void DisposeCore()
        {
            if (dbContext != null)
                dbContext.Dispose();
        }
    }
public interface IRepository<T> where T : class
    {
        // Marks an entity as new
        void Add(T entity);
        // Marks an entity as modified
        void Update(T entity);
        // Marks an entity to be removed
        void Delete(T entity);
        void Delete(Expression<Func<T, bool>> where);
        // Get an entity by int id
        T GetById(int id);
        // Get an entity using delegate
        T Get(Expression<Func<T, bool>> where);
        // Gets all entities of type T
        IEnumerable<T> GetAll();
        // Gets entities using delegate
        IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
    }
public abstract class RepositoryBase<T> where T : class
    {
        #region Properties
        private BloggerEntities dataContext;
        private readonly IDbSet<T> dbSet;

        protected IDbFactory DbFactory
        {
            get;
            private set;
        }

        protected BloggerEntities DbContext
        {
            get { return dataContext ?? (dataContext = DbFactory.Init()); }
        }
        #endregion

        protected RepositoryBase(IDbFactory dbFactory)
        {
            DbFactory = dbFactory;
            dbSet = DbContext.Set<T>();
        }

        #region Implementation
        public virtual void Add(T entity)
        {
            dbSet.Add(entity);
        }

        public virtual void Update(T entity)
        {
            dbSet.Attach(entity);
            dataContext.Entry(entity).State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            dbSet.Remove(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = dbSet.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
                dbSet.Remove(obj);
        }

        public virtual T GetById(int id)
        {
            return dbSet.Find(id);
        }

        public virtual IEnumerable<T> GetAll()
        {
            return dbSet.ToList();
        }

        public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
        {
            return dbSet.Where(where).ToList();
        }

        public T Get(Expression<Func<T, bool>> where)
        {
            return dbSet.Where(where).FirstOrDefault<T>();
        }

        #endregion
    
    }
public interface IUnitOfWork
    {
        void Commit();
    }
public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbFactory dbFactory;
        private BloggerEntities dbContext;

        public UnitOfWork(IDbFactory dbFactory)
        {
            this.dbFactory = dbFactory;
        }

        public BloggerEntities DbContext
        {
            get { return dbContext ?? (dbContext = dbFactory.Init()); }
        }

        public void Commit()
        {
            DbContext.Commit();
        }
    }
public class ArticleRepository : RepositoryBase<Article>, IArticleRepository
    {
        public ArticleRepository(IDbFactory dbFactory)
            : base(dbFactory) { }

        public Article GetArticleByTitle(string articleTitle)
        {
            var _article = this.DbContext.Articles.Where(b => b.Title == articleTitle).FirstOrDefault();

            return _article;
        }
    }

    public interface IArticleRepository : IRepository<Article>
    {
        Article GetArticleByTitle(string articleTitle);
    }
public class BlogRepository : RepositoryBase<Blog>, IBlogRepository
    {
        public BlogRepository(IDbFactory dbFactory)
            : base(dbFactory) { }

        public Blog GetBlogByName(string blogName)
        {
            var _blog = this.DbContext.Blogs.Where(b => b.Name == blogName).FirstOrDefault();

            return _blog;
        }
    }

    public interface IBlogRepository : IRepository<Blog>
    {
        Blog GetBlogByName(string blogName);
    }

Service layer

Switch to UnitTestingWebAPI.Service project, add references to UnitTestingWebAPI.Domain,UnitTestingWebAPI.Data and add the following two files:

// operations you want to expose
    public interface IArticleService
    {
        IEnumerable<Article> GetArticles(string name = null);
        Article GetArticle(int id);
        Article GetArticle(string name);
        void CreateArticle(Article article);
        void UpdateArticle(Article article);
        void DeleteArticle(Article article);
        void SaveArticle();
    }

    public class ArticleService : IArticleService
    {
        private readonly IArticleRepository articlesRepository;
        private readonly IUnitOfWork unitOfWork;

        public ArticleService(IArticleRepository articlesRepository, IUnitOfWork unitOfWork)
        {
            this.articlesRepository = articlesRepository;
            this.unitOfWork = unitOfWork;
        }

        #region IArticleService Members

        public IEnumerable<Article> GetArticles(string title = null)
        {
            if (string.IsNullOrEmpty(title))
                return articlesRepository.GetAll();
            else
                return articlesRepository.GetAll().Where(c => c.Title.ToLower().Contains(title.ToLower()));
        }

        public Article GetArticle(int id)
        {
            var article = articlesRepository.GetById(id);
            return article;
        }

        public Article GetArticle(string title)
        {
            var article = articlesRepository.GetArticleByTitle(title);
            return article;
        }

        public void CreateArticle(Article article)
        {
            articlesRepository.Add(article);
        }

        public void UpdateArticle(Article article)
        {
            articlesRepository.Update(article);
        }

        public void DeleteArticle(Article article)
        {
            articlesRepository.Delete(article);
        }

        public void SaveArticle()
        {
            unitOfWork.Commit();
        }

        #endregion
    }
// operations you want to expose
    public interface IBlogService
    {
        IEnumerable<Blog> GetBlogs(string name = null);
        Blog GetBlog(int id);
        Blog GetBlog(string name);
        void CreateBlog(Blog blog);
        void UpdateBlog(Blog blog);
        void SaveBlog();
        void DeleteBlog(Blog blog);
    }

    public class BlogService : IBlogService
    {
        private readonly IBlogRepository blogsRepository;
        private readonly IUnitOfWork unitOfWork;

        public BlogService(IBlogRepository blogsRepository, IUnitOfWork unitOfWork)
        {
            this.blogsRepository = blogsRepository;
            this.unitOfWork = unitOfWork;
        }

        #region IBlogService Members

        public IEnumerable<Blog> GetBlogs(string name = null)
        {
            if (string.IsNullOrEmpty(name))
                return blogsRepository.GetAll();
            else
                return blogsRepository.GetAll().Where(c => c.Name == name);
        }

        public Blog GetBlog(int id)
        {
            var blog = blogsRepository.GetById(id);
            return blog;
        }

        public Blog GetBlog(string name)
        {
            var blog = blogsRepository.GetBlogByName(name);
            return blog;
        }

        public void CreateBlog(Blog blog)
        {
            blogsRepository.Add(blog);
        }

        public void UpdateBlog(Blog blog)
        {
            blogsRepository.Update(blog);
        }

        public void DeleteBlog(Blog blog)
        {
            blogsRepository.Delete(blog);
        }

        public void SaveBlog()
        {
            unitOfWork.Commit();
        }

        #endregion
    }

Web API Core Components

Switch to UnitTestingWebAPI.API.Core and add references to UnitTestingWebAPI.API.Domain and UnitTestingWebAPI.API.Service projects. Install the following packages from Nuget Packages:

  1. Entity Framework
  2. Microsoft.AspNet.WebApi.Core
  3. Microsoft.AspNet.WebApi.Client

Add the following Web API Controllers to a Controllers folder:

public class ArticlesController : ApiController
    {
        private IArticleService _articleService;

        public ArticlesController(IArticleService articleService)
        {
            _articleService = articleService;
        }

        // GET: api/Articles
        public IEnumerable<Article> GetArticles()
        {
            return _articleService.GetArticles();
        }

        // GET: api/Articles/5
        [ResponseType(typeof(Article))]
        public IHttpActionResult GetArticle(int id)
        {
            Article article = _articleService.GetArticle(id);
            if (article == null)
            {
                return NotFound();
            }

            return Ok(article);
        }

        // PUT: api/Articles/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutArticle(int id, Article article)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != article.ID)
            {
                return BadRequest();
            }

            _articleService.UpdateArticle(article);

            try
            {
                _articleService.SaveArticle();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ArticleExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Articles
        [ResponseType(typeof(Article))]
        public IHttpActionResult PostArticle(Article article)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            _articleService.CreateArticle(article);

            return CreatedAtRoute("DefaultApi", new { id = article.ID }, article);
        }

        // DELETE: api/Articles/5
        [ResponseType(typeof(Article))]
        public IHttpActionResult DeleteArticle(int id)
        {
            Article article = _articleService.GetArticle(id);
            if (article == null)
            {
                return NotFound();
            }

            _articleService.DeleteArticle(article);

            return Ok(article);
        }

        private bool ArticleExists(int id)
        {
            return _articleService.GetArticle(id) != null;
        }
    }
public class BlogsController : ApiController
    {
        private IBlogService _blogService;

        public BlogsController(IBlogService blogService)
        {
            _blogService = blogService;
        }

        // GET: api/Blogs
        public IEnumerable<Blog> GetBlogs()
        {
            return _blogService.GetBlogs();
        }

        // GET: api/Blogs/5
        [ResponseType(typeof(Blog))]
        public IHttpActionResult GetBlog(int id)
        {
            Blog blog = _blogService.GetBlog(id);
            if (blog == null)
            {
                return NotFound();
            }

            return Ok(blog);
        }

        // PUT: api/Blogs/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutBlog(int id, Blog blog)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != blog.ID)
            {
                return BadRequest();
            }

            _blogService.UpdateBlog(blog);

            try
            {
                _blogService.SaveBlog();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!BlogExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Blogs
        [ResponseType(typeof(Blog))]
        public IHttpActionResult PostBlog(Blog blog)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            _blogService.CreateBlog(blog);

            return CreatedAtRoute("DefaultApi", new { id = blog.ID }, blog);
        }

        // DELETE: api/Blogs/5
        [ResponseType(typeof(Blog))]
        public IHttpActionResult DeleteBlog(int id)
        {
            Blog blog = _blogService.GetBlog(id);
            if (blog == null)
            {
                return NotFound();
            }

            _blogService.DeleteBlog(blog);

            return Ok(blog);
        }

        private bool BlogExists(int id)
        {
            return _blogService.GetBlog(id) != null;
        }
    }

Add the following filter which when applied, it reverses the order of a List of articles:

public class ArticlesReversedFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            var objectContent = actionExecutedContext.Response.Content as ObjectContent;
            if (objectContent != null)
            {
                List<Article> _articles = objectContent.Value as List<Article>;
                if (_articles != null && _articles.Count > 0)
                {
                    _articles.Reverse();
                }
            }
        }
    }

Add the following MediaTypeFormatter which can return a comma serated representation of articles:

public class ArticleFormatter : BufferedMediaTypeFormatter
    {
        public ArticleFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/article"));
        }

        public override bool CanReadType(Type type)
        {
            return false;
        }

        public override bool CanWriteType(Type type)
        {
            //for single article object
            if (type == typeof(Article))
                return true;
            else
            {
                // for multiple article objects
                Type _type = typeof(IEnumerable<Article>);
                return _type.IsAssignableFrom(type);
            }
        }

        public override void WriteToStream(Type type,
                                           object value,
                                           Stream writeStream,
                                           HttpContent content)
        {
            using (StreamWriter writer = new StreamWriter(writeStream))
            {
                var articles = value as IEnumerable<Article>;
                if (articles != null)
                {
                    foreach (var article in articles)
                    {
                        writer.Write(String.Format("[{0},\"{1}\",\"{2}\",\"{3}\",\"{4}\"]",
                                                    article.ID,
                                                    article.Title,
                                                    article.Author,
                                                    article.URL,
                                                    article.Contents));
                    }
                }
                else
                {
                    var _article = value as Article;
                    if (_article == null)
                    {
                        throw new InvalidOperationException("Cannot serialize type");
                    }
                    writer.Write(String.Format("[{0},\"{1}\",\"{2}\",\"{3}\",\"{4}\"]",
                                                    _article.ID,
                                                    _article.Title,
                                                    _article.Author,
                                                    _article.URL,
                                                    _article.Contents));
                }
            }
        }
    }

Add the following two Message Handlers. The first one is responsible to add a custom header in the response and the second one is able to terminate the request if applied:

public class HeaderAppenderHandler : DelegatingHandler
    {
        async protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

            response.Headers.Add("X-WebAPI-Header", "Web API Unit testing in chsakell's blog.");
            return response;
        }
    }
public class EndRequestHandler : DelegatingHandler
    {
        async protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request.RequestUri.AbsoluteUri.Contains("test"))
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent("Unit testing message handlers!")
                };

                var tsc = new TaskCompletionSource<HttpResponseMessage>();
                tsc.SetResult(response);
                return await tsc.Task;
            }
            else
            {
                return await base.SendAsync(request, cancellationToken);
            }
        }
    }


Add the following DefaultAssembliesResolver which will be used for Controller registration from the Web Application project:

public class CustomAssembliesResolver : DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            var baseAssemblies = base.GetAssemblies().ToList();
            var assemblies = new List<Assembly>(baseAssemblies) { typeof(BlogsController).Assembly };
            baseAssemblies.AddRange(assemblies);

            return baseAssemblies.Distinct().ToList();
        }
    }

ASP.NET Web Application

Switch to UnitTestingWebAPI.API web application project and add references to UnitTestingWebAPI.Core,UnitTestingWebAPI.Data and UnitTestingWebAPI.Service. You will also need to install the following Nuget packages:

  1. Entity Framework
  2. Microsoft.AspNet.WebApi.WebHost
  3. Microsoft.AspNet.WebApi.Core
  4. Microsoft.AspNet.WebApi.Client
  5. Microsoft.AspNet.WebApi.Owin
  6. Microsoft.Owin.Host.SystemWeb
  7. Microsoft.Owin
  8. Autofac.WebApi2

Add a Global Configuration file if not exists and set the database initializer:

public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            // Init database
            System.Data.Entity.Database.SetInitializer(new BloggerInitializer());
        }
    }

Also make sure you add a relevant connection string in the Web.config file:

  <connectionStrings>
    <add name="BloggerEntities" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=BloggerDB;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

External Controller Registration

Create an Owin Startup.cs file at the root of the Web application and paste the following code. This code will ensure to use WebApi controllers from the UnitTestingWebAPI.API.Core project (CustomAssembliesResolver) and inject the appropriate repositories and services when required (autofac configuration):

public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            var config = new HttpConfiguration();
            config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
            config.Formatters.Add(new ArticleFormatter());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );


            // Autofac configuration
            var builder = new ContainerBuilder();
            builder.RegisterApiControllers(typeof(BlogsController).Assembly);
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
            builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerRequest();

            //Repositories
            builder.RegisterAssemblyTypes(typeof(BlogRepository).Assembly)
                .Where(t => t.Name.EndsWith("Repository"))
                .AsImplementedInterfaces().InstancePerRequest();
            // Services
            builder.RegisterAssemblyTypes(typeof(ArticleService).Assembly)
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces().InstancePerRequest();

            IContainer container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            appBuilder.UseWebApi(config);
        }
    }

At this point you should be able to fire the Web application and request articles or blogs using the following requests (port may be different in yours):

http://localhost:56032/api/articles
http://localhost:56032/api/blogs

Section Two: Unit Testing

We have completed structuring our application and it’s time to unit test all of our components. Switch to UnitTestingWebAPI.Tests class library and add references to UnitTestingWebAPI.Domain, UnitTestingWebAPI.Data,UnitTestingWebAPI.Service and UnitTestingWebAPI.API.Core. Also make sure you install the following Nuget Packages:

  1. Entity Framework
  2. Microsoft.AspNet.WebApi.Core
  3. Microsoft.AspNet.WebApi.Client
  4. Microsoft.AspNet.WebApi.Owin
  5. Microsoft.AspNet.WebApi.SelfHost
  6. Micoroft.Owin
  7. Owin
  8. Micoroft.Owin.Hosting
  9. Micoroft.Owin.Host.HttpListener
  10. Autofac.WebApi2
  11. NUnit
  12. NUnitTestAdapter

As you see we are going to use NUnit to write our unit tests.

Services Unit Testing

When writing Unit tests, first you need to setup or initiate some variables to be used for the unit tests. With NUnit this is done via a function with an attribute Setup applied on it. This very function will run before any NUnit test is executed. Unit testing the Service layer is the first thing you need to do since all the Controller’s constructors are injected with Services. Hence, you need to emulate repositories and service behavior before starting unit testing WebAPI Core components. In this example we ‘ll see how to emulate the ArticleSevice. This service’s constructor is injected with instances of IArticleRepository and IUnitOfWork so all we have to do is create two “special” instances and inject them.

public ArticleService(IArticleRepository articlesRepository, IUnitOfWork unitOfWork)
        {
            this.articlesRepository = articlesRepository;
            this.unitOfWork = unitOfWork;
        }

I said “special” cause those instance are not going to be real instances that actually can access the database.

Attention

Unit tests must run in memory and shouldn’t access databases. All core functionality must be emulated by using frameworks such as Mock in our case. This way automated tests will be much more faster. The basic purpose of unit tests is more testing component behavior rather than testing real results.

Let’s procceed with testing the ArticleService. Create a file named ServiceTests and for start add the following code:

    [TestFixture]
    public class ServicesTests
    {
        #region Variables
        IArticleService _articleService;
        IArticleRepository _articleRepository;
        IUnitOfWork _unitOfWork;
        List<Article> _randomArticles;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _randomArticles = SetupArticles();

            _articleRepository = SetupArticleRepository();
            _unitOfWork = new Mock<IUnitOfWork>().Object;
            _articleService = new ArticleService(_articleRepository, _unitOfWork);
        }

        public List<Article> SetupArticles()
        {
            int _counter = new int();
            List<Article> _articles = BloggerInitializer.GetAllArticles();

            foreach (Article _article in _articles)
                _article.ID = ++_counter;

            return _articles;
        }

        public IArticleRepository SetupArticleRepository()
        {
            // Init repository
            var repo = new Mock<IArticleRepository>();

            // Setup mocking behavior
            repo.Setup(r => r.GetAll()).Returns(_randomArticles);

            repo.Setup(r => r.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Article>(
                    id => _randomArticles.Find(a => a.ID.Equals(id))));

            repo.Setup(r => r.Add(It.IsAny<Article>()))
                .Callback(new Action<Article>(newArticle =>
                {
                    dynamic maxArticleID = _randomArticles.Last().ID;
                    dynamic nextArticleID = maxArticleID + 1;
                    newArticle.ID = nextArticleID;
                    newArticle.DateCreated = DateTime.Now;
                    _randomArticles.Add(newArticle);
                }));

            repo.Setup(r => r.Update(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                    {
                        var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
                        oldArticle.DateEdited = DateTime.Now;
                        oldArticle = x;
                    }));

            repo.Setup(r => r.Delete(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var _articleToRemove = _randomArticles.Find(a => a.ID == x.ID);

                    if (_articleToRemove != null)
                        _randomArticles.Remove(_articleToRemove);
                }));

            // Return mock implementation
            return repo.Object;
        }

        #endregion
   }
}

In the SetupArticleRepository() function we emulate our _articleRepository behavior, in other words we setup what results are expected from this repository instance when a specific function is called. Then we inject this instance in our _articleService’s constructor and we are ready to go. Let’s say that we want to test that the _articleService.GetArticles() behaves as expected. Add the following NUnit test in the same file:

        [Test]
        public void ServiceShouldReturnAllArticles()
        {
            var articles = _articleService.GetArticles();

            Assert.That(articles, Is.EqualTo(_randomArticles));
        }

Build the Tests project, run the test and make sure it passes. In the same way create the following tests:

        [Test]
        public void ServiceShouldReturnRightArticle()
        {
            var wcfSecurityArticle = _articleService.GetArticle(2);

            Assert.That(wcfSecurityArticle,
                Is.EqualTo(_randomArticles.Find(a => a.Title.Contains("Secure WCF Services"))));
        }

        [Test]
        public void ServiceShouldAddNewArticle()
        {
            var _newArticle = new Article()
            {
                Author = "Chris Sakellarios",
                Contents = "If you are an ASP.NET MVC developer, you will certainly..",
                Title = "URL Rooting in ASP.NET (Web Forms)",
                URL = "http://chsakell.com/2013/12/15/url-rooting-in-asp-net-web-forms/"
            };

            int _maxArticleIDBeforeAdd = _randomArticles.Max(a => a.ID);
            _articleService.CreateArticle(_newArticle);

            Assert.That(_newArticle, Is.EqualTo(_randomArticles.Last()));
            Assert.That(_maxArticleIDBeforeAdd + 1, Is.EqualTo(_randomArticles.Last().ID));
        }

        [Test]
        public void ServiceShouldUpdateArticle()
        {
            var _firstArticle = _randomArticles.First();

            _firstArticle.Title = "OData feat. ASP.NET Web API"; // reversed :-)
            _firstArticle.URL = "http://t.co/fuIbNoc7Zh"; // short link
            _articleService.UpdateArticle(_firstArticle);

            Assert.That(_firstArticle.DateEdited, Is.Not.EqualTo(DateTime.MinValue));
            Assert.That(_firstArticle.URL, Is.EqualTo("http://t.co/fuIbNoc7Zh"));
            Assert.That(_firstArticle.ID, Is.EqualTo(1)); // hasn't changed
        }

        [Test]
        public void ServiceShouldDeleteArticle()
        {
            int maxID = _randomArticles.Max(a => a.ID); // Before removal
            var _lastArticle = _randomArticles.Last();

            // Remove last article
            _articleService.DeleteArticle(_lastArticle);

            Assert.That(maxID, Is.GreaterThan(_randomArticles.Max(a => a.ID))); // Max reduced by 1
        }

Web API Controllers Unit Testing

Now that we are familiar with emulating our services behavior we can procceed with unit testing Web API Controllers. First thing we need to do is Setup the variables to be used through our test, so create a ControllerTests.cs file and paste the following code:

    [TestFixture]
    public class ControllerTests
    {
        #region Variables
        IArticleService _articleService;
        IArticleRepository _articleRepository;
        IUnitOfWork _unitOfWork;
        List<Article> _randomArticles;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _randomArticles = SetupArticles();

            _articleRepository = SetupArticleRepository();
            _unitOfWork = new Mock<IUnitOfWork>().Object;
            _articleService = new ArticleService(_articleRepository, _unitOfWork);
        }

        public List<Article> SetupArticles()
        {
            int _counter = new int();
            List<Article> _articles = BloggerInitializer.GetAllArticles();

            foreach (Article _article in _articles)
                _article.ID = ++_counter;

            return _articles;
        }

        public IArticleRepository SetupArticleRepository()
        {
            // Init repository
            var repo = new Mock<IArticleRepository>();

            // Setup mocking behavior
            repo.Setup(r => r.GetAll()).Returns(_randomArticles);

            repo.Setup(r => r.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Article>(
                    id => _randomArticles.Find(a => a.ID.Equals(id))));

            repo.Setup(r => r.Add(It.IsAny<Article>()))
                .Callback(new Action<Article>(newArticle =>
                {
                    dynamic maxArticleID = _randomArticles.Last().ID;
                    dynamic nextArticleID = maxArticleID + 1;
                    newArticle.ID = nextArticleID;
                    newArticle.DateCreated = DateTime.Now;
                    _randomArticles.Add(newArticle);
                }));

            repo.Setup(r => r.Update(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
                    oldArticle.DateEdited = DateTime.Now;
                    oldArticle.URL = x.URL;
                    oldArticle.Title = x.Title;
                    oldArticle.Contents = x.Contents;
                    oldArticle.BlogID = x.BlogID;
                }));

            repo.Setup(r => r.Delete(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var _articleToRemove = _randomArticles.Find(a => a.ID == x.ID);

                    if (_articleToRemove != null)
                        _randomArticles.Remove(_articleToRemove);
                }));

            // Return mock implementation
            return repo.Object;
        }

        #endregion
   }
}

WebAPI Controller classes are classes just like all others so we can test them respectively. Let’s see if the _articlesController.GetArticles() does return all articles available:

        [Test]
        public void ControlerShouldReturnAllArticles()
        {
            var _articlesController = new ArticlesController(_articleService);

            var result = _articlesController.GetArticles();

            CollectionAssert.AreEqual(result, _randomArticles);
        }

The most important line here is the highlighted one where the _articleService instance injection will ensure the service’s behavior.
In the same way we ensure that the last article is returned when invoking _articlesController.GetArticle(3) since we setup only 3 articles.

        [Test]
        public void ControlerShouldReturnLastArticle()
        {
            var _articlesController = new ArticlesController(_articleService);

            var result = _articlesController.GetArticle(3) as OkNegotiatedContentResult<Article>;

            Assert.IsNotNull(result);
            Assert.AreEqual(result.Content.Title, _randomArticles.Last().Title);
        }

Let’s test that an invalid Update operation must fail and return a BadRequestResult. Recall the Update operation setup on the _articleRepository:

repo.Setup(r => r.Update(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
                    oldArticle.DateEdited = DateTime.Now;
                    oldArticle.URL = x.URL;
                    oldArticle.Title = x.Title;
                    oldArticle.Contents = x.Contents;
                    oldArticle.BlogID = x.BlogID;
                }));

So If we pass an non existing article this update should fail:

        [Test]
        public void ControlerShouldPutReturnBadRequestResult()
        {
            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Put,
                    RequestUri = new Uri("http://localhost/api/articles/-1")
                }
            };

            var badresult = _articlesController.PutArticle(-1, new Article() { Title = "Unknown Article" });
            Assert.That(badresult, Is.TypeOf<BadRequestResult>());
        }

Complete the Controller Unit testing by adding the following three tests which tests that updating first article succeeds, post new article succeeds and post new article fails respectivelly.

        [Test]
        public void ControlerShouldPutUpdateFirstArticle()
        {
            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Put,
                    RequestUri = new Uri("http://localhost/api/articles/1")
                }
            };

            IHttpActionResult updateResult = _articlesController.PutArticle(1, new Article()
            {
                ID = 1,
                Title = "ASP.NET Web API feat. OData",
                URL = "http://t.co/fuIbNoc7Zh",
                Contents = @"OData is an open standard protocol.."
            }) as IHttpActionResult;

            Assert.That(updateResult, Is.TypeOf<StatusCodeResult>());

            StatusCodeResult statusCodeResult = updateResult as StatusCodeResult;

            Assert.That(statusCodeResult.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));

            Assert.That(_randomArticles.First().URL, Is.EqualTo("http://t.co/fuIbNoc7Zh"));
        }

        [Test]
        public void ControlerShouldPostNewArticle()
        {
            var article = new Article
            {
                Title = "Web API Unit Testing",
                URL = "http://chsakell.com/web-api-unit-testing",
                Author = "Chris Sakellarios",
                DateCreated = DateTime.Now,
                Contents = "Unit testing Web API.."
            };

            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Post,
                    RequestUri = new Uri("http://localhost/api/articles")
                }
            };

            _articlesController.Configuration.MapHttpAttributeRoutes();
            _articlesController.Configuration.EnsureInitialized();
            _articlesController.RequestContext.RouteData = new HttpRouteData(
            new HttpRoute(), new HttpRouteValueDictionary { { "_articlesController", "Articles" } });
            var result = _articlesController.PostArticle(article) as CreatedAtRouteNegotiatedContentResult<Article>;

            Assert.That(result.RouteName, Is.EqualTo("DefaultApi"));
            Assert.That(result.Content.ID, Is.EqualTo(result.RouteValues["id"]));
            Assert.That(result.Content.ID, Is.EqualTo(_randomArticles.Max(a => a.ID)));
        }

        [Test]
        public void ControlerShouldNotPostNewArticle()
        {
            var article = new Article
            {
                Title = "Web API Unit Testing",
                URL = "http://chsakell.com/web-api-unit-testing",
                Author = "Chris Sakellarios",
                DateCreated = DateTime.Now,
                Contents = null
            };

            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Post,
                    RequestUri = new Uri("http://localhost/api/articles")
                }
            };

            _articlesController.Configuration.MapHttpAttributeRoutes();
            _articlesController.Configuration.EnsureInitialized();
            _articlesController.RequestContext.RouteData = new HttpRouteData(
            new HttpRoute(), new HttpRouteValueDictionary { { "Controller", "Articles" } });
            _articlesController.ModelState.AddModelError("Contents", "Contents is required field");

            var result = _articlesController.PostArticle(article) as InvalidModelStateResult;

            Assert.That(result.ModelState.Count, Is.EqualTo(1));
            Assert.That(result.ModelState.IsValid, Is.EqualTo(false));
        }

Take a good look the highlighted lines and see that we can unit test several aspects of our requests, such as CodeStatus returned or routing properties.

Message Handlers Unit Testing

You can test Message Handlers by creating an instance of HttpMessageInvoker, passing the Message Handler instance you want to test and invoke the SendAsync function. Create a MessageHandlerTests.cs file and paste the Setup code first:

    [TestFixture]
    public class MessageHandlerTests
    {
        #region Variables
        private EndRequestHandler _endRequestHandler;
        private HeaderAppenderHandler _headerAppenderHandler;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            // Direct MessageHandler test
            _endRequestHandler = new EndRequestHandler();
            _headerAppenderHandler = new HeaderAppenderHandler()
            {
                InnerHandler = _endRequestHandler
            };
        }
        #endregion
   }
}

We setup the HeaderAppenderHandler’s inner handler another Handler that will terminate the request. Recall that the EndRequestHandler will end the request only if the Uri contains a test literal. Let’s write the test now:

        [Test]
        public async void ShouldAppendCustomHeader()
        {
            var invoker = new HttpMessageInvoker(_headerAppenderHandler);
            var result = await invoker.SendAsync(new HttpRequestMessage(HttpMethod.Get, 
                new Uri("http://localhost/api/test/")), CancellationToken.None);

            Assert.That(result.Headers.Contains("X-WebAPI-Header"), Is.True);
            Assert.That(result.Content.ReadAsStringAsync().Result, 
                Is.EqualTo("Unit testing message handlers!"));
        }

Now let’s pick up tha pace a little bit and make things quite more interesting. Let’s say you want to make an integration test that is you want to test the actual behavior of your Message Handler when a request is dispatched to a controller’s action. This would require to host the Web API and then run the unit test but is this possible here? Of course it, and this is the beauty when you create a highly loosely coupled application. All you have to do is Self host the web api and setup the appropriate configurations. In our case we are gonna host the web api and also setup Moq instances to be injected for Repositories and Services. Add the following Startup.cs file in the UnitTestingWebAPI.Tests project:

public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            var config = new HttpConfiguration();
            config.MessageHandlers.Add(new HeaderAppenderHandler());
            config.MessageHandlers.Add(new EndRequestHandler());
            config.Filters.Add(new ArticlesReversedFilter());
            config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            config.MapHttpAttributeRoutes();

            // Autofac configuration
            var builder = new ContainerBuilder();
            builder.RegisterApiControllers(typeof(ArticlesController).Assembly);

            // Unit of Work
            var _unitOfWork = new Mock<IUnitOfWork>();
            builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();

            //Repositories
            var _articlesRepository = new Mock<IArticleRepository>();
            _articlesRepository.Setup(x => x.GetAll()).Returns(
                    BloggerInitializer.GetAllArticles()
                );
            builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();

            var _blogsRepository = new Mock<IBlogRepository>();
            _blogsRepository.Setup(x => x.GetAll()).Returns(
                BloggerInitializer.GetBlogs
                );
            builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();

            // Services
            builder.RegisterAssemblyTypes(typeof(ArticleService).Assembly)
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces().InstancePerRequest();

            builder.RegisterInstance(new ArticleService(_articlesRepository.Object, _unitOfWork.Object));
            builder.RegisterInstance(new BlogService(_blogsRepository.Object, _unitOfWork.Object));

            IContainer container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            appBuilder.UseWebApi(config);
        }
    }

Notice that it’s quite similar to the Startup class we wrote for the Web Application project, except that fake repositories and services are used. Now let’s return and write this integration test:

        [Test]
        public void ShouldCallToControllerActionAppendCustomHeader()
        {
            //Arrange
            var address = "http://localhost:9000/";

            using (WebApp.Start<Startup>(address))
            {
                HttpClient _client = new HttpClient();
                var response = _client.GetAsync(address + "api/articles").Result;

                Assert.That(response.Headers.Contains("X-WebAPI-Header"), Is.True);

                var _returnedArticles = response.Content.ReadAsAsync<List<Article>>().Result;
                Assert.That(_returnedArticles.Count, Is.EqualTo( BloggerInitializer.GetAllArticles().Count));
            }
        }

Since the request doesn’t contain a test literal, it will reach the controller’s action and also bring the results. Notice also that the custom header has also been appended.

Action Filters Unit Testing

Recall that we had created an ArticlesReversedFilter that when applied it reverses the order of the articles that should be returned. We can either direct unit test this filter or run an integration one. We will see how to do both of them. To direct test an action filter you need to run it’s OnActionExecuted function by passing a instance of HttpActionExecutedContext as a parameter as follow:

        [Test]
        public void ShouldSortArticlesByTitle()
        {
            var filter = new ArticlesReversedFilter();
            var executedContext = new HttpActionExecutedContext(new HttpActionContext
            {
                Response = new HttpResponseMessage(),
            }, null);

            executedContext.Response.Content = new ObjectContent<List<Article>>(new List<Article>(_articles), new JsonMediaTypeFormatter());

            filter.OnActionExecuted(executedContext);

            var _returnedArticles = executedContext.Response.Content.ReadAsAsync<List<Article>>().Result;

            Assert.That(_returnedArticles.First(), Is.EqualTo(_articles.Last()));
        }

To run an integration test you need to self host the Web API and make the appropriate request. Mind that the filter must be registered in the Startup configuration class.

        [Test]
        public void ShouldCallToControllerActionReverseArticles()
        {
            //Arrange
            var address = "http://localhost:9000/";

            using (WebApp.Start<Startup>(address))
            {
                HttpClient _client = new HttpClient();
                var response = _client.GetAsync(address + "api/articles").Result;

                var _returnedArticles = response.Content.ReadAsAsync<List<Article>>().Result;

                Assert.That(_returnedArticles.First().Title, Is.EqualTo(BloggerInitializer.GetAllArticles().Last().Title));
            }
        }

Media Type formatters Unit Testing

You have created some custom Media Type formatters and you want to test their behavior. Recall the ArticleFormatter we created in the UnitTestingWebAPI.API.Core project and it’s able to return a comma separated string representation of articles. It can only write Article instances, not read ones or understand other type of classes. You need to set the Accept request header to application/article in order to apply the formatter. Let’s see the Setup configuration of our tests:

    [TestFixture]
    public class MediaTypeFormatterTests
    {
        #region Variables
        Blog _blog;
        Article _article;
        ArticleFormatter _formatter;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _blog = BloggerInitializer.GetBlogs().First();
            _article = BloggerInitializer.GetChsakellsArticles().First();
            _formatter = new ArticleFormatter();
        }
        #endregion
   }
}

You can test a MediaTypeFormatter by creating an instance of ObjectContent, passing the object to check if can be formatted by the respective formatter, and the formatter itself. If the formatter cannot read or write the passed object an exception will be thrown, otherwise not. For example let’s ensure that the ArticleFormatter cannot understand Blog instances:

        [Test]
        public void FormatterShouldThrowExceptionWhenUnsupportedType()
        {
            Assert.Throws<InvalidOperationException>(() => new ObjectContent<Blog>(_blog, _formatter));
        }

On the other hand it must work fine with parsing Article objects:

        [Test]
        public void FormatterShouldNotThrowExceptionWhenArticle()
        {
            Assert.DoesNotThrow(() => new ObjectContent<Article>(_article, _formatter));
        }

And here are some other tests you can run against your custom Media type formatters:

[Test]
        public void FormatterShouldHeaderBeSetCorrectly()
        {
            var content = new ObjectContent<Article>(_article, new ArticleFormatter());

            Assert.That(content.Headers.ContentType.MediaType, Is.EqualTo("application/article"));
        }

        [Test]
        public async void FormatterShouldBeAbleToDeserializeArticle()
        {
            var content = new ObjectContent<Article>(_article, _formatter);

            var deserializedItem = await content.ReadAsAsync<Article>(new[] { _formatter });

            Assert.That(_article, Is.SameAs(deserializedItem));
        }

        [Test]
        public void FormatterShouldNotBeAbleToWriteUnsupportedType()
        {
            var canWriteBlog = _formatter.CanWriteType(typeof(Blog));
            Assert.That(canWriteBlog, Is.False);
        }

        [Test]
        public void FormatterShouldBeAbleToWriteArticle()
        {
            var canWriteArticle = _formatter.CanWriteType(typeof(Article));
            Assert.That(canWriteArticle, Is.True);
        }

Routing Unit Testing

You want to test your routing configuration without hosting Web API. For this you ‘ll need a helper class that is able to return the Controller type or the controller’s action from an instance of a HttpControllerContext. Before this you have to create an HttpConfiguration with your routing configuration setup in it. Let’s see first the helper class:

public class ControllerActionSelector
    {
        #region Variables
        HttpConfiguration config;
        HttpRequestMessage request;
        IHttpRouteData routeData;
        IHttpControllerSelector controllerSelector;
        HttpControllerContext controllerContext;
        #endregion

        #region Constructor
        public ControllerActionSelector(HttpConfiguration conf, HttpRequestMessage req)
        {
            config = conf;
            request = req;
            routeData = config.Routes.GetRouteData(request);
            request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
            controllerSelector = new DefaultHttpControllerSelector(config);
            controllerContext = new HttpControllerContext(config, routeData, request);
        }
        #endregion

        #region Methods
        public string GetActionName()
        {
            if (controllerContext.ControllerDescriptor == null)
                GetControllerType();

            var actionSelector = new ApiControllerActionSelector();
            var descriptor = actionSelector.SelectAction(controllerContext);

            return descriptor.ActionName;    
        }

        public Type GetControllerType()
        {
            var descriptor = controllerSelector.SelectController(request);
            controllerContext.ControllerDescriptor = descriptor;
            return descriptor.ControllerType;
        }
        #endregion
    }

And now the RouteTests Setup configuration:

    [TestFixture]
    public class RouteTests
    {
        #region Variables
        HttpConfiguration _config;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _config = new HttpConfiguration();
            _config.Routes.MapHttpRoute(name: "DefaultWebAPI", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });
        }
        #endregion

        #region Helper methods
        public static string GetMethodName<T, U>(Expression<Func<T, U>> expression)
        {
            var method = expression.Body as MethodCallExpression;
            if (method != null)
                return method.Method.Name;

            throw new ArgumentException("Expression is wrong");
        }
        #endregion

Let’s see that a request to api/articles/5 invokes the ArticlesController.GetArticle(int id) function:

        [Test]
        public void RouteShouldControllerGetArticleIsInvoked()
        {
            var request = new HttpRequestMessage(HttpMethod.Get, "http://www.chsakell.com/api/articles/5");

            var _actionSelector = new ControllerActionSelector(_config, request);

            Assert.That(typeof(ArticlesController), Is.EqualTo(_actionSelector.GetControllerType()));
            Assert.That(GetMethodName((ArticlesController c) => c.GetArticle(5)),
                Is.EqualTo(_actionSelector.GetActionName()));
        }

We used some reflection to get controller’s action name. In the same way we can test that the post action is invoked:

        [Test]
        public void RouteShouldPostArticleActionIsInvoked()
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "http://www.chsakell.com/api/articles/");

            var _actionSelector = new ControllerActionSelector(_config, request);

            Assert.That(GetMethodName((ArticlesController c) =>
                c.PostArticle(new Article())), Is.EqualTo(_actionSelector.GetActionName()));
        }

You will probably want to test that an invalid route is not working:

        [Test]
        public void RouteShouldInvalidRouteThrowException()
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "http://www.chsakell.com/api/InvalidController/");

            var _actionSelector = new ControllerActionSelector(_config, request);

            Assert.Throws<HttpResponseException>(() => _actionSelector.GetActionName());
        }

Conclusion

We have seen many aspects of Unit Testing in Web API stack such as mocking the Service layer, unit testing Controllers, Message Handlers, Filters, Custom Media type Formatters and the routing configuration. Try to always writing unit tests for your application and you will never regret it. The most unit tests you write the more benefits you will get. For example a simple change in your repository may brake many aspects in your application. If the appropriate tests have been written, then in the first run you should see all broken parts of your application immediately. I hope you liked the post as much I did. You can download the source code for this project here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Web API File Uploading (Desktop and Web client)

$
0
0

File uploading is one of the most common tasks all developers have to deal with, at some point of their application development cycle. For example, Web applications (such as social networks) usually require users to upload a profile picture during registration or in the case of desktop ones, users may required to upload private documents.
This post will show you how to upload multipart/form-data files using Web API with both Web and Desktop clients. More particular here’s what we gonna see:

  • Web API File Upload Controller: Create an action for Uploading multipart-form data
  • Web client: Create an AngularJS web client to upload multiple files at once using the Angular File Upload module
  • Desktop client: Create an Windows Form client to upload multiple files using HttpClient

Let’s start:

The Controller

Create a solution named WebApiFileUpload and add a new empty Web Application named WebApiFileUpload.API selecting both the Web API and MVC checkboxes. Add a Web API Controller named FileUploadController. Before showing controller’s code let’s see what this controller requires:

  • A FileUpload result: Custom result to return to clients
  • A custom MultipartFormDataStreamProvider: The provider which will actually capture and save the uploaded file
  • Allow only Mime-Multipart content to be uploaded:For this our best option is to create a Filter and apply it to the POST method

Create a Infrastructure folder inside the API project and add the following three classes that implement the above required behaviors.

public class FileUploadResult
    {
        public string LocalFilePath { get; set; }
        public string FileName { get; set; }
        public long FileLength { get; set; }
    }
public class UploadMultipartFormProvider : MultipartFormDataStreamProvider
    {
        public UploadMultipartFormProvider(string rootPath) : base(rootPath) { }

        public override string GetLocalFileName(HttpContentHeaders headers)
        {
            if (headers != null &&
                headers.ContentDisposition != null)
            {
                return headers
                    .ContentDisposition
                    .FileName.TrimEnd('"').TrimStart('"');
            }

            return base.GetLocalFileName(headers);
        }
    }
public class MimeMultipart : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (!actionContext.Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(
                    new HttpResponseMessage(
                        HttpStatusCode.UnsupportedMediaType)
                );
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {

        }
    }

And now the controller:

public class FileUploadController : ApiController
    {
        [MimeMultipart]
        public async Task<FileUploadResult> Post()
        {
            var uploadPath = HttpContext.Current.Server.MapPath("~/Uploads");

            var multipartFormDataStreamProvider = new UploadMultipartFormProvider(uploadPath);

            // Read the MIME multipart asynchronously 
            await Request.Content.ReadAsMultipartAsync(multipartFormDataStreamProvider);

            string _localFileName = multipartFormDataStreamProvider
                .FileData.Select(multiPartData => multiPartData.LocalFileName).FirstOrDefault();

            // Create response
            return new FileUploadResult
            {
                LocalFilePath = _localFileName,

                FileName = Path.GetFileName(_localFileName),

                FileLength = new FileInfo(_localFileName).Length
            };
        }
    }

Make sure you create a folder named Uploads inside the project cause this is where the uploaded files will be saved. The uploaded result will contain the file uploaded local path in the server, file’s name and length in bytes. You can add any other properties you want.

AngularJS Web client

It’s time to create a web client using AngularJS that will be able to upload multiple files using the controller we have just created. As we have mentioned we are going to use the AngularJS File Upload module which can be found here. Create a Scripts folder inside the API project and you need to add the following javascript files:

  1. angular.js
  2. angular-file-upload.js
  3. angular-route.js
  4. jquery-1.8.2.js (you can use any other version if you want)

Also add the bootstrap.css inside a Styles folder. Add a HomeController MVC Controller and add an Index() action if not exists. Create the respective View for this action and paste the following code:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <link href="~/Styles/bootstrap.css" rel="stylesheet" />
</head>
<body ng-app="angularUploadApp">
    <div class="container">
        <div class="jumbotron">
            <p>Web API File uploading using AngularJS client. Read more at <a href="http://chsakell.com/2015/06/07/web-api-file-uploading-desktop-and-web-client" target="_blank">chsakell.com/</a>.</p>
        </div>
        <div ng-include="'app/templates/fileUpload.html'"></div>

    </div>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script src="Scripts/angular.js"></script>
    <script src="Scripts/angular-file-upload.js"></script>
    <script src="Scripts/angular-route.js"></script>
    <script src="app/app.js"></script>
    <script src="app/controllers/fileUploadCtrl.js"></script>

</body>
</html>

We haven’t created either the angularJS fileUpload.html or the app.js and fileUploadCtrl.js yet, so let’s add them. You have to create the following folders first:

  1. app
  2. app/controllers
  3. app/templates
'use strict';

angular.module('angularUploadApp', [
    'ngRoute',
    'angularFileUpload'
])

.config(function ($routeProvider) {
    $routeProvider
    .when('/', {
        templateUrl: 'app/templates/fileUpload.html',
        controller: 'fileUploadCtrl'
    })
    .otherwise({
        redirectTo: '/'
    });
});
'use strict';

angular.module('angularUploadApp')
    .controller('fileUploadCtrl', function ($scope, $http, $timeout, $upload) {
        $scope.upload = [];
        $scope.UploadedFiles = [];

        $scope.startUploading = function ($files) {
            //$files: an array of files selected
            for (var i = 0; i < $files.length; i++) {
                var $file = $files[i];
                (function (index) {
                    $scope.upload[index] = $upload.upload({
                        url: "./api/fileupload", // webapi url
                        method: "POST",
                        file: $file
                    }).progress(function (evt) {
                    }).success(function (data, status, headers, config) {
                        // file is uploaded successfully
                        $scope.UploadedFiles.push({ FileName: data.FileName, FilePath: data.LocalFilePath, FileLength : data.FileLength });
                    }).error(function (data, status, headers, config) {
                        console.log(data);
                    });
                })(i);
            }
        }
    });
<div class="row" ng-controller="fileUploadCtrl">
    <div class="col-xs-3">
        <div>
            <input type="file" ng-file-select="startUploading($files)" multiple>
        </div>
    </div>
    <div class="col-xs-9">
        <div class="panel panel-default" ng-repeat="uploadedFile in UploadedFiles track by $index">
            <div class="panel-heading">
                <strong>{{uploadedFile.FileName}}</strong>
            </div>
            <div class="panel-body">
                <div class=" media">
                    <a class="pull-left" href="#">
                        <img class="media-object" width="100" ng-src="../uploads/{{uploadedFile.FileName}}" />
                    </a>
                    <div class="media-body">
                        <div class="lead" style="font-size:14px;color: crimson;width:500px;word-wrap:break-word">{{uploadedFile.FilePath}}</div>
                    </div>
                </div>
            </div>
            <div class="panel-footer">
                File total bytes: <span style="color:black">{{uploadedFile.FileLength}}</span>
            </div>
        </div>
    </div>
</div>

Build and fire your application. Select multiple files to upload and ensure that all worked as expected.
webapi-file-upload-01

Desktop Client

For the Windows Form client you need to add a new Windows Form project named WebApiFileUpload.DesktopClient and add reference to the WebApiFileUpload.API project (just to access the FileUploadResult class). You also need to install the Microsoft.AspNet.WebApi.Client through the Nuget Packages. I have created a simple Form that allows the user to select multiple files and upload them through the API Controller we created. Let’s see the main method that does that.

try
                {
                    HttpClient httpClient = new HttpClient();
                    // Read the files 
                    foreach (String file in openFileDialog1.FileNames)
                    {
                        var fileStream = File.Open(file, FileMode.Open);
                        var fileInfo = new FileInfo(file);
                        FileUploadResult uploadResult = null;
                        bool _fileUploaded = false;

                        var content = new MultipartFormDataContent();
                        content.Add(new StreamContent(fileStream), "\"file\"", string.Format("\"{0}\"", fileInfo.Name)
                        );

                        Task taskUpload = httpClient.PostAsync(uploadServiceBaseAddress, content).ContinueWith(task =>
                        {
                            if (task.Status == TaskStatus.RanToCompletion)
                            {
                                var response = task.Result;

                                if (response.IsSuccessStatusCode)
                                {
                                    uploadResult = response.Content.ReadAsAsync<FileUploadResult>().Result;
                                    if (uploadResult != null)
                                        _fileUploaded = true;

                                    // Read other header values if you want..
                                    foreach (var header in response.Content.Headers)
                                    {
                                        Debug.WriteLine("{0}: {1}", header.Key, string.Join(",", header.Value));
                                    }
                                }
                                else
                                {
                                    Debug.WriteLine("Status Code: {0} - {1}", response.StatusCode, response.ReasonPhrase);
                                    Debug.WriteLine("Response Body: {0}", response.Content.ReadAsStringAsync().Result);
                                }
                            }

                            fileStream.Dispose();
                        });

                        taskUpload.Wait();
                        if (_fileUploaded)
                            AddMessage(uploadResult.FileName + " with length " + uploadResult.FileLength
                                            + " has been uploaded at " + uploadResult.LocalFilePath);
                    }

                    httpClient.Dispose();
                }
                catch (Exception ex)
                {
                    AddMessage(ex.Message);
                }

Of course you need to run the API Project first before trying to upload files using the Desktop client.
webapi-file-upload-02
That’s it, we are done! We saw how to upload multipart-form data files using Web API from both Web and Desktop clients. I hope you have enjoyed the post. You can download the project we built from here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small


Dependency injection in WCF

$
0
0

Dependency injection is a software design pattern that implements inversion of control for resolving dependencies and is highly recommended for building scalable, testable and maintainable applications. In this very blog we have seen many times this pattern, mostly in ASP.NET MVC and ASP.NET Web API related posts where for example dependencies (data repositories or middle services) were injected into MVC constructors. We haven’t seen this pattern though in applications using the Windows Communication Framework. I decided to write this post cause I believe there many interesting things to cover when it comes to “marry” Dependency Injection and WCF. I will divide this post in the following three main areas:

Solution architecture

We are going to build a Service Oriented Application based on Windows Communication Framework. The architecture is mostly based on the decision that we are going to de-couple client and business entities in our application. Many developers believe that the only way to create WCF clients is to generate them using Visual Studio built in functionality. Of course this method works like a charm but the truth is that it strongly binds both sides of the wire: Proxies will fault if any either client’s data contract or service data contract change. And here is where the power of System.Runtime.Serialization comes into the play supporting Data Contract versioning through the IExtensibleDataObject interface. This interface has one property ExtensionData of type ExtensionDataObject which makes data contract changes non-breakable in both sides. That said we will create both business and client entities in such a way that implement the IExtensibleDataObject interface. WCF Service will serve business entities but proxies (clients) will be able to serialize using their client ones. Business and client entities will share some common properties but nothing stops us to add more or remove some others in either of them. The serialization will work properly. More over you ‘ll see that client entities may have any other functionality you wish such as custom validators which a Web client may need for it’s UI. Data repositories will use business entities mapped to the database using Entity Framework. For this example we ‘ll use Entity Framework in an hybrid way that is design the database in SQL Server and manually add later the respective properties in our business entities. Let’s take a look at the architecture of our application.
wcf-dependency-injection-03

Let’s start building our SOA application. Create an empty solution named WCF.DependencyInjection and add the following solution folders with their respective projects:

  1. Business
    • Business.Entities: Class library (Contains Service side Entities)
    • Business.Services: Class library (Contains WCF Service contracts and implementations)
  2. Client
    • Client.Contracts: Class library (Contains the relevant Client side WCF Contracts)
    • Client.Entities: Class library (Contains client side Entities)
    • Client.Proxies: Class library (Contains WCF client proxies)
  3. Core
    • Core.Common: Class library (Contains Common classes – extensions for our solution)
  4. Hosts
    • WCF.DependencyInjection.Web: Empty ASP.NET Web application (Hosts WCF Services)
  5. Data Access
    • Data.Core: Class library (Contains Entity Framework configurations, Data repositories etc..)
  6. Tests
    • Test.Proxies: Class library (Contains NUnit tests for our solution)

Business.Entities

Switch to Business.Entities and create the following two Entities which will be used as Data Contracts at the business side of the wire.

 [DataContract]
    public class Blog : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public string Owner { get; set; }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }
[DataContract]
    public class Article : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Contents { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public int BlogID { get; set; }

        [DataMember]
        public int ContentLength
        {
            get
            {
                return Contents.Length;
            }
            set { }
        }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }

As you can see both of our entities implement the IExtensibleDataObject interface in order to support Round-Tripping (Forward-Compatible Data Contracts). The same will apply in Client entities too. You need to add reference to System.Runtime.Serialization assembly. In fact, go and add this reference to all of our projects.

Data.Core

We need to create the data access repositories before move to WCF Services so switch to Data.Core project, add reference to Business.Entities and install Entity Framework through Nuget Packages. At this point you will allow me not to paste all the required code for this layer since we have seen the same code implementing generic data repositories in many other posts of this blog. Instead, I will show you only the most important parts that needs to be explained for this solution. More over, I will provide you links for all not shown classes from my github account. Of course once again, all the source code for this post is available (you ‘ll find a link at the bottom of this post).
wcf-dependency-injection-02
Let’s see Entity Framework configurations for our entities.

public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            HasKey(b => b.ID);
            Property(b => b.Name).IsRequired().HasMaxLength(100);
            Property(b => b.URL).IsRequired().HasMaxLength(200);
            Property(b => b.Owner).IsRequired().HasMaxLength(50);

            Ignore(b => b.ExtensionData);
        }
    }
public class ArticleConfiguration : EntityTypeConfiguration<Article>
    {
        public ArticleConfiguration()
        {
            HasKey(a => a.ID);
            Property(a => a.Title).IsRequired().HasMaxLength(100);
            Property(a => a.Contents).IsRequired();
            Property(a => a.Author).IsRequired().HasMaxLength(50);
            Property(a => a.URL).IsRequired().HasMaxLength(200);

            Ignore(a => a.ExtensionData);
            Ignore(a => a.ContentLength);
        }
    }

It’s obvious that I just wanted to point that ExtensionData will not be mapped to a database table column. Now let’s see the DbContext class that we need to access table records through Entity Framework:

public class DIContext : DbContext
    {
        public DIContext()
            : base("DIContext")
        {
            Database.SetInitializer<DIContext>(null);
        }

        public DbSet<Blog> BlogSet { get; set; }
        public DbSet<Article> ArticleSet { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }

We turned off the database initializer and we told Entity Framework not to use the default naming convention when accessing database. Tables will have the same name as our entity classes. Here are all the other required classes:

  1. Data.Core.Infrastructure
  2. Data.Core.Repositories

Database

Let’s see WCFDI database we need for our example. Run the following script to create the database and insert some records as well.

USE [master]
GO
/****** Object:  Database [WCFDI] ******/
CREATE DATABASE [WCFDI]
GO
USE [WCFDI]
GO

-- Create Blog Table
CREATE TABLE [dbo].[Blog](
	[ID] [int] NOT NULL,
	[Name] [nvarchar](100) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[Owner] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Blog] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


-- Crate Article Table
CREATE TABLE [dbo].[Article](
	[ID] [int] NOT NULL,
	[Title] [nvarchar](100) NOT NULL,
	[Contents] [nvarchar](max) NOT NULL,
	[Author] [nvarchar](50) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[BlogID] [int] NOT NULL,
 CONSTRAINT [PK_Article] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
-- Create FK
GO
ALTER TABLE [dbo].[Article]  WITH CHECK ADD  CONSTRAINT [FK_Article_Blog] FOREIGN KEY([BlogID])
REFERENCES [dbo].[Blog] ([ID])
GO
ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_Article_Blog]
GO

-- Add some records
INSERT INTO dbo.Blog
        ( ID, Name, URL, Owner )
VALUES  ( 1, -- ID - int
          N'chsakells blog', -- Name - nvarchar(100)
          N'http://chsakell.com', -- URL - nvarchar(200)
          N'Chris Sakellarios'  -- Owner - nvarchar(50)
          )

INSERT INTO dbo.Article
        ( ID ,
          Title ,
          Contents ,
          Author ,
          URL ,
          BlogID
        )
VALUES  ( 1 , -- ID - int
          N'WCF Dependency Injection' , -- Title - nvarchar(100)
          N'Dependency injection is a software design pattern that implements..' , -- Contents - nvarchar(max)
          N'Christos Sakellarios' , -- Author - nvarchar(50)
          N'http://chsakell.com/2015/07/03/dependency-injection-in-wcf/' , -- URL - nvarchar(200)
          1  -- BlogID - int
        )

Business.Services

Now that we have done with the Data access layer we can proceed implement the core WCF Services that are going to be actually hosted (from Web application or Self hosts in our NUnit Tests). Add references to Data.Core, Business.Entities projects and System.ServiceModel assembly as well. Create a folder named Contracts inside the Business.Services project and add the following Service Contracts

    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }


At the root of this project create Services Implementations:

public class BlogService : IBlogService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IBlogRepository _blogRepository;

        public BlogService(IUnitOfWork unitOfWork, IBlogRepository blogRepository)
        {
            _unitOfWork = unitOfWork;
            _blogRepository = blogRepository;
        }
        public void Add(Blog blog)
        {
            _blogRepository.Add(blog);
            _unitOfWork.Commit();
        }

        public void Update(Blog blog)
        {
            _blogRepository.Update(blog);
            _unitOfWork.Commit();
        }

        public void Delete(Blog blog)
        {
            _blogRepository.Delete(blog);
            _unitOfWork.Commit();
        }

        public Blog GetById(int id)
        {
            return _blogRepository.GetById(id);
        }

        public Blog[] GetAll()
        {
            return _blogRepository.GetAll().ToArray();
        }
    }
public class ArticleService : IArticleService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IArticleRepository _articleRepository;

        public ArticleService(IUnitOfWork unitOfWork, IArticleRepository articleRepository)
        {
            _unitOfWork = unitOfWork;
            _articleRepository = articleRepository;
        }
        public void Add(Article article)
        {
            _articleRepository.Add(article);
            _unitOfWork.Commit();
        }

        public void Update(Article article)
        {
            _articleRepository.Update(article);
            _unitOfWork.Commit();
        }

        public void Delete(Article article)
        {
            _articleRepository.Delete(article);
            _unitOfWork.Commit();
        }

        public Article GetById(int id)
        {
            return _articleRepository.GetById(id);
        }

        public Article[] GetAll()
        {
            return _articleRepository.GetAll().ToArray();
        }
    }

Here we can see for the first time how Data repositories are going to be injected to WCF Services. For this to be completed we ‘ll have to register service’s implementations using an Inversion of Control container. We ‘ll do that later, both in Web host and self hosts using Autofac.

Core.Common

It’s time to create the client side of our SOA but before doing this, let’s create a base class for our client entities that will allow them to provide custom validation using the Fluent Validation. As we have already mentioned, client entities can have much more functionality than the Business Entities, functionality that may be required from client applications, web or desktop ones. For our example, we assume that clients needs to know if an entity is Valid or not based on custom validation rules for the specific client entity. In Core.Common install Fluent Validation through Nuget Packages and create the following class:

public class Validatable
    {
        protected IValidator _validator = null;
        protected IEnumerable<ValidationFailure> _validityErrors = null;

        protected virtual IValidator GetValidator()
        {
            return null;
        }

        public IEnumerable<ValidationFailure> ValidityErrors
        {
            get { return _validityErrors; }
            set { }
        }

        public void Validate()
        {
            if (_validator != null)
            {
                ValidationResult results = _validator.Validate(this);
                _validityErrors = results.Errors;
            }
        }

        public virtual bool IsValid
        {
            get
            {
                if (_validityErrors != null && _validityErrors.Count() > 0)
                    return false;
                else
                    return true;
            }
        }
    }

All that our Client Entities need to have is an override for the GetValidator() virtual method.

Client.Entities

Make sure you also install Fluent Validation package through Nuget Packages and create the following two files.

namespace Client.Entities
{
    public class Blog : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _name;
        string _url;
        string _owner;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Name
        {
            get
            {
                return _name;
            }
            set { _name = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public string Owner
        {
            get
            {
                return _owner;
            }
            set { _owner = value; }
        }

        #endregion

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new BlogValidator();
        }
        #endregion
    }

    // Validator Class
    class BlogValidator : AbstractValidator<Blog>
    {
        public BlogValidator()
        {
            RuleFor(b => b.Name).NotEmpty();
            RuleFor(b => b.Owner).NotEmpty();
            RuleFor(b => b.ID).GreaterThan(0);
        }
    }
}
namespace Client.Entities
{
    public class Article : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _title;
        string _contents;
        string _author;
        string _url;
        int _blogID;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Title
        {
            get
            {
                return _title;
            }
            set { _title = value; }
        }
        public string Contents
        {
            get
            {
                return _contents;
            }
            set { _contents = value; }
        }
        public string Author
        {
            get
            {
                return _author;
            }
            set { _author = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public int BlogID
        {
            get
            {
                return _blogID;
            }
            set { _blogID = value; }
        }
        #endregion 

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new ArticleValidator();
        }
        #endregion
    }

    // Validator Class
    class ArticleValidator : AbstractValidator<Article>
    {
        public ArticleValidator()
        {
            RuleFor(a => a.Author).NotEmpty();
            RuleFor(a => a.BlogID).GreaterThan(0);
            RuleFor(a => a.Contents).NotEmpty();
        }
    }
}

I believe it’s getting clearer now what we are trying to achieve. Those two classes will be used for serializing WCF call results. Clients won’t be tightly bound to WCF Business side entities. The next thing to do, is to create the relevant Client side Service Contracts that use Client Entities.

Client.Contracts

Switch to Client.Contracts and add references to Client.Entities and Core.Common projects. You also need to add reference to System.ServiceModel assembly. Create the following two contracts:

namespace Client.Contracts
{
    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
}
namespace Client.Contracts
{
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }
}

Seems familiar? Of course it does, it’s the same contracts we added in the business side of our SOA except that uses Client Entities.

Client.Proxies

We have created the client service contracts but we haven’t provided yet implementations for those interfaces. Guess what, the classes that will implement those contracts are the WCF Client proxies. Switch to Client.Proxies and add references to Client.Entities, Core.Common, Client.Contracts projects and System.ServiceModel assembly. Now provide the following implementations:

public class BlogClient : ClientBase<IBlogService>, IBlogService
    {
        #region IBlogService implementation
        public void Add(Blog blog)
        {
            Channel.Add(blog);
        }

        public void Update(Blog blog)
        {
            Channel.Update(blog);
        }

        public void Delete(Blog blog)
        {
            Channel.Delete(blog);
        }

        public Blog GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Blog[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }
public class ArticleClient : ClientBase<IArticleService>, IArticleService
    {
        #region IArticleService Members
        public void Add(Article article)
        {
            Channel.Add(article);
        }

        public void Update(Article article)
        {
            Channel.Update(article);
        }

        public void Delete(Article article)
        {
            Channel.Delete(article);
        }

        public Article GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Article[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion 

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }

Business Entities – Client Entities Deserialization

So far so good, but yet we haven’t completed all the missing parts relating to Deserializing Client entities to Business Entities and vise versa. Take a look at the Article entity in both sides, Client and Business. First of all notice that Business Article has DataContract and DataMember attributes to inform what parts of the class will be used for serialization. There isn’t such thing in the client side entity which means that all public properties will be used for serialization. I made that choice in order not to pollute the class since I may add even more functionality later. More over, If you are familiar with WCF Services, you will be aware that client and service data contracts must have the same namespace otherwise the deserialization will fail. At this point our client and business entities belong to different namespaces so it won’t work. We could add a namespace attribute value in the business entities like this:

[DataContract(Namespace="http://www.chsakell.com/WCF_DI")]

But this would require to insert the same attributes in the client side which is something I ‘d like to avoid. To solve this, there is a trick you can do, and register the common namespace for those entities. Switch to the AssemblyInfo.cs file in both of the projects and register there the namespace you want to be used for serialization-deserialization during WCF calls, by adding the following entries.

[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Business.Entities")]
[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Client.Entities")]

WCF Web Host

Switch to WCF.DependencyInjection.Web Web application to host our WCF Services. You need to add references to Business.Services, Data.Core projects and System.ServiceModel assembly. You also need to install Entity Framework and Autofac.Integration.Wcf packages from Nuget Packages as well.
Add two WCF Service files named BlogService.svc and ArticleService. Make sure you remove the *.svc.cs file created for each service and just leave the .svc file. Alter those files as follow:

<%@ ServiceHost 
    Language="C#" 
    Debug="true" 
    Service="Business.Services.Contracts.IBlogService, Business.Services" 
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>
<%@ ServiceHost 
    Language="C#" 
    Debug="true" 
    Service="Business.Services.Contracts.IArticleService, Business.Services" 
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

It’s time to register our services and implementations using Autofac in order for Dependency Injection to work. Create the following Bootstrapper class:

public static class Bootstrapper
    {
        /// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services
            
            builder.RegisterType<BlogService>().As<IBlogService>();
            builder.RegisterType<ArticleService>().As<IArticleService>();

            // register repositories and UnitOfWork
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
            builder.RegisterType<DbFactory>().As<IDbFactory>();
            builder.RegisterType<ArticleRepository>().As<IArticleRepository>();
            builder.RegisterType<BlogRepository>().As<IBlogRepository>();

            // build container
            return builder.Build();
        }
    }

You need to register this container in Application start event so create a Global.asax file (if not exists) and paste the following code that uses Autofac.Integration.Wcf.

protected void Application_Start(object sender, EventArgs e)
        {
            IContainer container = Bootstrapper.BuildContainer();
            AutofacHostFactory.Container = container;
        }

Don’t forget to add a connection string in Web.config file that points to WCFDI database we created before, to be used by Entity Framework.

  <connectionStrings>
    <add name="DIContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=WCFDI;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

At this point you should be able to use dependency injection concerning injecting data repositories to your WCF services.

Test.Proxies

We have done a lot of work structuring our SOA application and now it’s time to test all of our components. We need to test that data repositories are indeed injected and of course view how to configure proxy dependency injection. Switch to Test.Proxies project and for start add references to all of the other projects except the web application. Now install the following Nuget packages:

  1. Autofac WCF Integration
  2. Moq (We ‘ll mock the data repositories)
  3. NUnit
  4. NUnit Test Adapter

Also add reference to System.ServiceModel assembly.
Before creating the relevant Bootstrapper to be used by WCF self hosts, let’s create a class that needs WCF proxies to make WCF calls. Those proxies will have to be injected by Autofac container rather than be manually created.

public class ClientInjectionClass : Disposable
    {
        public Client.Contracts.IBlogService _blogProxy;
        public Client.Contracts.IArticleService _articleProxy;
        public ClientInjectionClass(Client.Contracts.IArticleService articleServiceProxy,
            Client.Contracts.IBlogService blogServiceProxy)
        {
            this._blogProxy = blogServiceProxy;
            this._articleProxy = articleServiceProxy;
        }

        #region IDisposable
        protected override void DisposeCore()
        {
            base.DisposeCore();
            try
            {
                (_blogProxy as Client.Proxies.BlogClient).CleanUp();

                (_articleProxy as Client.Proxies.ArticleClient).CleanUp();
            }
            catch
            {
                _blogProxy = null;
                _articleProxy = null;
            }
        }
        #endregion

        #region Methods

        public Client.Entities.Article[] GetArticles()
        {
            return _articleProxy.GetAll();
        }

        public Client.Entities.Blog GetBlogById(int id)
        {
            return _blogProxy.GetById(id);
        }

        #endregion
    }

Let’s view the Bootstrapper which will mock data repositories, register WCF Services, Client contracts with their respective implementations.

public static class Bootstrapper
    {/// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services
            builder.RegisterType<BlogService>().As<Business.Services.Contracts.IBlogService>();
            builder.RegisterType<ArticleService>().As<Business.Services.Contracts.IArticleService>();

            // register proxies
            builder.Register(c => new ChannelFactory<Client.Contracts.IArticleService>("BasicHttpBinding_IArticleService"))
                .InstancePerLifetimeScope();
            builder.Register(c => new ChannelFactory<Client.Contracts.IBlogService>("BasicHttpBinding_IBlogService"))
                .InstancePerLifetimeScope();

            builder.RegisterType<ArticleClient>().As<Client.Contracts.IArticleService>().UseWcfSafeRelease();
            builder.RegisterType<BlogClient>().As<Client.Contracts.IBlogService>().UseWcfSafeRelease();

            // Unit of Work
            var _unitOfWork = new Mock<IUnitOfWork>();
            builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();
            // DbFactory
            var _dbFactory = new Mock<IDbFactory>();
            builder.RegisterInstance(_dbFactory.Object).As<IDbFactory>();

            //Repositories
            var _articlesRepository = new Mock<IArticleRepository>();
            _articlesRepository.Setup(x => x.GetAll()).Returns(new List<Business.Entities.Article>()
                {
                    new Business.Entities.Article() { 
                        ID = 1, 
                        Author = "Chris Sakellarios", 
                        BlogID = 1, 
                        Contents = "Dependency injection is a software design pattern that implements..", 
                        Title = "WCF Dependency Injection", 
                        URL =" http://chsakell.com/2015/07/03/dependency-injection-in-wcf/"}
                });
            builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();

            var _blogsRepository = new Mock<IBlogRepository>();
            _blogsRepository.Setup(x => x.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Business.Entities.Blog>(
                    id => new Business.Entities.Blog() { 
                        ID = id,
                        Name = "chsakell's blog",
                        Owner = "Chris Sakellarios",
                        URL = "http://chsakell.com/"
                    }));
            builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();

            builder.RegisterType<ClientInjectionClass>();

            // build container
            return builder.Build();
        }
    }

I have highlighted the parts that we register client side service contracts to used for WCF proxy injection because when this is done, clients will search for the respective Endpoint configurations in the configuration file. To make this work, add an App.config file in the Test.Proxies project and paste the following code.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IArticleService" />
        <binding name="BasicHttpBinding_IBlogService" />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:18850/ArticleService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IArticleService"
          contract="Client.Contracts.IArticleService" name="BasicHttpBinding_IArticleService" />
      <endpoint address="http://localhost:18850/BlogService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBlogService"
          contract="Client.Contracts.IBlogService" name="BasicHttpBinding_IBlogService" />
    </client>
  </system.serviceModel>
</configuration>

Create a ProxiesTests.cs to write our NUnit Tests. First of all we need to Setup the self host and the IoC Container before each of our test runs. At the end of the test we can close the host. for those two we ‘ll use NUnit’s Setup and TearDown attributes.

    [TestFixture]
    public class ProxiesTests
    {
        IContainer container = null;
        ServiceHost svcArticleHost = null;
        ServiceHost svcBlogHost = null;
        Uri svcArticleServiceURI = new Uri("http://localhost:18850/ArticleService.svc");
        Uri svcBlogServiceURI = new Uri("http://localhost:18850/BlogService.svc");
        

        [SetUp]
        public void Setup()
        {
            try
            {
                container = Bootstrapper.BuildContainer();

                svcArticleHost = new ServiceHost(typeof(ArticleService), svcArticleServiceURI);
                svcBlogHost = new ServiceHost(typeof(BlogService), svcBlogServiceURI);

                svcArticleHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IArticleService>(container);
                svcBlogHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IBlogService>(container);

                svcArticleHost.Open();
                svcBlogHost.Open();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }

        [TearDown]
        public void TearDown()
        {
            try
            {
                if (svcArticleHost != null && svcArticleHost.State != CommunicationState.Closed)
                    svcArticleHost.Close();

                if (svcBlogHost != null && svcBlogHost.State != CommunicationState.Closed)
                    svcBlogHost.Close();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
            finally
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }
}

Before running tests, make sure you run Visual Studio as administrator for self host to work. Let’s write a test to check that self host works as expected:

        [Test]
        public void test_self_host_connection()
        {
            Assert.That(svcArticleHost.State, Is.EqualTo(CommunicationState.Opened));
            Assert.That(svcBlogHost.State, Is.EqualTo(CommunicationState.Opened));
        }

Test that ArticleClient and BlogClient proxies are injected properly when resolving Client.Contracts.IArticleService and Client.Contracts.IBlogService client service contracts:

        [Test]
        public void test_article_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IArticleService proxy
                = container.Resolve<Client.Contracts.IArticleService>();

                Assert.IsTrue(proxy is ArticleClient);
            }
        }

        [Test]
        public void test_blog_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IBlogService proxy
                = container.Resolve<Client.Contracts.IBlogService>();

                Assert.IsTrue(proxy is BlogClient);
            }
        }

Test proxy state expected behavior:

        [Test]
        public void test_article_proxy_state()
        {
            Client.Contracts.IArticleService proxy;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                CommunicationState state = (proxy as ArticleClient).State;
                Assert.That(state, Is.EqualTo(CommunicationState.Created));

                // Open connection
                (proxy as ArticleClient).Open();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Opened));
                // Close connection
                (proxy as ArticleClient).Close();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            }
        }

Test data repositories injection in WCF Services:

        [Test]
        public void test_article_proxy_getall()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            // Close connection
            if ((proxy as ArticleClient).State == CommunicationState.Opened)
                (proxy as ClientBase<Client.Contracts.IArticleService>).Close();
        }

Test that proxies are injected and cleaned properly in ClientInjectionClass.

        [Test]
        public void test_constructor_injected_proxy()
        {
            ClientInjectionClass _testClass = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                using (_testClass = new ClientInjectionClass(container.Resolve<Client.Contracts.IArticleService>(),
                   container.Resolve<Client.Contracts.IBlogService>()))
                {
                    {
                        Client.Entities.Article[] _articles = _testClass.GetArticles();
                        Client.Entities.Blog _blog = _testClass.GetBlogById(1);

                        Assert.That(_articles.Count(), Is.EqualTo(1));
                        Assert.That(_blog, Is.Not.Null);
                        Assert.That(_blog.IsValid, Is.EqualTo(true));
                    }
                }
            }

            Assert.That((_testClass._articleProxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            Assert.That((_testClass._blogProxy as BlogClient).State, Is.EqualTo(CommunicationState.Closed));
        }

In case you have noticed, I added an extra property named ContentLength at the Business Article entity, a property that doesn’t exist in the client Article entity. Still serialization – deserialization works like a charm due to the IExtensibleDataObject interface the both implement. Let’s write a test to view that Article client holds this property in it’s ExtensionData property. To read this property we need a method that uses reflection and you can view it from here.

        [Test]
        public void test_article_extension_data_not_empty()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            var contentLength = Extensions.GetExtensionDataMemberValue(articles.First(), "ContentLength");

            Assert.That(articles.First().Contents.Length, Is.EqualTo(Int32.Parse(contentLength.ToString())));
        }

Discussion

We have seen many interesting concepts in this article, from WCF Dependency Injection integration and client proxy injection to de-coupled and extensible Data Contracts. There is one thing I would like to discuss here and it has to do about dependency injection in constructors. Days ago, a fellow asked me what if a class required ten or even twenty repositories? Do I have to pollute my constructor that much? The answer is not necessary. There is another more abstract and generic pattern that you can use where you create a generic Factory capable of resolving custom IDataRepository interfaces. In other words, you can create an interface like this:

public interface IDataRepositoryFactory
    {
        T GetCustomDataRepository<T>() where T : IDataRepository;
    }

And then implement it as follow:

public class DataRepositoryFactory : IDataRepositoryFactory
    {
        T IDataRepositoryFactory.GetCustomDataRepository<T>()
        {
            return Container.Resole<T>();
        }
    }

Then all you have to do is register this high level interface using an IoC and use this in your constructors instead of custom ones. When you need a custom repository, for example an IArticleRepository you get it like this:

private IArticleRepository _articleRepository;
private IBlogRepository _blogRepository;
private IDataRepositoryFactory _repositoryFactory;

// constructor
public ArticleService(IDataRepositoryFactory repositoryFactory)
        {
            _repositoryFactory= repositoryFactory;
        }

// method that requires IArticleRepository
public Article[] GetAll()
{
  _articleRepository = _repositoryFactory.GetCustomDataRepository<IArticleRepository>();
  return _articleRepository.GetAll();
}
// method that requires IBlogRepository
public Blog[] GetAll()
{
  _blogRepository= _repositoryFactory.GetCustomDataRepository<IBlogRepository>();
  return _blogRepository.GetAll();
}

That’s it, we ‘re done! I hope you enjoyed reading this post as much as I did writing it. You can download the source code for this post here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Building Single Page Applications using Web API and angularJS (Free e-book)

$
0
0

Single Page Applications are getting more and more attractive nowadays for two basic reasons. Website users have always preferred a fluid user experience than one with page reloads and the incredible growth of several JavaScript frameworks such as angularJS. This growth in conjunction with all the powerful server side frameworks makes Single Page Application development a piece of cake. This post is the online version of the free e-book and describes step by step how to build a production-level SPA using ASP.NET Web API 2 and angularJS. You have two choices. Either grab a coffee and keep going on with this version or simply download the e-book and enjoy it whenever and wherever you want.
spa-webapi-angular-28
There are a lot of stuff to build in this application so I will break this post in the following sections:

  • What we are going to build: Describe the purpose and the requirements of our SPA
  • What we are going to use: The list of technologies and all server and front-end side libraries and frameworks
  • SPA architecture: The entire design of our SPA application from the lowest to the highest level
  • Domain Entities and Data repositories: Build the required Domain Entities and Data repositories using the generic repository pattern
  • Membership: Create a custom authentication mechanism which will be used for Basic Authentication through Web API
  • Single Page Application: Start building the core SPA components step by step
  • Discussion: We ‘ll discuss the choices we made in the development process and everything you need to know to scale up the SPA

Ready to go? Let’s start!

What we are going to build

We are going to build a Singe Page Application to support the requirements of a Video Rental store that is a store that customers visit, pick and rent DVDs. Days later they come back and return what they have borrowed. This Web application is supposed to be used only by the rental store’s employees and that’s a requirement that will affect mostly the front-end application’s architecture. Let’s see the requirements along with their respective screenshots:

Requirement 1: Home page

  1. Latest DVD movies released added to the system must be displayed
  2. For each DVD, relevant information and functionality must be available, such as display availability, watch YouTube trailer and its rating
  3. On the right side of the page, genre statistics are being displayed
  4. This page should be accessible to unauthenticated users

spa-webapi-angular-01

Requirement 2: Customers

  1. There will be 2 pages related to customers. One to view and edit them and another for registration
  2. Both of the pages must be accessible only to authenticated users
  3. The page where all customers are being displayed should use pagination for faster results. A search textbox must be able to filter the already displayed customers and start a new server side search as well
  4. Customer information should be editable in the same view through a modal popup window

spa-webapi-angular-02
spa-webapi-angular-03
spa-webapi-angular-04

Requirement 3: Movies

  1. All movies must be displayed with their relevant information (availability, trailer etc..)
  2. Pagination must be used for faster results, and user can either filter the already displayed movies or search for new ones
  3. Clicking on a DVD image must show the movie’s Details view where user can either edit the movie or rent it to a specific customer if available. This view is accessible only to authenticated users
  4. When employee decides to rent a specific DVD to a customer through the Rent view, it should be able to search customers through an auto-complete textbox
  5. The details view displays inside a panel, rental-history information for this movie, that is the dates rentals and returnings occurred. From this panel user can search a specific rental and mark it as returned
  6. Authenticated employees should be able to add a new entry to the system. They should be able to upload a relevant image for the movie as well

spa-webapi-angular-05
spa-webapi-angular-06
spa-webapi-angular-07
spa-webapi-angular-08
spa-webapi-angular-09

Requirement 4: Movie Rental History

  1. There should be a specific view for authenticated users where rental history is being displayed for all system’s movies. History is based on total rentals per date and it’s being displayed through a line chart

spa-webapi-angular-10

Requirement 5: Accounts

  1. There should be views for employees to either login or register to system. For start employees are being registered as Administrator

spa-webapi-angular-11

General requirements

  1. All views should be displayed smoothly even to mobile devices. For this bootstrap and collapsible components will be used (sidebar, topbar)

spa-webapi-angular-12

What we are going to use

We have all the requirements, now we need to decide the technologies we are going to use in order to build our SPA application.

Server Side

  1. ASP.NET Web API for serving data to Web clients (browsers)
  2. Entity Framework as Object-relational Mapper for accessing data (SQL Server)
  3. Autofac for Inversion of Control Container and resolving dependencies
  4. Automapper for mapping Domain entities to ViewModels
  5. FluentValidation for validating ViewModels in Web API Controllers

Front Side

  1. AngularJS as the core JavaScript framework
  2. Bootstrap 3 as the CSS framework for creating a fluent and mobile compatible interface
  3. 3rd party libraries

SPA architecture

We have seen both application’s requirements and the technologies we are going to use, now it’s time to design a decoupled, testable and scalable solution. There are two different designs we need to provide here. The first one has to do with the entire project’s solution structure and how is this divided in independent components. The second one has to do with the SPA structure itself, that is how angularJS folders and files will be organized.

Application Design – 1

  1. At the lowest level we have the Database. We ‘ll use Entity Framework Code First but this doesn’t prevent us to design the database directly from the SQL Server. In fact that’s what I did, I created all the tables and relationships in my SQL Server and then added the respective model Entities. That way I didn’t have to work with Code First Migrations and have more control on my database and entities. Though, I have to note that when development processes finished, I enabled code first migrations and added a seed method to initialize some data, just to help you kick of the project. We ‘ll see more about this in the installation section.
  2. The next level are the domain Entities. These are the classes that will map our database tables. One point I want to make here is that when I started design my entities, none of them had virtual references or collections for lazy loading. Those virtual properties were added during the development and the needs of the application.
  3. Entity Framework configurations, DbContext and Generic Repositories are the next level. Here we ‘ll configure EF and we ‘ll create the base classes and repositories to access database data
  4. Service layer is what comes next. For this application there will be only one service the membership service. This means that data repositories are going to be injected directly to our Web API Controllers. I ‘ve made that decision because there will be no complex functionality as far the data accessing. If you wish though you can use this layer to add a middle layer for data accessing too.
  5. Last but not least is the Web application that will contain the Web API Controllers and the SPA itself. This project will start as an Empty ASP.NET Web Application with both Web API and MVC references included.

Let’s take a look at the Database design.
spa-webapi-angular-13
Notice that for each Movie can exist multiple stock items that may be available or not. Think this as there are many DVDs of the same movie. The movie itself will be categorized as available or not depending on if there is any available stock item or not. Each customer rents a stock item and when he/she does, this stock item becomes unavailable until he/she returns it back to store. The tables used to accomplish this functionality are Customer, Rental, Stock. You can also see that there are 3 membership tables, User, UserRole and Role which are quite self explanatory. Upon them we ‘ll build the custom membership mechanism. I have also created an Error table just to show you how to avoid polluting you code with Try, Catch blocks and have a centralized logging point in your application.
spa-webapi-angular-14

Application Design – 2 (angular components)

  1. Folders are organized by Feature in our SPA. This means that you ‘ll see folders such as Customers, Movies and Rental
  2. Each of those folders may have angularJS controllers, directives or templates
  3. There is a folder Modules for hosting reusable components-modules. Those modules use common directives or services from the respective common folders
  4. 3rd party libraries are inside a folder Vendors. Here I want to point something important. You should (if not already yet) start using Bower for installing-downloading web dependencies, packages etc.. After you download required packages through Bower, you can then either include them in your project or simple simply reference them from their downloaded folder. In this application though, you will find all the required vendors inside this folder and just for reference, I will provide you with the bower installation commands for most of those packages.

spa-webapi-angular-15

Domain Entities and Data repositories

Time to start building our Single Page Application. Create a new empty solution named HomeCinema and add new class library project named HomeCinema.Entities. We ‘ll create those first. All of our entities will implement an IEntityBase interface which means that will have an ID property mapping to their primary key in the database. Add the following interface:

public interface IEntityBase
    {
        int ID { get; set; }
    }

Each movie belongs to a specific Genre (Comedy, Drama, Action, etc..). If we want to be able to retrieve all movies through a Genre instance, then we need to add a virtual collection of Movies property.

public class Genre : IEntityBase
    {
        public Genre()
        {
            Movies = new List<Movie>();
        }
        public int ID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Movie> Movies { get; set; }
    }

The most important Entity of our application is the Movie. A movie holds information such as title, director, release date, trailer URL (Youtube) or rating. As we have already mentioned, for each movie there are several stock items and hence for this entity we need to add a collection of Stock.

public class Movie : IEntityBase
    {
        public Movie()
        {
            Stocks = new List<Stock>();
        }
        public int ID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Image { get; set; }
        public int GenreId { get; set; }
        public virtual Genre Genre { get; set; }
        public string Director { get; set; }
        public string Writer { get; set; }
        public string Producer { get; set; }
        public DateTime ReleaseDate { get; set; }
        public byte Rating { get; set; }
        public string TrailerURI { get; set; }
        public virtual ICollection<Stock> Stocks { get; set; }
    }

Each stock actually describes a DVD by itself. It has a reference to a specific movie and a unique key (code) that uniquely identifies it. For example, when there are three available DVDs for a specific movie then 3 unique codes identify those DVDs. The employee will choose among those codes which could probably be written on the DVD to rent a specific movie to a customer. Since a movie rental is directly connected to a stock item, Stock entity may have a collection of Rental items that is all rentals for this stock item.

public class Stock : IEntityBase
    {
        public Stock()
        {
            Rentals = new List<Rental>();
        }
        public int ID { get; set; }
        public int MovieId { get; set; }
        public virtual Movie Movie { get; set; }
        public Guid UniqueKey { get; set; }
        public bool IsAvailable { get; set; }
        public virtual ICollection<Rental> Rentals { get; set; }
    }

The customer Entity is self explanatory.

public class Customer : IEntityBase
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string IdentityCard { get; set; }
        public Guid UniqueKey { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Mobile { get; set; }
        public DateTime RegistrationDate { get; set; }
    }

The Rental entity which finally describes a DVD rental for a specific customer holds information about the customer, the stock item he/she picked (DVD and its code), the rentals date, its status (Borrowed or Returned and the date the customer returned it.

public class Rental : IEntityBase
    {
        public int ID { get; set; }
        public int CustomerId { get; set; }
        public int StockId { get; set; }
        public virtual Stock Stock { get; set; }
        public DateTime RentalDate { get; set; }
        public Nullable<DateTime> ReturnedDate { get; set; }
        public string Status { get; set; }
    }

Now let’s see all Entities related to Membership. The first one is the Role that describes logged in user’s role. For our application there will be only the Admin role (employees) but we will discuss later the scalability options we have in case we want customers to use the application as well. Let me remind you that we are going to use Basic Authentication for Web API Controllers and many controllers and their actions will have an Authorize attribute and a list of roles authorized to access their resources.

public class Role : IEntityBase
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

User entity holds basic information for the user and most important the salt and the encrypted by this salt, password.

public class User : IEntityBase
    {
        public User()
        {
            UserRoles = new List<UserRole>();
        }
        public int ID { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public string HashedPassword { get; set; }
        public string Salt { get; set; }
        public bool IsLocked { get; set; }
        public DateTime DateCreated { get; set; }

        public virtual ICollection<UserRole> UserRoles { get; set; }
    }

A user may have more than one roles so we have a UserRole Entity as well.

public class UserRole :IEntityBase
    {
        public int ID { get; set; }
        public int UserId { get; set; }
        public int RoleId { get; set; }
        public virtual Role Role { get; set; }
    }

One last entity I have added is the Error. It is always good to log your application’s errors and we ‘ll use a specific repository to do this. I decided to add error logging functionality in order to show you a nice trick that will prevent you from polluting you controllers with Try Catch blocks all over the place. We ‘ll see it in action when we reach Web API Controllers.

public class Error : IEntityBase
    {
        public int ID { get; set; }
        public string Message { get; set; }
        public string StackTrace { get; set; }
        public DateTime DateCreated { get; set; }
    }

Data Repositories

Add a new class library project named HomeCinema.Data and add reference to HomeCinema.Entities project. Make sure you also install Entity Framework through Nuget Packages. For start we will create EF Configurations for our Entities. Add a new folder named Configurations and add the following configuration to declare the primary key for our Entities:

public class EntityBaseConfiguration<T> : EntityTypeConfiguration<T> where T : class, IEntityBase
    {
        public EntityBaseConfiguration()
        {
            HasKey(e => e.ID);
        }
    }

Entity Framework either way assumes that a property named “ID” is a primary key but this is a nice way to declare it in case you give this property different name. Following are one by one all other configurations. I will highlight the important lines (if any) to notice for each of these.

public class GenreConfiguration : EntityBaseConfiguration<Genre>
    {
        public GenreConfiguration()
        {
            Property(g => g.Name).IsRequired().HasMaxLength(50);
        }
    }
public MovieConfiguration()
        {
            Property(m => m.Title).IsRequired().HasMaxLength(100);
            Property(m => m.GenreId).IsRequired();
            Property(m => m.Director).IsRequired().HasMaxLength(100);
            Property(m => m.Writer).IsRequired().HasMaxLength(50);
            Property(m => m.Producer).IsRequired().HasMaxLength(50);
            Property(m => m.Writer).HasMaxLength(50);
            Property(m => m.Producer).HasMaxLength(50);
            Property(m => m.Rating).IsRequired();
            Property(m => m.Description).IsRequired().HasMaxLength(2000);
            Property(m => m.TrailerURI).HasMaxLength(200);
            HasMany(m => m.Stocks).WithRequired().HasForeignKey(s => s.MovieId);
        }
    }
public class StockConfiguration : EntityBaseConfiguration<Stock>
    {
        public StockConfiguration()
        {
            Property(s => s.MovieId).IsRequired();
            Property(s => s.UniqueKey).IsRequired();
            Property(s => s.IsAvailable).IsRequired();
            HasMany(s => s.Rentals).WithRequired(r=> r.Stock).HasForeignKey(r => r.StockId);
        }
    }
public class CustomerConfiguration : EntityBaseConfiguration<Customer>
    {
        public CustomerConfiguration()
        {
            Property(u => u.FirstName).IsRequired().HasMaxLength(100);
            Property(u => u.LastName).IsRequired().HasMaxLength(100);
            Property(u => u.IdentityCard).IsRequired().HasMaxLength(50);
            Property(u => u.UniqueKey).IsRequired();
            Property(c => c.Mobile).HasMaxLength(10);
            Property(c => c.Email).IsRequired().HasMaxLength(200);
            Property(c => c.DateOfBirth).IsRequired();
        }
    }
public class RentalConfiguration : EntityBaseConfiguration<Rental>
    {
        public RentalConfiguration()
        {
            Property(r => r.CustomerId).IsRequired();
            Property(r => r.StockId).IsRequired();
            Property(r => r.Status).IsRequired().HasMaxLength(10);
            Property(r => r.ReturnedDate).IsOptional();
        }
    }
public class RoleConfiguration : EntityBaseConfiguration<Role>
    {
        public RoleConfiguration()
        {
            Property(ur => ur.Name).IsRequired().HasMaxLength(50);
        }
    }
public class UserRoleConfiguration : EntityBaseConfiguration<UserRole>
    {
        public UserRoleConfiguration()
        {
            Property(ur => ur.UserId).IsRequired();
            Property(ur => ur.RoleId).IsRequired();
        }
    }
public class UserConfiguration : EntityBaseConfiguration<User>
    {
        public UserConfiguration()
        {
            Property(u => u.Username).IsRequired().HasMaxLength(100);
            Property(u => u.Email).IsRequired().HasMaxLength(200);
            Property(u => u.HashedPassword).IsRequired().HasMaxLength(200);
            Property(u => u.Salt).IsRequired().HasMaxLength(200);
            Property(u => u.IsLocked).IsRequired();
            Property(u => u.DateCreated);
        }
    }

Those configurations will affect how the database tables will be created. Add a new class named HomeCinemaContext at the root of the project. This class will inherit from DbContext and will be the main class for accessing data from the database.

 public class HomeCinemaContext : DbContext
    {
        public HomeCinemaContext()
            : base("HomeCinema")
        {
            Database.SetInitializer<HomeCinemaContext>(null);
        }

        #region Entity Sets
        public IDbSet<User> UserSet { get; set; }
        public IDbSet<Role> RoleSet { get; set; }
        public IDbSet<UserRole> UserRoleSet { get; set; }
        public IDbSet<Customer> CustomerSet { get; set; }
        public IDbSet<Movie> MovieSet { get; set; }
        public IDbSet<Genre> GenreSet { get; set; }
        public IDbSet<Stock> StockSet { get; set; }
        public IDbSet<Rental> RentalSet { get; set; }
        public IDbSet<Error> ErrorSet { get; set; }
        #endregion

        public virtual void Commit()
        {
            base.SaveChanges();
        }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new UserConfiguration());
            modelBuilder.Configurations.Add(new UserRoleConfiguration());
            modelBuilder.Configurations.Add(new RoleConfiguration());
            modelBuilder.Configurations.Add(new CustomerConfiguration());
            modelBuilder.Configurations.Add(new MovieConfiguration());
            modelBuilder.Configurations.Add(new GenreConfiguration());
            modelBuilder.Configurations.Add(new StockConfiguration());
            modelBuilder.Configurations.Add(new RentalConfiguration());
        }
    }

Notice that I ‘ve made a decision to name all of the Entity Sets with a Set prefix. Also I turned of the default pluralization convention that Entity Framework uses when creating the tables in database. This will result in tables having the same name as the Entity. You need to add an App.config file if not exists and create the following connection string:

  <connectionStrings>
    <add name="HomeCinema" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=HomeCinema;Integrated Security=SSPI; MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />
  </connectionStrings>

You can alter the server if you wish to match your development environment.
Let us proceed with the UnitOfWork pattern implementation. Add a folder named Infrastructure and paste the following classes and interfaces:

public class Disposable : IDisposable
    {
        private bool isDisposed;

        ~Disposable()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if (!isDisposed && disposing)
            {
                DisposeCore();
            }

            isDisposed = true;
        }

        // Ovveride this to dispose custom objects
        protected virtual void DisposeCore()
        {
        }
    }
public interface IDbFactory : IDisposable
    {
        HomeCinemaContext Init();
    }
public class DbFactory : Disposable, IDbFactory
    {
        HomeCinemaContext dbContext;

        public HomeCinemaContext Init()
        {
            return dbContext ?? (dbContext = new HomeCinemaContext());
        }

        protected override void DisposeCore()
        {
            if (dbContext != null)
                dbContext.Dispose();
        }
    }
public interface IUnitOfWork
    {
        void Commit();
    }
public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbFactory dbFactory;
        private HomeCinemaContext dbContext;

        public UnitOfWork(IDbFactory dbFactory)
        {
            this.dbFactory = dbFactory;
        }

        public HomeCinemaContext DbContext
        {
            get { return dbContext ?? (dbContext = dbFactory.Init()); }
        }

        public void Commit()
        {
            DbContext.Commit();
        }
    }

Time for the Generic Repository Pattern. We have seen this pattern many times in this blog but this time I will make a slight change. One of the blog’s readers asked me if he had to create a specific repository class that implements the generic repository T each time a need for a new type of repository is needed. Reader’s question was really good if you think that you may have hundred of Entities in a large scale application. The answer is NO and we will see it on action in this project where will try to inject repositories of type T as needed. Create a folder named Repositories and add the following interface with its implementation class:

public interface IEntityBaseRepository<T> where T : class, IEntityBase, new()
    {
        IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
        IQueryable<T> All { get; }
        IQueryable<T> GetAll();
        T GetSingle(int id);
        IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Edit(T entity);
    }
public class EntityBaseRepository<T> : IEntityBaseRepository<T>
            where T : class, IEntityBase, new()
    {

        private HomeCinemaContext dataContext;

        #region Properties
        protected IDbFactory DbFactory
        {
            get;
            private set;
        }

        protected HomeCinemaContext DbContext
        {
            get { return dataContext ?? (dataContext = DbFactory.Init()); }
        }
        public EntityBaseRepository(IDbFactory dbFactory)
        {
            DbFactory = dbFactory;
        }
        #endregion
        public virtual IQueryable<T> GetAll()
        {
            return DbContext.Set<T>();
        }
        public virtual IQueryable<T> All
        {
            get
            {
                return GetAll();
            }
        }
        public virtual IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = DbContext.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;
        }
        public T GetSingle(int id)
        {
            return GetAll().FirstOrDefault(x => x.ID == id);
        }
        public virtual IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
        {
            return DbContext.Set<T>().Where(predicate);
        }

        public virtual void Add(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry<T>(entity);
            DbContext.Set<T>().Add(entity);
        }
        public virtual void Edit(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Modified;
        }
        public virtual void Delete(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Deleted;
        }
    }

Before leaving this project and proceed with the Services one, there is only one thing remained to do. As I said when I was developing this application I was designing the database on my SQL Server and adding the respective Entities at the same time (yeap, you can do this as well..). No migrations where enabled. Yet, I thought that I should enable them in order to help you kick of the project and create the database automatically. For this you should do the same thing if you follow along with me. Open Package Manager Console, make sure you have selected the HomeCinema.Data project and type the following command:

enable-migrations

This will add a Configuration class inside a Migrations folder. This Configuration class has a seed method that is invoked when you create the database. The seed method I ‘ve written is a little bit large for pasting it here so please find it here. What I did is add data for Genres, Movies, Roles, Customers and Stocks. For customers I used a Nuget Package named MockData so make sure you install it too. More over I added a user with username chsakell and password homecinema. You can use them in order to login to our SPA. Otherwise you can just register a new user and sign in with those credentials. If you want to create the database right now, run the following commands from the Package Manager Console:

add-migration "initial_migration"
update-database -verbose

We ‘ll come up again to HomeCinema.Data project later to add some extension methods.

Membership

There is one middle layer between the Web application and the Data repositories and that’s the Service layer. In this application though, we will use this layer only for the membership’s requirements leaving all data repositories being injected as they are directly to API Controllers. Add a new class library project named HomeCinema.Services and make sure you add references to both of the other projects, HomeCinema.Data and HomeCinema.Entities. First, we ‘ll create a simple Encryption service to create salts and encrypted passwords and then we ‘ll use this service to implement a custom membership mechanism. Add a folder named Abstract and create the following interfaces.

public interface IEncryptionService
    {
        string CreateSalt();
        string EncryptPassword(string password, string salt);
    }
public interface IMembershipService
    {
        MembershipContext ValidateUser(string username, string password);
        User CreateUser(string username, string email, string password, int[] roles);
        User GetUser(int userId);
        List<Role> GetUserRoles(string username);
    }

At the root of this project add the EncryptionService implementation. It is a simple password encryption based on a salt and the SHA256 algorithm from System.Security.Cryptography namespace. Of course you can always use your own implementation algorithm.

public class EncryptionService : IEncryptionService
    {
        public string CreateSalt()
        {
            var data = new byte[0x10];
            using (var cryptoServiceProvider = new RNGCryptoServiceProvider())
            {
                cryptoServiceProvider.GetBytes(data);
                return Convert.ToBase64String(data);
            }
        }

        public string EncryptPassword(string password, string salt)
        {
            using (var sha256 = SHA256.Create())
            {
                var saltedPassword = string.Format("{0}{1}", salt, password);
                byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
                return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
            }
        }
    }

Let’s move to the Membership Service now. For start let’s see the base components of this class. Add the following class at the root of the project as well.

public class MembershipService : IMembershipService
    {
        #region Variables
        private readonly IEntityBaseRepository<User> _userRepository;
        private readonly IEntityBaseRepository<Role> _roleRepository;
        private readonly IEntityBaseRepository<UserRole> _userRoleRepository;
        private readonly IEncryptionService _encryptionService;
        private readonly IUnitOfWork _unitOfWork;
        #endregion

        public MembershipService(IEntityBaseRepository<User> userRepository, IEntityBaseRepository<Role> roleRepository,
        IEntityBaseRepository<UserRole> userRoleRepository, IEncryptionService encryptionService, IUnitOfWork unitOfWork)
        {
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _userRoleRepository = userRoleRepository;
            _encryptionService = encryptionService;
            _unitOfWork = unitOfWork;
        }
    }

Here we can see for the first time the way the generic repositories are going to be injected through the Autofac Inversion of Control Container. Before moving to the core implementation of this service we will have to add a User extension method in HomeCinema.Data and some helper methods in the previous class. Switch to HomeCinema.Data and add a new folder named Extensions. We will use this folder for adding Data repository extensions based on the Entity Set. Add the following User Entity extension method which retrieves a User instance based on its username.

public static class UserExtensions
    {
        public static User GetSingleByUsername(this IEntityBaseRepository<User> userRepository, string username)
        {
            return userRepository.GetAll().FirstOrDefault(x => x.Username == username);
        }
    }

Switch again to MembershipService class and add the following helper private methods.

#region Helper methods
        private void addUserToRole(User user, int roleId)
        {
            var role = _roleRepository.GetSingle(roleId);
            if (role == null)
                throw new ApplicationException("Role doesn't exist.");

            var userRole = new UserRole()
            {
                RoleId = role.ID,
                UserId = user.ID
            };
            _userRoleRepository.Add(userRole);
        }

        private bool isPasswordValid(User user, string password)
        {
            return string.Equals(_encryptionService.EncryptPassword(password, user.Salt), user.HashedPassword);
        }

        private bool isUserValid(User user, string password)
        {
            if (isPasswordValid(user, password))
            {
                return !user.IsLocked;
            }

            return false;
        }
        #endregion

Let’s view the CreateUser implementation method. The method checks if username already in use and if not creates the user.

public User CreateUser(string username, string email, string password, int[] roles)
        {
            var existingUser = _userRepository.GetSingleByUsername(username);

            if (existingUser != null)
            {
                throw new Exception("Username is already in use");
            }

            var passwordSalt = _encryptionService.CreateSalt();

            var user = new User()
            {
                Username = username,
                Salt = passwordSalt,
                Email = email,
                IsLocked = false,
                HashedPassword = _encryptionService.EncryptPassword(password, passwordSalt),
                DateCreated = DateTime.Now
            };

            _userRepository.Add(user);

            _unitOfWork.Commit();

            if (roles != null || roles.Length > 0)
            {
                foreach (var role in roles)
                {
                    addUserToRole(user, role);
                }
            }

            _unitOfWork.Commit();

            return user;
        }

The GetUser and GetUserRoles implementations are quite simple.

public User GetUser(int userId)
        {
            return _userRepository.GetSingle(userId);
        }
public List<Role> GetUserRoles(string username)
        {
            List<Role> _result = new List<Role>();

            var existingUser = _userRepository.GetSingleByUsername(username);

            if (existingUser != null)
            {
                foreach (var userRole in existingUser.UserRoles)
                {
                    _result.Add(userRole.Role);
                }
            }

            return _result.Distinct().ToList();
        }

I left the ValidateUser implementation last because is a little more complex than the others. You will have noticed from its interface that this service make use of a class named MembershipContext. This custom class is the one that will hold the IPrincipal object when authenticating users. When a valid user passes his/her credentials the service method will create an instance of GenericIdentity for user’s username. Then it will set the IPrincipal property using the GenericIdentity created and user’s roles. User roles information will be used to authorize API Controller’s actions based on logged in user’s roles. Let’s see the MembershipContext class and then the ValidateUser implementation.

public class MembershipContext
    {
        public IPrincipal Principal { get; set; }
        public User User { get; set; }
        public bool IsValid()
        {
            return Principal != null;
        }
    }
public MembershipContext ValidateUser(string username, string password)
        {
            var membershipCtx = new MembershipContext();

            var user = _userRepository.GetSingleByUsername(username);
            if (user != null && isUserValid(user, password))
            {
                var userRoles = GetUserRoles(user.Username);
                membershipCtx.User = user;
                
                var identity = new GenericIdentity(user.Username);
                membershipCtx.Principal = new GenericPrincipal(
                    identity,
                    userRoles.Select(x => x.Name).ToArray());
            }

            return membershipCtx;
        }

Check that MembershipContext class may hold additional information for the User. Add anything else you wish according to your needs.

Single Page Application

We are done building the core components for the HomeCinema Single Page Application, now it’s time to create the Web Application that will make use all of the previous parts we created. Add a new Empty Web Application project named HomeCinema.Web and make sure to check both the Web API and MVC check buttons. Add references to all the previous projects and install the following Nuget Packages:

Nuget Packages

  1. Entity Framework
  2. Autofac ASP.NET Web API 2.2 Integration
  3. Automapper
  4. FluentValidation

First thing we need to do is create any configurations we want to apply to our application. We ‘ll start with the Autofac Inversion of Control Container to work along with Web API framework. Next we will configure the bundling and last but not least, we will add the MessageHandler to configure the Basic Authentication. Add the following class inside the App_Start project’s folder.

public class AutofacWebapiConfig
    {
        public static IContainer Container;
        public static void Initialize(HttpConfiguration config)
        {
            Initialize(config, RegisterServices(new ContainerBuilder()));
        }

        public static void Initialize(HttpConfiguration config, IContainer container)
        {
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        }

        private static IContainer RegisterServices(ContainerBuilder builder)
        {
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // EF HomeCinemaContext
            builder.RegisterType<HomeCinemaContext>()
                   .As<DbContext>()
                   .InstancePerRequest();

            builder.RegisterType<DbFactory>()
                .As<IDbFactory>()
                .InstancePerRequest();

            builder.RegisterType<UnitOfWork>()
                .As<IUnitOfWork>()
                .InstancePerRequest();

            builder.RegisterGeneric(typeof(EntityBaseRepository<>))
                   .As(typeof(IEntityBaseRepository<>))
                   .InstancePerRequest();

            // Services
            builder.RegisterType<EncryptionService>()
                .As<IEncryptionService>()
                .InstancePerRequest();

            builder.RegisterType<MembershipService>()
                .As<IMembershipService>()
                .InstancePerRequest();

            Container = builder.Build();

            return Container;
        }
    }

This will make sure dependencies will be injected in constructors as expected. Since we are in the App_Start folder let’s configure the Bundling in our application. Add the following class in App_Start folder as well.

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                "~/Scripts/Vendors/modernizr.js"));

            bundles.Add(new ScriptBundle("~/bundles/vendors").Include(
                "~/Scripts/Vendors/jquery.js",
                "~/Scripts/Vendors/bootstrap.js",
                "~/Scripts/Vendors/toastr.js",
                "~/Scripts/Vendors/jquery.raty.js",
                "~/Scripts/Vendors/respond.src.js",
                "~/Scripts/Vendors/angular.js",
                "~/Scripts/Vendors/angular-route.js",
                "~/Scripts/Vendors/angular-cookies.js",
                "~/Scripts/Vendors/angular-validator.js",
                "~/Scripts/Vendors/angular-base64.js",
                "~/Scripts/Vendors/angular-file-upload.js",
                "~/Scripts/Vendors/angucomplete-alt.min.js",
                "~/Scripts/Vendors/ui-bootstrap-tpls-0.13.1.js",
                "~/Scripts/Vendors/underscore.js",
                "~/Scripts/Vendors/raphael.js",
                "~/Scripts/Vendors/morris.js",
                "~/Scripts/Vendors/jquery.fancybox.js",
                "~/Scripts/Vendors/jquery.fancybox-media.js",
                "~/Scripts/Vendors/loading-bar.js"
                ));

            bundles.Add(new ScriptBundle("~/bundles/spa").Include(
                "~/Scripts/spa/modules/common.core.js",
                "~/Scripts/spa/modules/common.ui.js",
                "~/Scripts/spa/app.js",
                "~/Scripts/spa/services/apiService.js",
                "~/Scripts/spa/services/notificationService.js",
                "~/Scripts/spa/services/membershipService.js",
                "~/Scripts/spa/services/fileUploadService.js",
                "~/Scripts/spa/layout/topBar.directive.js",
                "~/Scripts/spa/layout/sideBar.directive.js",
                "~/Scripts/spa/layout/customPager.directive.js",
                "~/Scripts/spa/directives/rating.directive.js",
                "~/Scripts/spa/directives/availableMovie.directive.js",
                "~/Scripts/spa/account/loginCtrl.js",
                "~/Scripts/spa/account/registerCtrl.js",
                "~/Scripts/spa/home/rootCtrl.js",
                "~/Scripts/spa/home/indexCtrl.js",
                "~/Scripts/spa/customers/customersCtrl.js",
                "~/Scripts/spa/customers/customersRegCtrl.js",
                "~/Scripts/spa/customers/customerEditCtrl.js",
                "~/Scripts/spa/movies/moviesCtrl.js",
                "~/Scripts/spa/movies/movieAddCtrl.js",
                "~/Scripts/spa/movies/movieDetailsCtrl.js",
                "~/Scripts/spa/movies/movieEditCtrl.js",
                "~/Scripts/spa/controllers/rentalCtrl.js",
                "~/Scripts/spa/rental/rentMovieCtrl.js",
                "~/Scripts/spa/rental/rentStatsCtrl.js"
                ));

            bundles.Add(new StyleBundle("~/Content/css").Include(
                "~/content/css/site.css",
                "~/content/css/bootstrap.css",
                "~/content/css/bootstrap-theme.css",
                 "~/content/css/font-awesome.css",
                "~/content/css/morris.css",
                "~/content/css/toastr.css",
                "~/content/css/jquery.fancybox.css",
                "~/content/css/loading-bar.css"));

            BundleTable.EnableOptimizations = false;
        }
    }

You may wonder where the heck will I find all these files? Do not worry about that, i will provide you links for all the static JavaScript libraries inside the ~/bundles/vendors bundle and the CSS stylesheets in the ~/Content/css one as well. All JavaScript files in the ~/bundles/spa bundle are the angularJS components we are going to build. Don’t forget that as always the source code for this application will be available at the end of this post. Add the Bootstrapper class in the App_Start folder. We will call its Run method from the Global.asax ApplicationStart method. For now let the Automapper’s part commented out and we will un-comment it when the time comes.

public class Bootstrapper
    {
        public static void Run()
        {
            // Configure Autofac
            AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
            //Configure AutoMapper
            //AutoMapperConfiguration.Configure();
        }
    }

Change Global.asax.cs (add it if not exists) as follow:

public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            var config = GlobalConfiguration.Configuration;

            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(config);
            Bootstrapper.Run();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            GlobalConfiguration.Configuration.EnsureInitialized();
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

Not all pages will be accessible to unauthenticated users as opposed from application requirements and for this reason we are going to use Basic Authentication. This will be done through a Message Handler whose job is to search for an Authorization header in the request. Create a folder named Infrastructure at the root of the web application project and add a sub-folder named MessageHandlers. Create the following handler.

public class HomeCinemaAuthHandler : DelegatingHandler
    {
        IEnumerable<string> authHeaderValues = null;
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try
            {
                request.Headers.TryGetValues("Authorization",out authHeaderValues);
                if(authHeaderValues == null)
                    return base.SendAsync(request, cancellationToken); // cross fingers

                var tokens = authHeaderValues.FirstOrDefault();
                tokens = tokens.Replace("Basic","").Trim();
                if (!string.IsNullOrEmpty(tokens))
                {
                    byte[] data = Convert.FromBase64String(tokens);
                    string decodedString = Encoding.UTF8.GetString(data);
                    string[] tokensValues = decodedString.Split(':');
                    var membershipService = request.GetMembershipService();

                    var membershipCtx = membershipService.ValidateUser(tokensValues[0], tokensValues[1]);
                    if (membershipCtx.User != null)
                    {
                        IPrincipal principal = membershipCtx.Principal;
                        Thread.CurrentPrincipal = principal;
                        HttpContext.Current.User = principal;
                    }
                    else // Unauthorized access - wrong crededentials
                    {
                        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                        var tsc = new TaskCompletionSource<HttpResponseMessage>();
                        tsc.SetResult(response);
                        return tsc.Task;
                    }
                }
                else
                {
                    var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                    var tsc = new TaskCompletionSource<HttpResponseMessage>();
                    tsc.SetResult(response);
                    return tsc.Task;
                }
                return base.SendAsync(request, cancellationToken);
            }
            catch
            {
                var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                var tsc = new TaskCompletionSource<HttpResponseMessage>();
                tsc.SetResult(response);
                return tsc.Task;
            }
        }
    }

The GetMembershipService() is an HttpRequestMessage extension which I will provide right away. Notice that we ‘ve made some decisions in this handler. When a request dispatches, the handler searches for an Authorization header. If it doesn’t find one then we cross our fingers and let the next level decide if the request is accessible or not. This means that if a Web API Controller’s action has the AllowAnonymous attribute the request doesn’t have to hold an Authorization header. One the other hand if the action did have an Authorize attribute then an Unauthorized response message would be returned in case of empty Authorization header. If Authorization header is present, the membership service decodes the based64 encoded credentials and checks their validity. User and role information is saved in the HttpContext.Current.User object.
spa-webapi-angular-16
As far as the HttpRequestMessage extension add a folder named Extensions inside the Infrastructure folder and create the following class.

public static class RequestMessageExtensions
    {
        internal static IMembershipService GetMembershipService(this HttpRequestMessage request)
        {
            return request.GetService<IMembershipService>();
        }

        private static TService GetService<TService>(this HttpRequestMessage request)
        {
            IDependencyScope dependencyScope = request.GetDependencyScope();
            TService service = (TService)dependencyScope.GetService(typeof(TService));

            return service;
        }
    }

Now switch to the WebApiConfig.cs inside the App_Start folder and register the authentication message handler.

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            config.MessageHandlers.Add(new HomeCinemaAuthHandler());

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

Don’t forget to add the same connection string you added in the HomeCinema.Data App.config file, in the Web.config configuration file.

Static CSS files and images

We configured previously the bundling but at this point you don’t have all the necessary files and their respective folders in your application. Add a Content folder at the root of the web application and create the following sub-folders in it.

CSS – Fonts – Images

  1. Content/css: Bootstrap, Font-awesome, fancy-box, morris, toastr, homecinema relative css files
  2. Content/fonts: Bootstrap and font-awesome required fonts
  3. Content/images
  4. Content/images/movies: Hold’s application’s movies
  5. Content/images/raty: Raty.js required images

You can find and download all these static files from here.

Vendors – 3rd party libraries

Create a Scripts folder in application’s root and a two sub-folders, Scripts/spa and Scripts/vendors. Each of those libraries solves a specific requirement and it was carefully picked. Before providing you some basic information for most of them, let me point out something important.

Always pick 3rd libraries carefully. When searching for a specific component you may find several implementations for it out on the internet. One thing to care about is do not end up with a bazooka while trying to kill a mosquito. For example, in this application we need modal popups. If you search on the internet you will find various implementations but most of these are quite complex. All we need is a modal window so for this I decided that the $modal service of angularJS UI-bootstrap is more than enough to do the job. Another thing that you should do is always check for any opened issues for the 3rd library. Those libraries are usually hosted on Github and there is an Issues section that you can view. Check if any performance or memory leaks issue exists before decide to attach any library to your project.

3rd party libraries

  1. toastr.js: A non-blocking notification library
  2. jquery.raty.js: A star rating plug-in
  3. angular-validator.js: A light weighted validation directive
  4. angular-base64.js: Base64 encode-decode library
  5. angular-file-upload.js: A file-uploading library
  6. angucomplete-alt.min.js: Auto-complete search directive
  7. ui-bootstrap-tpls-0.13.1.js: Native AngularJS (Angular) directives for Bootstrap
  8. morris.js: Charts library
  9. jquery.fancybox.js: Tool for displaying images

These are the most important libraries we will be using but you may find some other files too. Download all those files from here. As I mentioned, generally you should use Bower and Grunt or Gulp for resolving web dependencies so let me provide you some installation commands for the above libraries:

bower install angucomplete-alt --save
bower install angular-base64
bower install angular-file-upload
bower install tg-angular-validator
bower install bootstrap
bower install raty
bower install angular-loading-bar
bower install angular-bootstrap

The ng-view

Now that we are done configurating the Single Page Application it’s time to create its initial page, the page that will be used as the parent of all different views and templates in our application. Inside the Controllers folder, create an MVC Controller named HomeController as follow. Right click inside its Index method and create a new view with the respective name.

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

Alter the Views/Home/Index.cshtml file as follow:

@{
    Layout = null;
}

<!DOCTYPE html>

<html ng-app="homeCinema">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home Cinema</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body ng-controller="rootCtrl">
    <top-bar></top-bar>
    <div class="row-offcanvas row-offcanvas-left">
        <side-bar></side-bar>
        <div id="main">
            <div class="col-md-12">
                <p class="visible-xs">
                    <button type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><i class="glyphicon glyphicon-chevron-left"></i></button>
                </p>
                <div class="page {{ pageClass }}" ng-view></div>
            </div>
        </div>
    </div><!--/row-offcanvas -->
    @Scripts.Render("~/bundles/vendors")
    @Scripts.Render("~/bundles/spa")

</body>
</html>

Let’s explain one by one the highlighted lines, from top to bottom. The main module in our spa will be named homeCinema. This module will have two other modules as dependencies, the Common.core and the Common.UI which we will create later. Next we render the CSS bundles we created before. There will be one root-parent controller in the application named rootCtrl. The top-bar and side-bar elements are custom angularJS directives what will build for the top and side bar collapsible menus respectively. We use a page-class $scope variable to change the style in a rendered template. This is a nice trick to render different styles in ng-view templates. Last but not least, we render the vendors and spa JavaScript bundles.

From what we have till now, it’s obvious that first we should create the homeCinema module, the rootCtrl controller then the two custom directives for this initial page to be rendered. Instead of doing this, we ‘ll take a step back and prepare some basic angularJS components, the Common.core and Common.ui modules. Add a Modules folder in the Scripts/spa folder and create the following two files.

(function () {
    'use strict';

    angular.module('common.ui', ['ui.bootstrap', 'chieffancypants.loadingBar']);

})();
(function () {
	'use strict';

	angular.module('common.core', ['ngRoute', 'ngCookies', 'base64', 'angularFileUpload', 'angularValidator', 'angucomplete-alt']);

})();

Let Common.ui module be a UI related reusable component through our SPA application and Common.core a core functional one. We could just inject all those dependencies directly to the homeCinema module but that would require to do the same in case we wanted to scale the application as we ‘ll discuss later. Pay some attention the way we created the modules. We will be using this pattern a lot for not polluting the global JavaScript’s namespace. At the root of the spa folder add the following app.js file and define the core homeCinema module and its routes.

(function () {
    'use strict';

    angular.module('homeCinema', ['common.core', 'common.ui'])
        .config(config);

    config.$inject = ['$routeProvider'];
    function config($routeProvider) {
        $routeProvider
            .when("/", {
                templateUrl: "scripts/spa/home/index.html",
                controller: "indexCtrl"
            })
            .when("/login", {
                templateUrl: "scripts/spa/account/login.html",
                controller: "loginCtrl"
            })
            .when("/register", {
                templateUrl: "scripts/spa/account/register.html",
                controller: "registerCtrl"
            })
            .when("/customers", {
                templateUrl: "scripts/spa/customers/customers.html",
                controller: "customersCtrl"
            })
            .when("/customers/register", {
                templateUrl: "scripts/spa/customers/register.html",
                controller: "customersRegCtrl"
            })
            .when("/movies", {
                templateUrl: "scripts/spa/movies/movies.html",
                controller: "moviesCtrl"
            })
            .when("/movies/add", {
                templateUrl: "scripts/spa/movies/add.html",
                controller: "movieAddCtrl"
            })
            .when("/movies/:id", {
                templateUrl: "scripts/spa/movies/details.html",
                controller: "movieDetailsCtrl"
            })
            .when("/movies/edit/:id", {
                templateUrl: "scripts/spa/movies/edit.html",
                controller: "movieEditCtrl"
            })
            .when("/rental", {
                templateUrl: "scripts/spa/rental/rental.html",
                controller: "rentStatsCtrl"
            }).otherwise({ redirectTo: "/" });
    }

})();

Once again take a look at the explicit service injection using the angularJS property annotation $inject which allows the minifiers to rename the function parameters and still be able to inject the right services. You probably don’t have all the referenced files in the previous script (except if you have downloaded the source code) but that’s OK. We ‘ll create all of those one by one. It is common practice to place any angularJS components related to application’s layout in a layout folder so go ahead and create this folder. For the side-bar element directive we used we need two files, one for the directive definition and another for its template. Add the following two files to the layout folder you created.

(function(app) {
    'use strict';

    app.directive('sideBar', sideBar);

    function sideBar() {
        return {
            restrict: 'E',
            replace: true,
            templateUrl: '/scripts/spa/layout/sideBar.html'
        }
    }

})(angular.module('common.ui'));
<div id="sidebar" class="sidebar-offcanvas">
    <div class="col-md-12">
        <h3></h3>
        <ul class="nav nav-pills nav-stacked">
            <li class="nav-divider"></li>
            <li class=""><a ng-href="#/">Home<i class="fa fa-home fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/customers/">Customers<i class="fa fa-users fa-fw pull-right"></i></a></li>
            <li><a ng-href="#/customers/register">Register customer<i class="fa fa-user-plus fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/movies/">Movies<i class="fa fa-film fa-fw pull-right"></i></a></li>
            <li><a ng-href="#/movies/add">Add movie<i class="fa fa-plus-circle fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/rental/">Rental history<i class="fa fa-leanpub fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/login" ng-if="!userData.isUserLoggedIn">Login<i class="fa fa-sign-in fa-fw pull-right"></i></a></li>
            <li><button type="button" class="btn btn-danger btn-sm" ng-click="logout();" ng-if="userData.isUserLoggedIn">Logout<i class="fa fa-sign-out fa-fw pull-right"></i></a></li>
        </ul>
    </div>
</div>

This directive will create a collapsible side-bar such as the following when applied.
spa-webapi-angular-17
Let’s see the top-bar directive and its template as well.

(function(app) {
    'use strict';

    app.directive('topBar', topBar);

    function topBar() {
        return {
            restrict: 'E',
            replace: true,
            templateUrl: '/scripts/spa/layout/topBar.html'
        }
    }

})(angular.module('common.ui'));
<div class="navbar navbar-default navbar-fixed-top">
    <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand active" href="#/">Home Cinema</a>
    </div>
    <div class="collapse navbar-collapse">
        <ul class="nav navbar-nav">
            <li><a href="#about">About<i class="fa fa-info-circle fa-fw pull-right"></i></a></li>
        </ul>
        <ul class="nav navbar-nav navbar-right" ng-if="userData.isUserLoggedIn">
            <li class="userinfo"><a href="#/">{{username}}<i class="fa fa-user fa-fw"></i></a></li>
        </ul>
    </div>
</div>

You may have noticed I highlighted two lines in the side-bar template’s code. Those lines are responsible to show or hide the login and log-off buttons respectively depending if the user is logged in or not. The required functionality will be place at the rootCtrl Controller inside a home folder.

(function (app) {
    'use strict';

    app.controller('rootCtrl', rootCtrl);

    function rootCtrl($scope) {

        $scope.userData = {};
        
        $scope.userData.displayUserInfo = displayUserInfo;
        $scope.logout = logout;


        function displayUserInfo() {

        }

        function logout() {

        }
    }

})(angular.module('homeCinema'));

We will update its contents as soon as we create the membership service. All of our views require to fetch data from the server and for that a specific apiService will be used through our application. This service will also be able to display some kind of notifications to the user so let’s build a notificationService as well. Create a services folder under the spa and add the following angularJS factory services.

(function (app) {
    'use strict';

    app.factory('notificationService', notificationService);

    function notificationService() {

        toastr.options = {
            "debug": false,
            "positionClass": "toast-top-right",
            "onclick": null,
            "fadeIn": 300,
            "fadeOut": 1000,
            "timeOut": 3000,
            "extendedTimeOut": 1000
        };

        var service = {
            displaySuccess: displaySuccess,
            displayError: displayError,
            displayWarning: displayWarning,
            displayInfo: displayInfo
        };

        return service;

        function displaySuccess(message) {
            toastr.success(message);
        }

        function displayError(error) {
            if (Array.isArray(error)) {
                error.forEach(function (err) {
                    toastr.error(err);
                });
            } else {
                toastr.error(error);
            }
        }

        function displayWarning(message) {
            toastr.warning(message);
        }

        function displayInfo(message) {
            toastr.info(message);
        }

    }

})(angular.module('common.core'));

The notificationService is based on the toastr.js notification library. It displays different type (style class) of notifications depending on the method invoked, that is success, error, warning and info.

(function (app) {
    'use strict';

    app.factory('apiService', apiService);

    apiService.$inject = ['$http', '$location', 'notificationService','$rootScope'];

    function apiService($http, $location, notificationService, $rootScope) {
        var service = {
            get: get,
            post: post
        };

        function get(url, config, success, failure) {
            return $http.get(url, config)
                    .then(function (result) {
                        success(result);
                    }, function (error) {
                        if (error.status == '401') {
                            notificationService.displayError('Authentication required.');
                            $rootScope.previousState = $location.path();
                            $location.path('/login');
                        }
                        else if (failure != null) {
                            failure(error);
                        }
                    });
        }

        function post(url, data, success, failure) {
            return $http.post(url, data)
                    .then(function (result) {
                        success(result);
                    }, function (error) {
                        if (error.status == '401') {
                            notificationService.displayError('Authentication required.');
                            $rootScope.previousState = $location.path();
                            $location.path('/login');
                        }
                        else if (failure != null) {
                            failure(error);
                        }
                    });
        }

        return service;
    }

})(angular.module('common.core'));

The apiService is quite straight forward. It defines a factory with two basic methods, GET and POST. Both of these methods can handle 401 errors by redirecting the user at the login view and saving the previous state so that after a successful login, the user gets back where he/she was. They also accept a required success callback to invoke and an optional failure one in case of a failed request. Our spa application is pretty much ready to fetch and post data from the server so this is the right time to write the first Web API controller.

Web API

We ‘ll try to apply some basic rules for all of Web API Controllers in our application. The first one is that all of them will inherit from a base class named ApiControllerBase. The basic responsibility of this class will be handling the Error logging functionality. That’s the only class where instances of IEntityBaseRepository<Error> will be injected with the centralized Try, Catch point we talked about at the start of this post. This class of course will inherit from the ApiController. Create a folder named core inside the Infrastructure and create the base class for our controllers.

 public class ApiControllerBase : ApiController
    {
        protected readonly IEntityBaseRepository<Error> _errorsRepository;
        protected readonly IUnitOfWork _unitOfWork;

        public ApiControllerBase(IEntityBaseRepository<Error> errorsRepository, IUnitOfWork unitOfWork)
        {
            _errorsRepository = errorsRepository;
            _unitOfWork = unitOfWork;
        }

        protected HttpResponseMessage CreateHttpResponse(HttpRequestMessage request, Func<HttpResponseMessage> function)
        {
            HttpResponseMessage response = null;

            try
            {
                response = function.Invoke();
            }
            catch (DbUpdateException ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.BadRequest, ex.InnerException.Message);
            }
            catch (Exception ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }

            return response;
        }

        private void LogError(Exception ex)
        {
            try
            {
                Error _error = new Error()
                {
                    Message = ex.Message,
                    StackTrace = ex.StackTrace,
                    DateCreated = DateTime.Now
                };

                _errorsRepository.Add(_error);
                _unitOfWork.Commit();
            }
            catch { }
        }
    }

You will surprised how powerful the CreateHttpResponse function can be when we reach the discussion section. Notice that this method can handle a DbUpdateException exception as well. You can omit this type if you want and write more custom methods such as this. Each controller’s action will start by calling this base method.

If you recall, the home’s page displays the latest movies released plus some genre statistics on the right. Let’s start from the latest movies. First thing we need to do is create a ViewModel for Movie entities. For each type of Entity we ‘ll create the respective ViewModel for the client. All ViewModels will have the relative validation rules based on the FluentValidation Nuget package. Add the MovieViewModel and GenreViewModel classes inside the Models folder.

    [Bind(Exclude = "Image")]
    public class MovieViewModel : IValidatableObject
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Image { get; set; }
        public string  Genre { get; set; }
        public int GenreId { get; set; }
        public string Director { get; set; }
        public string Writer { get; set; }
        public string Producer { get; set; }
        public DateTime ReleaseDate { get; set; }
        public byte Rating { get; set; }
        public string TrailerURI { get; set; }
        public bool IsAvailable { get; set; }
        public int NumberOfStocks { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new MovieViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }

We excluded the Image property from MovieViewModel binding cause we will be using a specific FileUpload action to upload images.

public class GenreViewModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int NumberOfMovies { get; set; }
    }

Create a Validators folder inside the Infrastructure and the MovieViewModelValidator.

public class MovieViewModelValidator : AbstractValidator<MovieViewModel>
    {
        public MovieViewModelValidator()
        {
            RuleFor(movie => movie.GenreId).GreaterThan(0)
                .WithMessage("Select a Genre");

            RuleFor(movie => movie.Director).NotEmpty().Length(1,100)
                .WithMessage("Select a Director");

            RuleFor(movie => movie.Writer).NotEmpty().Length(1,50)
                .WithMessage("Select a writer");

            RuleFor(movie => movie.Producer).NotEmpty().Length(1, 50)
                .WithMessage("Select a producer");

            RuleFor(movie => movie.Description).NotEmpty()
                .WithMessage("Select a description");

            RuleFor(movie => movie.Rating).InclusiveBetween((byte)0, (byte)5)
                .WithMessage("Rating must be less than or equal to 5");

            RuleFor(movie => movie.TrailerURI).NotEmpty().Must(ValidTrailerURI)
                .WithMessage("Only Youtube Trailers are supported");
        }

        private bool ValidTrailerURI(string trailerURI)
        {
            return (!string.IsNullOrEmpty(trailerURI) && trailerURI.ToLower().StartsWith("https://www.youtube.com/watch?"));
        }
    }

Now that we have our first ViewModels and its a validator setup, we can configure the Automapper mappings as well. Add a Mappings folder inside the Infrastructure and create the following DomainToViewModelMappingProfile Profile class.

public class DomainToViewModelMappingProfile : Profile
    {
        public override string ProfileName
        {
            get { return "DomainToViewModelMappings"; }
        }

        protected override void Configure()
        {
            Mapper.CreateMap<Movie, MovieViewModel>()
                .ForMember(vm => vm.Genre, map => map.MapFrom(m => m.Genre.Name))
                .ForMember(vm => vm.GenreId, map => map.MapFrom(m => m.Genre.ID))
                .ForMember(vm => vm.IsAvailable, map => map.MapFrom(m => m.Stocks.Any(s => s.IsAvailable)))
                .ForMember(vm => vm.NumberOfStocks, map => map.MapFrom(m => m.Stocks.Count))
                .ForMember(vm => vm.Image, map => map.MapFrom(m => string.IsNullOrEmpty(m.Image) == true ? "unknown.jpg" : m.Image));

            Mapper.CreateMap<Genre, GenreViewModel>()
                .ForMember(vm => vm.NumberOfMovies, map => map.MapFrom(g => g.Movies.Count()));
        }
    }

Notice how we set if a Movie (ViewModel) is available or not by checking if any of its stocks is available. Add Automapper’s configuration class and make sure to comment out the respective line in the Bootstrapper class.

public class AutoMapperConfiguration
    {
        public static void Configure()
        {
            Mapper.Initialize(x =>
            {
                x.AddProfile<DomainToViewModelMappingProfile>();
            });
        }
    }
public static void Run()
        {
            // Configure Autofac
            AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
            //Configure AutoMapper
            AutoMapperConfiguration.Configure();
        }

We have done so much preparation and now it is time to implement and view all application requirements in practice. We will start with the Home page.

The Home page

The home page displays information about the latest DVD movie released plus some Genre statistics. For the first feature will start from creating the required Web API controller, MoviesController. Add this class inside the Controllers folder.

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/movies")]
    public class MoviesController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Movie> _moviesRepository;

        public MoviesController(IEntityBaseRepository<Movie> moviesRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _moviesRepository = moviesRepository;
        }

        [AllowAnonymous]
        [Route("latest")]
        public HttpResponseMessage Get(HttpRequestMessage request)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                var movies = _moviesRepository.GetAll().OrderByDescending(m => m.ReleaseDate).Take(6).ToList();

                IEnumerable<MovieViewModel> moviesVM = Mapper.Map<IEnumerable<Movie>, IEnumerable<MovieViewModel>>(movies);

                response = request.CreateResponse<IEnumerable<MovieViewModel>>(HttpStatusCode.OK, moviesVM);

                return response;
            });
        }
    }

Let’s explain the highlighted lines from top to bottom. All actions for this Controller required the user not only to be authenticated but also belong to Admin role, except if AllowAnonymous attribute is applied. All requests to this controller will start with a prefix of api/movies. The error handling as already explained is handled from the base class ApiControllerBase and its method CreateHttpResponse. Here we can see for the first time how this method is actually called. Add the GenresController as well.

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/genres")]
    public class GenresController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Genre> _genresRepository;

        public GenresController(IEntityBaseRepository<Genre> genresRepository,
             IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _genresRepository = genresRepository;
        }

        [AllowAnonymous]
        public HttpResponseMessage Get(HttpRequestMessage request)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                var genres = _genresRepository.GetAll().ToList();

                IEnumerable<GenreViewModel> genresVM = Mapper.Map<IEnumerable<Genre>, IEnumerable<GenreViewModel>>(genres);

                response = request.CreateResponse<IEnumerable<GenreViewModel>>(HttpStatusCode.OK, genresVM);

                return response;
            });
        }
    }

We prepared the server side part, let’s move on to its JavaScript one now. If you recall we ‘ll follow a structure by feature in our spa, so we will place the two required files for the home page inside the spa/home folder. We need two files, one template and the respective controller. Let’s see the template first.

<hr />
<div class="row">
    <div class="col-md-8">
        <div class="panel panel-primary" id="panelLatestMovies">
            <div class="panel-heading">
                <h3 class="panel-title">Latest Movies Released</h3>
            </div>

            <div class="panel-body">
                <div ng-if="loadingMovies">
                    <div class="col-xs-4"></div>
                    <div class="col-xs-4">
                        <i class="fa fa-refresh fa-5x fa-spin"></i> <label class="label label-primary">Loading movies...</label>
                    </div>
                    <div class="col-xs-4"></div>
                </div>
                <div class="col-xs-12 col-sm-6 movie" ng-repeat="movie in latestMovies">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <strong>{{movie.Title}} </strong>
                        </div>
                        <div class="panel-body">
                            <div class="media">
                                <a class="pull-left" href="#">
                                    <a class="fancybox pull-left" rel="gallery1" ng-href="../../Content/images/movies/{{movie.Image}}" title="{{movie.Description | limitTo:200}}">
                                        <img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
                                    </a>

                                </a>
                                <div class="media-body">
                                    <available-movie is-available="{{movie.IsAvailable}}"></available-movie>
                                    <div><small>{{movie.Description | limitTo: 70}}...</small></div>
                                    <label class="label label-info">{{movie.Genre}}</label><br />
                                </div>
                                <br />
                            </div>
                        </div>
                        <div class="panel-footer">
                            <span component-rating="{{movie.Rating}}"></span>
                            <a class="fancybox-media pull-right" ng-href="{{movie.TrailerURI}}">Trailer<i class="fa fa-video-camera fa-fw"></i></a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="panel panel-success" id="panelMovieGenres">
            <div class="panel-heading">
                <h3 class="panel-title">Movies Genres</h3>
            </div>

            <div class="panel-body">
                <div ng-if="loadingGenres">
                    <div class="col-xs-4"></div>
                    <div class="col-xs-4"><i class="fa fa-refresh fa-5x fa-spin"></i> <label class="label label-primary">Loading Genres..</label></div>
                    <div class="col-xs-4"></div>
                </div>
                <div id="genres-bar"></div>
            </div>
            <div class="panel-footer">
                <p class="text-center"><em>Wanna add a new Movie? Head over to the add movie form.</em></p>
                <p class="text-center"><a ng-href="#/movies/add" class="btn btn-default">Add new Movie</a></p>
            </div>
        </div>
    </div>
</div>

I made a convention that the first view rendered for each template will be named index.html. This means that you will see later the movies/index.html, rental/index.html etc.. We use ng-if angularJS directive to display a loader (spinner if you prefer) till server side data retrieved from the server. Let’s see now the controller that binds the data to the template, the indexCtrl. Add the following file to the home folder as well.

(function (app) {
    'use strict';

    app.controller('indexCtrl', indexCtrl);

    indexCtrl.$inject = ['$scope', 'apiService', 'notificationService'];

    function indexCtrl($scope, apiService, notificationService) {
        $scope.pageClass = 'page-home';
        $scope.loadingMovies = true;
        $scope.loadingGenres = true;
        $scope.isReadOnly = true;

        $scope.latestMovies = [];
        $scope.loadData = loadData;

        function loadData() {
            apiService.get('/api/movies/latest', null,
                        moviesLoadCompleted,
                        moviesLoadFailed);

            apiService.get("/api/genres/", null,
                genresLoadCompleted,
                genresLoadFailed);
        }

        function moviesLoadCompleted(result) {
            $scope.latestMovies = result.data;
            $scope.loadingMovies = false;
        }

        function genresLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function moviesLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function genresLoadCompleted(result) {
            var genres = result.data;
            Morris.Bar({
                element: "genres-bar",
                data: genres,
                xkey: "Name",
                ykeys: ["NumberOfMovies"],
                labels: ["Number Of Movies"],
                barRatio: 0.4,
                xLabelAngle: 55,
                hideHover: "auto",
                resize: 'true'
            });

            $scope.loadingGenres = false;
        }

        loadData();
    }

})(angular.module('homeCinema'));

The loadData() function requests movie and genre data from the respective Web API controllers we previously created. For the movie data only thing needed to do is bind the requested data to a $scope.latestMovies variable. For the genres data thought, we used genres retrieved data and a specific div element, genres-bar to create a Morris bar.

Movie Directives

In case you noticed, the index.html template has two custom directives. One to render if the movie is available and another one to display its rating through the raty.js library. Those two directives will be used over and over again through our application so let’s take a look at them.

The first one is responsible to render a label element that may be red or green depending if the a movie is available or not. Since those directives can be used all over the application we ‘ll place their components inside a directives folder, so go ahead and add this folder under the spa. Create a availableMovie.html file which will be the template for the new directive.

<label ng-class="getAvailableClass()">{{getAvailability()}}</label>

Now add the directive definition, availableMovie.directive.js

(function (app) {
	'use strict';

	app.directive('availableMovie', availableMovie);

	function availableMovie() {
		return {
			restrict: 'E',
			templateUrl: "/Scripts/spa/directives/availableMovie.html",
			link: function ($scope, $element, $attrs) {
				$scope.getAvailableClass = function () {
					if ($attrs.isAvailable === 'true')
						return 'label label-success'
					else
						return 'label label-danger'
				};
				$scope.getAvailability = function () {
					if ($attrs.isAvailable === 'true')
						return 'Available!'
					else
						return 'Not Available'
				};
			}
		}
	}

})(angular.module('common.ui'));

spa-webapi-angular-18
The component-rating which displays a star based rating element, is slightly different in terms of restriction, since it is used as an element, not as an attribute. I named it component-rating cause you may want to use it to rate entities other than movies. When you want to render the rating directive all you have to do is create the following element.

<span component-rating="{{movie.Rating}}"></span>

The movie.Rating will hold the rating value. Let’s see the directive’s definition. Place the following file in the directives folder as well.

(function(app) {
    'use strict';

    app.directive('componentRating', componentRating);

    function componentRating() {
        return {
            restrict: 'A',
            link: function ($scope, $element, $attrs) {
                $element.raty({
                    score: $attrs.componentRating,
                    halfShow: false,
                    readOnly: $scope.isReadOnly,
                    noRatedMsg: "Not rated yet!",
                    starHalf: "../Content/images/raty/star-half.png",
                    starOff: "../Content/images/raty/star-off.png",
                    starOn: "../Content/images/raty/star-on.png",
                    hints: ["Poor", "Average", "Good", "Very Good", "Excellent"],
                    click: function (score, event) {
                        //Set the model value
                        $scope.movie.Rating = score;
                        $scope.$apply();
                    }
                });
            }
        }
    }

})(angular.module('common.ui'));

One important thing the directive needs to know is if the rating element will be editable or not and that is configured through the readOnly: $scope.isReadOnly definition. For the home/index.html we want the rating to be read-only so the controller has the following declaration:

$scope.isReadOnly = true;

Any other controller that requires to edit movie’s rating value will set this value to false.

Account

One of the most important parts in every application is how users are getting authenticated in order to access authorized resources. We certainly built a custom membership schema and add an Basic Authentication message handler in Web API, but we haven’t yet created neither the required Web API AccountController or the relative angularJS component. Let’s start with the server side first and view the AccountController.

    [Authorize(Roles="Admin")]
    [RoutePrefix("api/Account")]
    public class AccountController : ApiControllerBase
    {
        private readonly IMembershipService _membershipService;

        public AccountController(IMembershipService membershipService,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _membershipService = membershipService;
        }

        [AllowAnonymous]
        [Route("authenticate")]
        [HttpPost]
        public HttpResponseMessage Login(HttpRequestMessage request, LoginViewModel user)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (ModelState.IsValid)
                {
                    MembershipContext _userContext = _membershipService.ValidateUser(user.Username, user.Password);

                    if (_userContext.User != null)
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = true });
                    }
                    else
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = false });
                    }
                }
                else
                    response = request.CreateResponse(HttpStatusCode.OK, new { success = false });

                return response;
            });
        }

        [AllowAnonymous]
        [Route("register")]
        [HttpPost]
        public HttpResponseMessage Register(HttpRequestMessage request, RegistrationViewModel user)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateResponse(HttpStatusCode.BadRequest, new { success = false });
                }
                else
                {
                    Entities.User _user = _membershipService.CreateUser(user.Username, user.Email, user.Password, new int[] { 1 });

                    if (_user != null)
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = true });
                    }
                    else
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = false });
                    }
                }

                return response;
            });
        }
    }

User send a POST request to api/account/authenticate with their credentials (we ‘ll view the LoginViewModel soon) and the controller validates the user though the MemebershipService. If user’s credentials are valid then the returned MembershipContext will contain the relative user’s User entity. The registration process works pretty much the same. This time the user posts a request to api/account/register (we ‘ll view the RegistrationViewModel later) and if the ModelState is valid then the user is created through the MemebershipService’s CreateUser method. Let’s see now both the LoginViewModel and the RegistrationViewModel with their respective validators. Add the ViewModel classes in the Models folder and a AccountViewModelValidators.cs file inside the Infrastructure/Validators folder to hold both their validators.

public class LoginViewModel : IValidatableObject
    {
        public string Username { get; set; }
        public string Password { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new LoginViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }
public class RegistrationViewModel : IValidatableObject
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new RegistrationViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }
public class RegistrationViewModelValidator : AbstractValidator<RegistrationViewModel>
    {
        public RegistrationViewModelValidator()
        {
            RuleFor(r => r.Email).NotEmpty().EmailAddress()
                .WithMessage("Invalid email address");

            RuleFor(r => r.Username).NotEmpty()
                .WithMessage("Invalid username");

            RuleFor(r => r.Password).NotEmpty()
                .WithMessage("Invalid password");
        }
    }

    public class LoginViewModelValidator : AbstractValidator<LoginViewModel>
    {
        public LoginViewModelValidator()
        {
            RuleFor(r => r.Username).NotEmpty()
                .WithMessage("Invalid username");

            RuleFor(r => r.Password).NotEmpty()
                .WithMessage("Invalid password");
        }
    }

In the Front-End side now, we need to build a MembershipService to handle the following:

MembershipService factory

  1. Authenticate user through the Login view
  2. Register a user through the Register view
  3. Save user’s credentials after successful login or registration in a session cookie ($cookieStore)
  4. Remove credentials when user log-off from application
  5. Checks if user is logged in or not through the relative $cookieStore repository value

This factory service is mostly depending in the $cookieStore service (ngCookies module) and in a 3rd party module named ‘$base64’ able to encode – decode strings in base64 format. Logged in user’s credentials are saved in a $rootScope variable and added as Authorization header in a each http request. Add the membershipService.js file inside the spa/services folder.

(function (app) {
    'use strict';

    app.factory('membershipService', membershipService);

    membershipService.$inject = ['apiService', 'notificationService','$http', '$base64', '$cookieStore', '$rootScope'];

    function membershipService(apiService, notificationService, $http, $base64, $cookieStore, $rootScope) {

        var service = {
            login: login,
            register: register,
            saveCredentials: saveCredentials,
            removeCredentials: removeCredentials,
            isUserLoggedIn: isUserLoggedIn
        }

        function login(user, completed) {
            apiService.post('/api/account/authenticate', user,
            completed,
            loginFailed);
        }

        function register(user, completed) {
            apiService.post('/api/account/register', user,
            completed,
            registrationFailed);
        }

        function saveCredentials(user) {
            var membershipData = $base64.encode(user.username + ':' + user.password);

            $rootScope.repository = {
                loggedUser: {
                    username: user.username,
                    authdata: membershipData
                }
            };

            $http.defaults.headers.common['Authorization'] = 'Basic ' + membershipData;
            $cookieStore.put('repository', $rootScope.repository);
        }

        function removeCredentials() {
            $rootScope.repository = {};
            $cookieStore.remove('repository');
            $http.defaults.headers.common.Authorization = '';
        };

        function loginFailed(response) {
            notificationService.displayError(response.data);
        }

        function registrationFailed(response) {

            notificationService.displayError('Registration failed. Try again.');
        }

        function isUserLoggedIn() {
            return $rootScope.repository.loggedUser != null;
        }

        return service;
    }



})(angular.module('common.core'));

Now that we built this service we are able to handle page refreshes as well. Go ahead and add the run configuration for the main module homeCiname inside the app.js file.

(function () {
    'use strict';

    angular.module('homeCinema', ['common.core', 'common.ui'])
        .config(config)
        .run(run);
    // routeProvider code ommited

    run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];

    function run($rootScope, $location, $cookieStore, $http) {
        // handle page refreshes
        $rootScope.repository = $cookieStore.get('repository') || {};
        if ($rootScope.repository.loggedUser) {
            $http.defaults.headers.common['Authorization'] = $rootScope.repository.loggedUser.authdata;
        }

        $(document).ready(function () {
            $(".fancybox").fancybox({
                openEffect: 'none',
                closeEffect: 'none'
            });

            $('.fancybox-media').fancybox({
                openEffect: 'none',
                closeEffect: 'none',
                helpers: {
                    media: {}
                }
            });

            $('[data-toggle=offcanvas]').click(function () {
                $('.row-offcanvas').toggleClass('active');
            });
        });
    }

})();

I found the opportunity to add same fancy-box related initialization code as well. Now that we have the membership functionality configured both for the server and the front-end side,
let’s proceed to the login and register views with their controllers. Those angularJS components will be placed inside an account folder in the spa. Here is the login.html template:

<div class="row">
    <form class="form-signin" role="form" novalidate angular-validator name="userLoginForm" angular-validator-submit="login()">
        <h3 class="form-signin-heading">Sign in</h3>
        <div class="form-group">
            <label for="inputUsername" class="sr-only">Username</label>
            <input type="text" id="inputUsername" name="inputUsername" class="form-control" ng-model="user.username" placeholder="Username"
                   validate-on="blur" required required-message="'Username is required'">
        </div>
        <div class="form-group">
            <label for="inputPassword" class="sr-only">Password</label>
            <input type="password" id="inputPassword" name="inputPassword" class="form-control" ng-model="user.password" placeholder="Password"
                   validate-on="blur" required required-message="'Password is required'">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
        <div class="pull-right">
            <a ng-href="#/register" class="control-label">Register</a>
        </div>
    </form>
</div>

Here you can see (highlighted lines) for the first time a new library we will be using for validating form controls, the angularValidator.
spa-webapi-angular-19
And now the loginCtrl controller.

(function (app) {
    'use strict';

    app.controller('loginCtrl', loginCtrl);

    loginCtrl.$inject = ['$scope', 'membershipService', 'notificationService','$rootScope', '$location'];

    function loginCtrl($scope, membershipService, notificationService, $rootScope, $location) {
        $scope.pageClass = 'page-login';
        $scope.login = login;
        $scope.user = {};

        function login() {
            membershipService.login($scope.user, loginCompleted)
        }

        function loginCompleted(result) {
            if (result.data.success) {
                membershipService.saveCredentials($scope.user);
                notificationService.displaySuccess('Hello ' + $scope.user.username);
                $scope.userData.displayUserInfo();
                if ($rootScope.previousState)
                    $location.path($rootScope.previousState);
                else
                    $location.path('/');
            }
            else {
                notificationService.displayError('Login failed. Try again.');
            }
        }
    }

})(angular.module('common.core'));

The login function calls the membershipService’s login and passes a success callback. If the login succeed it does three more things: First, it saves user’s credentials through membershipService and then displays logged-in user’s info through the rootCtrl controller. Finally, checks if the user ended in login view cause authentication required to access another view and if so, redirects him/hem to that view. Let me remind you a small part of the apiService.

if (error.status == '401') {
      notificationService.displayError('Authentication required.');
      $rootScope.previousState = $location.path();
       $location.path('/login');
  }

The register.html template and its respective controller work in exactly the same way.

<div class="row">
    <form class="form-signin" role="form" novalidate angular-validator name="userRegistrationForm" angular-validator-submit="register()">
        <h3 class="form-signin-heading">Register <label class="label label-danger">Admin</label></h3>
        <div class="form-group">
            <label for="inputUsername" class="">Username</label>
            <input type="text" name="inputUsername" class="form-control" ng-model="user.username" 
                   placeholder="Username" validate-on="blur" required required-message="'Username is required'">
        </div>
        <div class="form-group">
            <label for="inputPassword">Password</label>
            <input type="password" name="inputPassword" class="form-control" ng-model="user.password" placeholder="Password"
                    validate-on="blur" required required-message="'Password is required'">
        </div>
        <div class="form-group">
            <label for="inputEmail">Email</label>
            <input type="email" name="inputEmail" class="form-control" ng-model="user.email" placeholder="Email address"
                   validate-on="blur" required required-message="'Email is required'">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
    </form>
</div>
(function (app) {
    'use strict';

    app.controller('registerCtrl', registerCtrl);

    registerCtrl.$inject = ['$scope', 'membershipService', 'notificationService', '$rootScope', '$location'];

    function registerCtrl($scope, membershipService, notificationService, $rootScope, $location) {
        $scope.pageClass = 'page-login';
        $scope.register = register;
        $scope.user = {};

        function register() {
            membershipService.register($scope.user, registerCompleted)
        }

        function registerCompleted(result) {
            if (result.data.success) {
                membershipService.saveCredentials($scope.user);
                notificationService.displaySuccess('Hello ' + $scope.user.username);
                $scope.userData.displayUserInfo();
                $location.path('/');
            }
            else {
                notificationService.displayError('Registration failed. Try again.');
            }
        }
    }

})(angular.module('common.core'));

spa-webapi-angular-20

Customers

The Customers feature is consisted by 2 views in our SPA application. The first view is responsible to display all customers. It also supports pagination, filtering current view data and start a new server search. The second one is the registration view where an employee can register a new customer. We will start from the server side required components first and then with the front-end as we did before. Add a new CustomersController Web API controller inside the controllers folder.

    [Authorize(Roles="Admin")]
    [RoutePrefix("api/customers")]
    public class CustomersController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Customer> _customersRepository;

        public CustomersController(IEntityBaseRepository<Customer> customersRepository, 
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
    }

First feature we want to support is the pagination with an optional filter search parameter. For this to work, the data returned by the Web API action must also include some pagination related information so that the front-end components can re-build the paginated list. Add the following generic PaginationSet class inside the Infrastructure/core folder.

public class PaginationSet<T>
    {
        public int Page { get; set; }

        public int Count
        {
            get
            {
                return (null != this.Items) ? this.Items.Count() : 0;
            }
        }

        public int TotalPages { get; set; }
        public int TotalCount { get; set; }

        public IEnumerable<T> Items { get; set; }
    }

This class holds the list of items we want to render plus all the pagination information we need to build a paginated list at the front side. Customer entity will have its own ViewModel so let’s create the CustomerViewModel and its validator. I assume that at this point you know where to place the following files.

   [Bind(Exclude = "UniqueKey")]
    public class CustomerViewModel : IValidatableObject
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string IdentityCard { get; set; }
        public Guid UniqueKey { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Mobile { get; set; }
        public DateTime RegistrationDate { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new CustomerViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }

I have excluded the UniqueKey property from binding since that’s a value to be created on server side.

public class CustomerViewModelValidator : AbstractValidator<CustomerViewModel>
    {
        public CustomerViewModelValidator()
        {
            RuleFor(customer => customer.FirstName).NotEmpty()
                .Length(1, 100).WithMessage("First Name must be between 1 - 100 characters");

            RuleFor(customer => customer.LastName).NotEmpty()
                .Length(1, 100).WithMessage("Last Name must be between 1 - 100 characters");

            RuleFor(customer => customer.IdentityCard).NotEmpty()
                .Length(1, 100).WithMessage("Identity Card must be between 1 - 50 characters");

            RuleFor(customer => customer.DateOfBirth).NotNull()
                .LessThan(DateTime.Now.AddYears(-16))
                .WithMessage("Customer must be at least 16 years old.");

            RuleFor(customer => customer.Mobile).NotEmpty().Matches(@"^\d{10}$")
                .Length(10).WithMessage("Mobile phone must have 10 digits");

            RuleFor(customer => customer.Email).NotEmpty().EmailAddress()
                .WithMessage("Enter a valid Email address");

        }
    }

Don’t forget to add the Automapper mapping from Customer to CustomerViewModel so switch to DomainToViewModelMappingProfile and add the following line inside the Configure() function.

Mapper.CreateMap<Customer, CustomerViewModel>();

Now we can go to CustomersController and create the Search method.

We will continue with the front-end required angularJS components. As opposed from the routes we defined in the app.js, we need two files to display the customers view, the customers.html template and a customersCtrl controller inside a customersCtrl.js file.

.when("/customers", {
        templateUrl: "scripts/spa/customers/customers.html",
         controller: "customersCtrl"
    })

Go ahead and add a customers folder inside the spa and create the following customers.html template.

<div class="row">
    <div class="panel panel-primary">
        <div class="panel-heading clearfix">
            <h4 class="panel-title pull-left" style="padding-top: 7.5px;">Home Cinema Customers</h4>
            <div class="input-group">
                <input id="inputSearchCustomers" type="search" ng-model="filterCustomers" class="form-control shortInputSearch" placeholder="Filter, search customers..">
                <div class="input-group-btn">
                    <button class="btn btn-primary" ng-click="search();"><i class="glyphicon glyphicon-search"></i></button>
                    <button class="btn btn-primary" ng-click="clearSearch();"><i class="glyphicon glyphicon-remove-sign"></i></button>
                </div>
            </div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div class="col-sm-6" ng-repeat="customer in Customers | filter:filterCustomers">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <strong>{{customer.FirstName}} {{customer.LastName}}</strong><br/>
                            
                        </div>
                        <div class="panel-body">
                            <div class="table-responsive">
                                <table class="table table-condensed shortMargin">
                                    <tr>
                                        <td class="shortPadding">Email:</td>
                                        <td class="shortPadding"><i>{{customer.Email}}</i></td>
                                    </tr>
                                    <tr>
                                        <td class="shortPadding">Mobile:</td>
                                        <td class="shortPadding"><i>{{customer.Mobile}}</i></td>
                                    </tr>
                                    <tr>
                                        <td class="shortPadding">Birth:</td>
                                        <td class="shortPadding"><i>{{customer.DateOfBirth | date:'mediumDate'}}</i></td>
                                    </tr>
                                    <tr>
                                        <td class="shortPadding">Registered:</td>
                                        <td class="shortPadding"><i>{{customer.RegistrationDate | date:'mediumDate'}}</i></td>
                                    </tr>
                                </table>
                            </div>
                        </div>
                        <div class="panel-footer clearfix">
                            <label class="label label-danger">{{customer.IdentityCard}}</label>
                            <div class="pull-right">
                                <buton class="btn btn-primary btn-xs" ng-click="openEditDialog(customer);">Edit <i class="fa fa-pencil pull-right"></i></buton>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="panel-footer">
            <div class="text-center">
                <custom-pager page="{{page}}" pages-count="{{pagesCount}}" total-count="{{totalCount}}" search-func="search(page)"></custom-pager>
            </div>
        </div>
    </div>
</div>

The most important highlighted line is the last one where we build a custom pager element. The directive we are going to add is responsible to render a paginated list depending on pagination information retrieved from the server (page, pages-count, total-count). Add the following pager.html template and its definition directive inside the layout folder.

<div>
    <div ng-hide="(!pagesCount || pagesCount < 2)" style="display:inline">
        <ul class="pagination pagination-sm">
            <li><a ng-hide="page == 0" ng-click="search(0)"><<</a></li>
            <li><a ng-hide="page == 0" ng-click="search(page-1)"><</a></li>
            <li ng-repeat="n in range()" ng-class="{active: n == page}">
                <a ng-click="search(n)" ng-if="n != page">{{n+1}}</a>
                <span ng-if="n == page">{{n+1}}</span>
            </li>
            <li><a ng-hide="page == pagesCount - 1" ng-click="search(pagePlus(1))">></a></li>
            <li><a ng-hide="page == pagesCount - 1" ng-click="search(pagesCount - 1)">>></a></li>
        </ul>
    </div>
</div>
(function(app) {
    'use strict';

    app.directive('customPager', customPager);

    function customPager() {
        return {
            scope: {
                page: '@',
                pagesCount: '@',
                totalCount: '@',
                searchFunc: '&',
                customPath: '@'
            },
            replace: true,
            restrict: 'E',
            templateUrl: '/scripts/spa/layout/pager.html',
            controller: ['$scope', function ($scope) {
                $scope.search = function (i) {
                    if ($scope.searchFunc) {
                        $scope.searchFunc({ page: i });
                    }
                };

                $scope.range = function () {
                    if (!$scope.pagesCount) { return []; }
                    var step = 2;
                    var doubleStep = step * 2;
                    var start = Math.max(0, $scope.page - step);
                    var end = start + 1 + doubleStep;
                    if (end > $scope.pagesCount) { end = $scope.pagesCount; }

                    var ret = [];
                    for (var i = start; i != end; ++i) {
                        ret.push(i);
                    }

                    return ret;
                };

                $scope.pagePlus = function(count)
                {
                    console.log($scope.page);
                    return +$scope.page + count;
                }
            }]
        }
    }

})(angular.module('homeCinema'));

spa-webapi-angular-02
Now let’s see the customersCtrl controller. This controller is responsible to retrieve data from Web API and start a new search if the user presses the magnify button next to the textbox. More over it’s the one that will open a modal popup window when the employee decides to edit a specific customer. For this popup window we will use the angular-ui $modal service.

(function (app) {
    'use strict';

    app.controller('customersCtrl', customersCtrl);

    customersCtrl.$inject = ['$scope','$modal', 'apiService', 'notificationService'];

    function customersCtrl($scope, $modal, apiService, notificationService) {

        $scope.pageClass = 'page-customers';
        $scope.loadingCustomers = true;
        $scope.page = 0;
        $scope.pagesCount = 0;
        $scope.Customers = [];

        $scope.search = search;
        $scope.clearSearch = clearSearch;

        $scope.search = search;
        $scope.clearSearch = clearSearch;
        $scope.openEditDialog = openEditDialog;

        function search(page) {
            page = page || 0;

            $scope.loadingCustomers = true;

            var config = {
                params: {
                    page: page,
                    pageSize: 4,
                    filter: $scope.filterCustomers
                }
            };

            apiService.get('/api/customers/search/', config,
            customersLoadCompleted,
            customersLoadFailed);
        }

        function openEditDialog(customer) {
            $scope.EditedCustomer = customer;
            $modal.open({
                templateUrl: 'scripts/spa/customers/editCustomerModal.html',
                controller: 'customerEditCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                clearSearch();
            }, function () {
            });
        }

        function customersLoadCompleted(result) {
            $scope.Customers = result.data.Items;

            $scope.page = result.data.Page;
            $scope.pagesCount = result.data.TotalPages;
            $scope.totalCount = result.data.TotalCount;
            $scope.loadingCustomers = false;

            if ($scope.filterCustomers && $scope.filterCustomers.length) {
                notificationService.displayInfo(result.data.Items.length + ' customers found');
            }

        }

        function customersLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function clearSearch() {
            $scope.filterCustomers = '';
            search();
        }

        $scope.search();
    }

})(angular.module('homeCinema'));

Let’s focus on the following part of the customersCtrl controller where the modal window pops up.

function openEditDialog(customer) {
            $scope.EditedCustomer = customer;
            $modal.open({
                templateUrl: 'scripts/spa/customers/editCustomerModal.html',
                controller: 'customerEditCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                clearSearch();
            }, function () {
            });
        }

When we decide to edit a customer we don’t have to request any data from the server. We have them already and we can pass them to the customerEditCtrl through the $scope

$scope.EditedCustomer = customer;

The popup window isn’t a new view to be rendered but a single pop-up window with a template and a custom controller. Let’s view both of those components, the editCustomerModal.html template and the customerEditCtrl controller.

<div class="panel panel-primary">
    <div class="panel-heading">
        Edit {{EditedCustomer.FirstName}} {{EditedCustomer.LastName}}
    </div>
    <div class="panel-body">
        <form role="form" novalidate angular-validator name="addCustomerForm" angular-validator-submit="Register()">
            <div class="">
                <div class="form-group">
                    <div class="row">
                        <div class="col-sm-6">
                            <label class="control-label" for="firstName">First Name</label>
                            <input type="text" class="form-control" ng-model="EditedCustomer.FirstName" name="firstName" id="firstName" placeholder="First Name"
                                   validate-on="blur" required required-message="'First Name is required'">
                        </div>

                        <div class="col-sm-6 selectContainer">
                            <label class="control-label" for="lastName">Last Name</label>
                            <input type="text" class="form-control" ng-model="EditedCustomer.LastName" name="lastName" id="lastName" placeholder="Last Name"
                                   validate-on="blur" required required-message="'Last Name is required'">
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label" for="email">Email Address</label>
                            <input type="email" class="form-control" ng-model="EditedCustomer.Email" id="email" name="email" placeholder="Email address"
                                   validate-on="blur" required required-message="'Email is required'">
                        </div>

                        <div class="col-xs-6 selectContainer">
                            <label class="control-label" for="IdentityCard">Identity Card</label>
                            <input type="text" class="form-control" ng-model="EditedCustomer.IdentityCard" id="identityCard" name="identityCard" placeholder="Identity Card number"
                                   validate-on="blur" required required-message="'Identity Card is required'">
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label" for="mobilePhone">Mobile</label>
                            <input type="text" ng-model="EditedCustomer.Mobile" class="form-control" id="mobilePhone" name="mobilePhone" placeholder="Mobile phone"
                                   validate-on="blur" required required-message="'Mobile phone is required'">
                        </div>
                        <div class="col-xs-6 selectContainer">
                            <label class="control-label" for="dateOfBirth">Date of Birth</label>
                            <p class="input-group">
                                <input type="text" class="form-control" name="dateOfBirth" datepicker-popup="{{format}}" ng-model="EditedCustomer.DateOfBirth" is-open="datepicker.opened"
                                        datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                                       validate-on="blur" required required-message="'Date of birth is required'" />
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                                </span>
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
    <div class="panel-footer clearfix">
        <div class="pull-right">
            <button type="button" class="btn btn-danger" ng-click="cancelEdit()">Cancel</button>
            <button type="button" class="btn btn-primary" ng-click="updateCustomer()">Update</button>
        </div>
    </div>
</div>
(function (app) {
    'use strict';

    app.controller('customerEditCtrl', customerEditCtrl);

    customerEditCtrl.$inject = ['$scope', '$modalInstance','$timeout', 'apiService', 'notificationService'];

    function customerEditCtrl($scope, $modalInstance, $timeout, apiService, notificationService) {

        $scope.cancelEdit = cancelEdit;
        $scope.updateCustomer = updateCustomer;

        $scope.openDatePicker = openDatePicker;
        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        function updateCustomer()
        {
            console.log($scope.EditedCustomer);
            apiService.post('/api/customers/update/', $scope.EditedCustomer,
            updateCustomerCompleted,
            updateCustomerLoadFailed);
        }

        function updateCustomerCompleted(response)
        {
            notificationService.displaySuccess($scope.EditedCustomer.FirstName + ' ' + $scope.EditedCustomer.LastName + ' has been updated');
            $scope.EditedCustomer = {};
            $modalInstance.dismiss();
        }

        function updateCustomerLoadFailed(response)
        {
            notificationService.displayError(response.data);
        }

        function cancelEdit() {
            $scope.isEnabled = false;
            $modalInstance.dismiss();
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            console.log('test');
            $timeout(function () {
                $scope.datepicker.opened = true;
            });
            
        };

    }

})(angular.module('homeCinema'));

When the update finishes we ensure that we call the $modalInstance.dismiss() function to close the modal popup window.
spa-webapi-angular-03
You also need to add the Update Web API action method to the CustomersController

        [HttpPost]
        [Route("update")]
        public HttpResponseMessage Update(HttpRequestMessage request, CustomerViewModel customer)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateResponse(HttpStatusCode.BadRequest,
                        ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                              .Select(m => m.ErrorMessage).ToArray());
                }
                else
                {
                    Customer _customer = _customersRepository.GetSingle(customer.ID);
                    _customer.UpdateCustomer(customer);

                    _unitOfWork.Commit();

                    response = request.CreateResponse(HttpStatusCode.OK);
                }

                return response;
            });
        }

Let’s procceed with the customer’s registration feature by adding the register.html template and the customersRegCtrl controller.

<hr />
<div class="alert alert-info alert-dismissable">
    <a class="panel-close close" data-dismiss="alert">×</a>
    <i class="fa fa-user-plus fa-3x"></i>
    Register <strong>{{movie.Title}}</strong> new customer. Make sure you fill all required fields.
</div>
<div class="row">
    <form role="form" novalidate angular-validator name="addCustomerForm" angular-validator-submit="Register()">
        <div class="col-sm-6">
            <div class="form-group">
                <label for="firstName">First Name</label>
                <div class="form-group">
                    <input type="text" class="form-control" ng-model="newCustomer.FirstName" name="firstName" id="firstName" placeholder="First Name"
                           validate-on="blur" required required-message="'First Name is required'">

                </div>
            </div>
            <div class="form-group">
                <label for="lastName">Last Name</label>
                <div class="form-group">
                    <input type="text" class="form-control" ng-model="newCustomer.LastName" name="lastName" id="lastName" placeholder="Last Name"
                           validate-on="blur" required required-message="'Last Name is required'">

                </div>
            </div>
            <div class="form-group">
                <label for="email">Email address</label>
                <div class="form-group">
                    <input type="email" class="form-control" ng-model="newCustomer.Email" id="email" name="email" placeholder="Email address"
                           validate-on="blur" required required-message="'Email is required'">
                </div>
            </div>
            <div class="form-group">
                <label for="identityCard">Identity Card</label>
                <div class="form-group">
                    <input type="text" class="form-control" ng-model="newCustomer.IdentityCard" id="identityCard" name="identityCard" placeholder="Identity Card number"
                           validate-on="blur" required required-message="'Identity Card is required'">
                </div>
            </div>
            <div class="form-group">
                <label for="dateOfBirth">Date of Birth</label>
                <p class="input-group">
                    <input type="text" class="form-control" name="dateOfBirth" datepicker-popup="{{format}}" ng-model="newCustomer.DateOfBirth" is-open="datepicker.opened" datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                           validate-on="blur" required required-message="'Date of birth is required'" />
                    <span class="input-group-btn">
                        <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                    </span>
                </p>
            </div>
            <div class="form-group">
                <label for="mobilePhone">Mobile phone</label>
                <div class="form-group">
                    <input type="text" ng-model="newCustomer.Mobile" class="form-control" id="mobilePhone" name="mobilePhone" placeholder="Mobile phone"
                           validate-on="blur" required required-message="'Mobile phone is required'">

                </div>
            </div>
            <input type="submit" name="submit" id="submit" value="Submit" class="btn btn-info pull-right">
        </div>
    </form>
    <div class="col-sm-5 col-md-push-1">
        <div class="col-md-12">
            <div class="alert alert-success">
                <ul>
                    <li ng-repeat="message in submission.successMessages track by $index">
                        <strong>{{message}}</strong>
                    </li>
                </ul>
                <strong ng-bind="submission.successMessage"><span class="glyphicon glyphicon-ok"></span> </strong>
            </div>
            <div class="alert alert-danger">
                <ul>
                    <li ng-repeat="error in submission.errorMessages track by $index">
                        <strong>{{error}}</strong>
                    </li>
                </ul>
            </div>
        </div>
    </div>
</div>
(function (app) {
    'use strict';

    app.controller('customersRegCtrl', customersRegCtrl);

    customersRegCtrl.$inject = ['$scope', '$location', '$rootScope', 'apiService'];

    function customersRegCtrl($scope, $location, $rootScope, apiService) {

        $scope.newCustomer = {};

        $scope.Register = Register;

        $scope.openDatePicker = openDatePicker;
        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        $scope.submission = {
            successMessages: ['Successfull submission will appear here.'],
            errorMessages: ['Submition errors will appear here.']
        };

        function Register() {
            apiService.post('/api/customers/register', $scope.newCustomer,
           registerCustomerSucceded,
           registerCustomerFailed);
        }

        function registerCustomerSucceded(response) {
            $scope.submission.errorMessages = ['Submition errors will appear here.'];
            console.log(response);
            var customerRegistered = response.data;
            $scope.submission.successMessages = [];
            $scope.submission.successMessages.push($scope.newCustomer.LastName + ' has been successfully registed');
            $scope.submission.successMessages.push('Check ' + customerRegistered.UniqueKey + ' for reference number');
            $scope.newCustomer = {};
        }

        function registerCustomerFailed(response) {
            console.log(response);
            if (response.status == '400')
                $scope.submission.errorMessages = response.data;
            else
                $scope.submission.errorMessages = response.statusText;
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $scope.datepicker.opened = true;
        };
    }

})(angular.module('homeCinema'));

There’s a small problem when rendering the customers registration template. You see there is no authorized resource to call when this template is rendered but the post action will force the user to authenticate himself/herself. We would like to avoid this and render the view only if the user is logged in. What we have used till now (check the customers view), is that when the controller bound to the view is activated and requests data from the server, if the server requires the user to be authenticated, then the apiService automatically redirects the user to the login view.

if (error.status == '401') {
        notificationService.displayError('Authentication required.');
        $rootScope.previousState = $location.path();
        $location.path('/login');
    }

On the other hand, in the register customer view the user will be requested to be authenticated when the employee tries to post the data (new customer) to the server. We can overcome this by adding a resolve function through the route provider for this route. Switch to the app.js and make the following modification.

.when("/customers/register", {
                templateUrl: "scripts/spa/customers/register.html",
                controller: "customersRegCtrl",
                resolve: { isAuthenticated: isAuthenticated }
            })
// code omitted		
isAuthenticated.$inject = ['membershipService', '$rootScope','$location'];
	
function isAuthenticated(membershipService, $rootScope, $location) {
        if (!membershipService.isUserLoggedIn()) {
            $rootScope.previousState = $location.path();
            $location.path('/login');
        }
    }

We use a route resolve function when we want to check a condition before the route actually changes. In our application we can use it to check if the user is logged in or not and if not redirect to login view. Now add the Register Web API action to the CustomersController.

public HttpResponseMessage Register(HttpRequestMessage request, CustomerViewModel customer)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateResponse(HttpStatusCode.BadRequest,
                        ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                              .Select(m => m.ErrorMessage).ToArray());
                }
                else
                {
                    if (_customersRepository.UserExists(customer.Email, customer.IdentityCard))
                    {
                        ModelState.AddModelError("Invalid user", "Email or Identity Card number already exists");
                        response = request.CreateResponse(HttpStatusCode.BadRequest,
                        ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                              .Select(m => m.ErrorMessage).ToArray());
                    }
                    else
                    {
                        Customer newCustomer = new Customer();
                        newCustomer.UpdateCustomer(customer);
                        _customersRepository.Add(newCustomer);

                        _unitOfWork.Commit();

                        // Update view model
                        customer = Mapper.Map<Customer, CustomerViewModel>(newCustomer);
                        response = request.CreateResponse<CustomerViewModel>(HttpStatusCode.Created, customer);
                    }
                }

                return response;
            });
        }

I have highlighted the line where we update the database customer entity using an extension method. We have an Automapper map from Customer entity to CustomerViewModel but not vice-versa. You could do it but I recommend you not to cause it doesn’t work so well with Entity Framework. That’s why I created an extension method for Customer entities. Add a new folder named Extensions inside the Infrastructure and create the following class. Then make sure you include the namespace in the CustomersController class.

public static class EntitiesExtensions
    {
        public static void UpdateCustomer(this Customer customer, CustomerViewModel customerVm)
        {
            customer.FirstName = customerVm.FirstName;
            customer.LastName = customerVm.LastName;
            customer.IdentityCard = customerVm.IdentityCard;
            customer.Mobile = customerVm.Mobile;
            customer.DateOfBirth = customerVm.DateOfBirth;
            customer.Email = customerVm.Email;
            customer.UniqueKey = (customerVm.UniqueKey == null || customerVm.UniqueKey == Guid.Empty)
                ? Guid.NewGuid() : customerVm.UniqueKey;
            customer.RegistrationDate = (customer.RegistrationDate == DateTime.MinValue ? DateTime.Now : customerVm.RegistrationDate);
        }
    }

Movies

The most complex feature in our application is the Movies and that’s because several requirements are connected to that feature. Let’s recap what we need to do.

  1. All movies must be displayed with their relevant information (availability, trailer etc..)
  2. Pagination must be used for faster results, and user can either filter the already displayed movies or search for new ones
  3. Clicking on a DVD image must show the movie’s Details view where user can either edit the movie or rent it to a specific customer if available. This view is accessible only to authenticated users
  4. When employee decides to rent a specific DVD to a customer through the Rent view, it should be able to search customers through an auto-complete textbox
  5. The details view displays inside a panel, rental-history information for this movie, that is the dates rentals and returnings occurred. From this panel user can search a specific rental and mark it as returned
  6. Authenticated employees should be able to add a new entry to the system. They should be able to upload a relevant image for the movie as well

We will start with the first two of them that that is display all movies with pagination, filtering and searching capabilities. We have seen such features when we created the customers base view. First, let’s add the required Web API action method in the MoviesController.

        [AllowAnonymous]
        [Route("{page:int=0}/{pageSize=3}/{filter?}")]
        public HttpResponseMessage Get(HttpRequestMessage request, int? page, int? pageSize, string filter = null)
        {
            int currentPage = page.Value;
            int currentPageSize = pageSize.Value;

            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                List<Movie> movies = null;
                int totalMovies = new int();

                if (!string.IsNullOrEmpty(filter))
                {
                    movies = _moviesRepository.GetAll()
                        .OrderBy(m => m.ID)
                        .Where(m => m.Title.ToLower()
                        .Contains(filter.ToLower().Trim()))
                        .ToList();
                }
                else
                {
                    movies = _moviesRepository.GetAll().ToList();
                }

                totalMovies = movies.Count();
                movies = movies.Skip(currentPage * currentPageSize)
                        .Take(currentPageSize)
                        .ToList();

                IEnumerable<MovieViewModel> moviesVM = Mapper.Map<IEnumerable<Movie>, IEnumerable<MovieViewModel>>(movies);

                PaginationSet<MovieViewModel> pagedSet = new PaginationSet<MovieViewModel>()
                {
                    Page = currentPage,
                    TotalCount = totalMovies,
                    TotalPages = (int)Math.Ceiling((decimal)totalMovies / currentPageSize),
                    Items = moviesVM
                };

                response = request.CreateResponse<PaginationSet<MovieViewModel>>(HttpStatusCode.OK, pagedSet);

                return response;
            });
        }

As you can see, this view doesn’t require the user to be authenticated. Once more we used the PaginationSet class to return additional information for pagination purposes. On the front-end side, create a movies folder inside the spa, add the movies.html template and the moviesCtrl.js controller as follow.

<div class="row">
    <div class="panel panel-primary">
        <div class="panel-heading clearfix">
            <h4 class="panel-title pull-left" style="padding-top: 7.5px;">Home Cinema Movies</h4>
            <div class="input-group">
                <input id="inputSearchMovies" type="search" ng-model="filterMovies" class="form-control shortInputSearch" placeholder="Filter, search movies..">
                <div class="input-group-btn">
                    <button class="btn btn-primary" ng-click="search();"><i class="glyphicon glyphicon-search"></i></button>
                    <button class="btn btn-primary" ng-click="clearSearch();"><i class="glyphicon glyphicon-remove-sign"></i></button>
                </div>
            </div>
        </div>

        <div class="panel-body">
            <div class="row">
                <div class="col-xs-12 col-sm-6 col-md-4" ng-repeat="movie in Movies | filter:filterMovies">
                    <div class="media">
                        <a class="pull-left" ng-href="#/movies/{{movie.ID}}" title="View {{movie.Title}} details">
                            <img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
                        </a>
                        <div class="media-body">
                            <h4 class="media-heading">{{movie.Title}}</h4>
                            Director: <strong>{{movie.Director}}</strong>
                            <br />
                            Writer: <strong>{{movie.Writer}}</strong>
                            <br />
                            Producer: <strong>{{movie.Producer}}</strong>
                            <br />
                            <a class="fancybox-media" ng-href="{{movie.TrailerURI}}">Trailer<i class="fa fa-video-camera fa-fw"></i></a>
                        </div>
                        <div class="media-bottom">
                            <span component-rating="{{movie.Rating}}"></span>
                        </div>
                        <label class="label label-info">{{movie.Genre}}</label>
                        <available-movie is-available="{{movie.IsAvailable}}"></available-movie>
                    </div>
                    <br /><br />
                </div>
            </div>
        </div>
        <div class="panel-footer">
            <div class="text-center">
                <custom-pager page="{{page}}" custom-path="{{customPath}}" pages-count="{{pagesCount}}" total-count="{{totalCount}}" search-func="search(page)"></custom-pager>
            </div>
        </div>
    </div>
</div>

Once again we used both the available-movie and custom-pager directives. Moreover, check that when we click on an image (lines 18:20) we want to change route and display selected movie details.

(function (app) {
    'use strict';

    app.controller('moviesCtrl', moviesCtrl);

    moviesCtrl.$inject = ['$scope', 'apiService','notificationService'];

    function moviesCtrl($scope, apiService, notificationService) {
        $scope.pageClass = 'page-movies';
        $scope.loadingMovies = true;
        $scope.page = 0;
        $scope.pagesCount = 0;
        
        $scope.Movies = [];

        $scope.search = search;
        $scope.clearSearch = clearSearch;

        function search(page) {
            page = page || 0;

            $scope.loadingMovies = true;

            var config = {
                params: {
                    page: page,
                    pageSize: 6,
                    filter: $scope.filterMovies
                }
            };

            apiService.get('/api/movies/', config,
            moviesLoadCompleted,
            moviesLoadFailed);
        }

        function moviesLoadCompleted(result) {
            $scope.Movies = result.data.Items;
            $scope.page = result.data.Page;
            $scope.pagesCount = result.data.TotalPages;
            $scope.totalCount = result.data.TotalCount;
            $scope.loadingMovies = false;

            if ($scope.filterMovies && $scope.filterMovies.length)
            {
                notificationService.displayInfo(result.data.Items.length + ' movies found');
            }
            
        }

        function moviesLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function clearSearch() {
            $scope.filterMovies = '';
            search();
        }

        $scope.search();
    }

})(angular.module('homeCinema'));

spa-webapi-angular-05
Let’s continue with the movie details page. Think this page as an control panel for selected movie where you can edit or rent this movie to a customer and last but not least view all rental history related to that movie, in other words, who borrowed that movie and its rental status (borrowed, returned). First, we will prepare the server side part so swith to the MoviesController and add the following action that returns details for a specific movie. Check that this action is only available for authenticated users and hence when an employee tries to display the details view he/she will be forced to log in first.

        [Route("details/{id:int}")]
        public HttpResponseMessage Get(HttpRequestMessage request, int id)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                var movie = _moviesRepository.GetSingle(id);

                MovieViewModel movieVM = Mapper.Map<Movie, MovieViewModel>(movie);

                response = request.CreateResponse<MovieViewModel>(HttpStatusCode.OK, movieVM);

                return response;
            });
        }

The movie details page also displays rental-history information so let’s see how to implement this functionality. What we mean by movie rental-history is all rentals occurred on stock items related to a specific movie. I remind you that a specific movie may have multiple stock items (DVDs) and more over, a rental is actually assigned to the stock item, not the movie entity.
spa-webapi-angular-22
Let’s create a new ViewModel named RentalHistoryViewModel to hold the information about a specific rental. Add the following class in the Models folder.

public class RentalHistoryViewModel
    {
        public int ID { get; set; }
        public int StockId { get; set; }
        public string Customer { get; set; }
        public string Status { get; set; }
        public DateTime RentalDate { get; set; }
        public Nullable<DateTime> ReturnedDate { get; set; }
    }

The purpose is to return a list of RentalHistoryViewModel items related to the movie being displayed on the details view. In other words, find all rentals related to stock items that have foreign key the selected movie’s ID. Add the following Web API RentalsController controller.

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/rentals")]
    public class RentalsController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Rental> _rentalsRepository;
        private readonly IEntityBaseRepository<Customer> _customersRepository;
        private readonly IEntityBaseRepository<Stock> _stocksRepository;
        private readonly IEntityBaseRepository<Movie> _moviesRepository;

        public RentalsController(IEntityBaseRepository<Rental> rentalsRepository,
            IEntityBaseRepository<Customer> customersRepository, IEntityBaseRepository<Movie> moviesRepository,
            IEntityBaseRepository<Stock> stocksRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _rentalsRepository = rentalsRepository;
            _moviesRepository = moviesRepository;
            _customersRepository = customersRepository;
            _stocksRepository = stocksRepository;
        }
    }

We need a private method in this controller which returns the rental-history items as we previously described.

        private List<RentalHistoryViewModel> GetMovieRentalHistory(int movieId)
        {
            List<RentalHistoryViewModel> _rentalHistory = new List<RentalHistoryViewModel>();
            List<Rental> rentals = new List<Rental>();

            var movie = _moviesRepository.GetSingle(movieId);

            foreach (var stock in movie.Stocks)
            {
                rentals.AddRange(stock.Rentals);
            }

            foreach (var rental in rentals)
            {
                RentalHistoryViewModel _historyItem = new RentalHistoryViewModel()
                {
                    ID = rental.ID,
                    StockId = rental.StockId,
                    RentalDate = rental.RentalDate,
                    ReturnedDate = rental.ReturnedDate.HasValue ? rental.ReturnedDate : null,
                    Status = rental.Status,
                    Customer = _customersRepository.GetCustomerFullName(rental.CustomerId)
                };

                _rentalHistory.Add(_historyItem);
            }

            _rentalHistory.Sort((r1, r2) => r2.RentalDate.CompareTo(r1.RentalDate));

            return _rentalHistory;
        }

And now we can create the Web API action that the client will invoke when requesting rental history information.

        [HttpGet]
        [Route("{id:int}/rentalhistory")]
        public HttpResponseMessage RentalHistory(HttpRequestMessage request, int id)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                List<RentalHistoryViewModel> _rentalHistory = GetMovieRentalHistory(id);

                response = request.CreateResponse<List<RentalHistoryViewModel>>(HttpStatusCode.OK, _rentalHistory);

                return response;
            });
        }

In case we wanted to request rental history for movie with ID=4 then the request would be in the following form:

api/rentals/4/rentalhistory

The employee must be able to mark a specific movie rental as Returned when the customer returns the DVD so let’s add a Return action method as well.

        [HttpPost]
        [Route("return/{rentalId:int}")]
        public HttpResponseMessage Return(HttpRequestMessage request, int rentalId)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                var rental = _rentalsRepository.GetSingle(rentalId);

                if (rental == null)
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid rental");
                else
                {
                    rental.Status = "Returned";
                    rental.Stock.IsAvailable = true;
                    rental.ReturnedDate = DateTime.Now;

                    _unitOfWork.Commit();

                    response = request.CreateResponse(HttpStatusCode.OK);
                }

                return response;
            });
        }

You can mark a movie with ID=4 as Returned with a POST request such as:

api/rentals/return/4

spa-webapi-angular-23
At this point we can switch to the front-end and create the details.html template and its relative controller movieDetailsCtrl. Add the following files inside the movies folder.

<hr />
<div class="jumbotron">
    <div class="container text-center">
        <img alt="{{movie.Title}}" ng-src="../../../Content/images/movies/{{movie.Image}}" class="pull-left" height="120" />
        <div class="movieDescription"><i><i class="fa fa-quote-left"></i>{{movie.Description}}<i class="fa fa-quote-right"></i></i></div>
        <br />
        <div class="btn-group">
            <button ng-if="movie.IsAvailable" type="button" ng-click="openRentDialog();" class="btn btn-sm btn-primary">Rent movie<i class="fa fa-book pull-right"></i></button>
            <a href="#/movies/edit/{{movie.ID}}" class="btn btn-sm btn-default">Edit movie<i class="fa fa-pencil-square-o pull-right"></i></a>
        </div> <!-- end btn-group -->
    </div> <!-- end container -->
</div>

<div class="row">
    <div class="col-md-6">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h5>{{movie.Title}}</h5>
            </div>
            <div class="panel-body" ng-if="!loadingMovie">
                <div class="media">
                    <a class="pull-right" ng-href="#/movies/{{movie.ID}}" title="View {{movie.Title}} details">
                        <img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
                    </a>
                    <div class="media-body">
                        <h4 class="media-heading">{{movie.Title}}</h4>
                        Directed by: <label>{{movie.Director}}</label><br />
                        Written by: <label>{{movie.Writer}}</label><br />
                        Produced by: <label>{{movie.Producer}}</label><br />
                        Rating: <span component-rating='{{movie.Rating}}'></span>
                        <br />
                        <label class="label label-info">{{movie.Genre}}</label>
                        <available-movie is-available="{{movie.IsAvailable}}"></available-movie>
                    </div>
                </div>
            </div>
            <div class="panel-footer clearfix" ng-if="!loadingMovie">
                <div class="pull-right">
                    <a ng-href="{{movie.TrailerURI}}" class="btn btn-primary fancybox-media">View Trailer <i class="fa fa-video-camera pull-right"></i></a>
                    <a ng-href="#/movies/edit/{{movie.ID}}" class="btn btn-default">Edit movie <i class="fa fa-pencil-square pull-right"></i></a>
                </div>
            </div>
            <div ng-if="loadingMovie">
                <div class="col-xs-4"></div>
                <div class="col-xs-4">
                    <i class="fa fa-refresh fa-4x fa-spin"></i> <label class="label label-primary">Loading movie data...</label>
                </div>
                <div class="col-xs-4"></div>
            </div>
        </div>

    </div>
    <div class="col-md-6">
        <div class="panel panel-danger shortPanel">
            <div class="panel-heading clearfix">
                <h5 class="pull-left">Rentals</h5>
                <div class="input-group">
                    <input id="inputSearchMovies" type="search" ng-model="filterRentals" class="form-control" placeholder="Filter..">
                    <div class="input-group-btn">
                        <button class="btn btn-primary" ng-click="clearSearch();"><i class="glyphicon glyphicon-remove-sign"></i></button>
                    </div>
                </div>
            </div>
            <div class="table-responsive" ng-if="!loadingRentals">
                <table class="table table-bordered">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Name</th>
                            <th>Rental date</th>
                            <th>Status</th>
                            <th></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="rental in rentalHistory | filter:filterRentals">
                            <td>{{rental.ID}}</td>
                            <td>{{rental.Customer}}</td>
                            <td>{{rental.RentalDate | date:'fullDate'}}</td>
                            <td ng-class="getStatusColor(rental.Status)">{{rental.Status}}</td>
                            <td class="text-center">
                                <button ng-if="isBorrowed(rental)" type="button" class="btn btn-primary btn-xs" ng-click="returnMovie(rental.ID)">Return</button>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <div ng-if="loadingRentals">
                <div class="col-xs-4"></div>
                <div class="col-xs-4">
                    <i class="fa fa-refresh fa-4x fa-spin"></i> <label class="label label-primary">Loading rental history...</label>
                </div>
                <div class="col-xs-4"></div>
            </div>
        </div>
    </div>
</div>
(function (app) {
    'use strict';

    app.controller('movieDetailsCtrl', movieDetailsCtrl);

    movieDetailsCtrl.$inject = ['$scope', '$location', '$routeParams', '$modal', 'apiService', 'notificationService'];

    function movieDetailsCtrl($scope, $location, $routeParams, $modal, apiService, notificationService) {
        $scope.pageClass = 'page-movies';
        $scope.movie = {};
        $scope.loadingMovie = true;
        $scope.loadingRentals = true;
        $scope.isReadOnly = true;
        $scope.openRentDialog = openRentDialog;
        $scope.returnMovie = returnMovie;
        $scope.rentalHistory = [];
        $scope.getStatusColor = getStatusColor;
        $scope.clearSearch = clearSearch;
        $scope.isBorrowed = isBorrowed;

        function loadMovie() {

            $scope.loadingMovie = true;

            apiService.get('/api/movies/details/' + $routeParams.id, null,
            movieLoadCompleted,
            movieLoadFailed);
        }

        function loadRentalHistory() {
            $scope.loadingRentals = true;

            apiService.get('/api/rentals/' + $routeParams.id + '/rentalhistory', null,
            rentalHistoryLoadCompleted,
            rentalHistoryLoadFailed);
        }

        function loadMovieDetails() {
            loadMovie();
            loadRentalHistory();
        }

        function returnMovie(rentalID) {
            apiService.post('/api/rentals/return/' + rentalID, null,
            returnMovieSucceeded,
            returnMovieFailed);
        }

        function isBorrowed(rental)
        {
            return rental.Status == 'Borrowed';
        }

        function getStatusColor(status) {
            if (status == 'Borrowed')
                return 'red'
            else {
                return 'green';
            }
        }

        function clearSearch()
        {
            $scope.filterRentals = '';
        }

        function movieLoadCompleted(result) {
            $scope.movie = result.data;
            $scope.loadingMovie = false;
        }

        function movieLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function rentalHistoryLoadCompleted(result) {
            console.log(result);
            $scope.rentalHistory = result.data;
            $scope.loadingRentals = false;
        }

        function rentalHistoryLoadFailed(response) {
            notificationService.displayError(response);
        }

        function returnMovieSucceeded(response) {
            notificationService.displaySuccess('Movie returned to HomeCinema succeesfully');
            loadMovieDetails();
        }

        function returnMovieFailed(response) {
            notificationService.displayError(response.data);
        }

        function openRentDialog() {
            $modal.open({
                templateUrl: 'scripts/spa/rental/rentMovieModal.html',
                controller: 'rentMovieCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                loadMovieDetails();
            }, function () {
            });
        }

        loadMovieDetails();
    }
})(angular.module('homeCinema'));

spa-webapi-angular-24
There is one more requirement we need to implement in the details view, the rental. As you may noticed from the movieDetailsCtrl controller, the rental works with a $modal popup window.

function openRentDialog() {
            $modal.open({
                templateUrl: 'scripts/spa/rental/rentMovieModal.html',
                controller: 'rentMovieCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                loadMovieDetails();
            }, function () {
            });
        }

We have seen the $modal popup in action when we were at the edit customer view. Create the rentMovieModal.html and the rentMovieCtrl controller inside a new folder named Rental under the spa.


<div class="panel panel-primary">
    <div class="panel-heading">
        Rent {{movie.Title}}
    </div>
    <div class="panel-body">
        <form class="form-horizontal" role="form">
            <div class="form-group">
                <div class="col-xs-8 selectContainer">
                    <label class="control-label">Available Stock items</label>
                    <select ng-model="selectedStockItem" class="form-control black" ng-options="option.ID as option.UniqueKey for option in stockItems" required></select>
                </div>
            </div>
            <div class="form-group">
                <div class="col-xs-8">
                    <label class="control-label">Select customer</label>
                    <angucomplete-alt id="members"
                                      placeholder="Search customers"
                                      pause="200"
                                      selected-object="selectCustomer"
                                      input-changed="selectionChanged"
                                      remote-url="/api/customers?filter="
                                      remote-url-data-field=""
                                      title-field="FirstName,LastName"
                                      description-field="Email"
                                      input-class="form-control form-control-small"
                                      match-class="red"
                                      text-searching="Searching customers.."
                                      text-no-results="No customers found matching your filter." />
                </div>
            </div>
        </form>
    </div>
    <div class="panel-footer clearfix">
        <div class="pull-right">
            <button type="button" class="btn btn-danger" ng-click="cancelRental()">Cancel</button>
            <button type="button" class="btn btn-primary" ng-click="rentMovie()" ng-disabled="!isEnabled">Rent movie</button>
        </div>
    </div>
</div>

One new thing to notice in this template is the use of the angucomplete-alt directive. We use it in order search customers with auto-complete support. In this directive we declared where to request the data from, the fields to display when an option is selected, a text to display till the request is completed, what to do when an option is selected or changed, etc.. You can find more info about this awesome auto-complete directive here.
spa-webapi-angular-25

(function (app) {
    'use strict';

    app.controller('rentMovieCtrl', rentMovieCtrl);

    rentMovieCtrl.$inject = ['$scope', '$modalInstance', '$location', 'apiService', 'notificationService'];

    function rentMovieCtrl($scope, $modalInstance, $location, apiService, notificationService) {

        $scope.Title = $scope.movie.Title;
        $scope.loadStockItems = loadStockItems;
        $scope.selectCustomer = selectCustomer;
        $scope.selectionChanged = selectionChanged;
        $scope.rentMovie = rentMovie;
        $scope.cancelRental = cancelRental;
        $scope.stockItems = [];
        $scope.selectedCustomer = -1;
        $scope.isEnabled = false;

        function loadStockItems() {
            notificationService.displayInfo('Loading available stock items for ' + $scope.movie.Title);

            apiService.get('/api/stocks/movie/' + $scope.movie.ID, null,
            stockItemsLoadCompleted,
            stockItemsLoadFailed);
        }

        function stockItemsLoadCompleted(response) {
            $scope.stockItems = response.data;
            $scope.selectedStockItem = $scope.stockItems[0].ID;
            console.log(response);
        }

        function stockItemsLoadFailed(response) {
            console.log(response);
            notificationService.displayError(response.data);
        }

        function rentMovie() {
            apiService.post('/api/rentals/rent/' + $scope.selectedCustomer + '/' + $scope.selectedStockItem, null,
            rentMovieSucceeded,
            rentMovieFailed);
        }

        function rentMovieSucceeded(response) {
            notificationService.displaySuccess('Rental completed successfully');
            $modalInstance.close();
        }

        function rentMovieFailed(response) {
            notificationService.displayError(response.data.Message);
        }

        function cancelRental() {
            $scope.stockItems = [];
            $scope.selectedCustomer = -1;
            $scope.isEnabled = false;
            $modalInstance.dismiss();
        }

        function selectCustomer($item) {
            if ($item) {
                $scope.selectedCustomer = $item.originalObject.ID;
                $scope.isEnabled = true;
            }
            else {
                $scope.selectedCustomer = -1;
                $scope.isEnabled = false;
            }
        }

        function selectionChanged($item) {
        }

        loadStockItems();
    }

})(angular.module('homeCinema'));

When an employee wants to rent a specific movie to a customer, first he must find the stock item using a code displayed on the DVD the customer requested to borrow. That’s why I highlighted the above lines in the rentMovieCtrl controller. Moreover, when he finally selects the stock item and the customer, he needs to press the Rent movie button and send a request to server with information about the selected customer and stock item as well. With all that said, we need to implement two more Web API actions. The first one will be in a new Web API Controller named StocksController and the second one responsible for movie rentals, inside the RentalsController.

    [Authorize(Roles="Admin")]
    [RoutePrefix("api/stocks")]
    public class StocksController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Stock> _stocksRepository;
        public StocksController(IEntityBaseRepository<Stock> stocksRepository, 
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _stocksRepository = stocksRepository;
        }

        [Route("movie/{id:int}")]
        public HttpResponseMessage Get(HttpRequestMessage request, int id)
        {
            IEnumerable<Stock> stocks = null;

            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                
                stocks = _stocksRepository.GetAvailableItems(id);

                IEnumerable<StockViewModel> stocksVM = Mapper.Map<IEnumerable<Stock>, IEnumerable<StockViewModel>>(stocks);

                response = request.CreateResponse<IEnumerable<StockViewModel>>(HttpStatusCode.OK, stocksVM);

                return response;
            });
        }
    }

We need to create the StockViewModel class with its validator and of course the Automapper mapping.

public class StockViewModel : IValidatableObject
    {
        public int ID { get; set; }
        public Guid UniqueKey { get; set; }
        public bool IsAvailable { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new StockViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }
public class StockViewModelValidator : AbstractValidator<StockViewModel>
    {
        public StockViewModelValidator()
        {
            RuleFor(s => s.ID).GreaterThan(0)
                .WithMessage("Invalid stock item");

            RuleFor(s => s.UniqueKey).NotEqual(Guid.Empty)
                .WithMessage("Invalid stock item");
        }
    }
protected override void Configure()
        {
            // code omitted 
            Mapper.CreateMap<Stock, StockViewModel>();
        }

For the rental functionality we need add the following action in the RentalsController

        [HttpPost]
        [Route("rent/{customerId:int}/{stockId:int}")]
        public HttpResponseMessage Rent(HttpRequestMessage request, int customerId, int stockId)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                var customer = _customersRepository.GetSingle(customerId);
                var stock = _stocksRepository.GetSingle(stockId);

                if (customer == null || stock == null)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid Customer or Stock");
                }
                else
                {
                    if (stock.IsAvailable)
                    {
                        Rental _rental = new Rental()
                        {
                            CustomerId = customerId,
                            StockId = stockId,
                            RentalDate = DateTime.Now,
                            Status = "Borrowed"
                        };

                        _rentalsRepository.Add(_rental);

                        stock.IsAvailable = false;

                        _unitOfWork.Commit();

                        RentalViewModel rentalVm = Mapper.Map<Rental, RentalViewModel>(_rental);

                        response = request.CreateResponse<RentalViewModel>(HttpStatusCode.Created, rentalVm);
                    }
                    else
                        response = request.CreateErrorResponse(HttpStatusCode.BadRequest, "Selected stock is not available anymore");
                }

                return response;
            });
        }

The action accepts the customer id selected from the auto-complete textbox plus the stock’s item id. Once more we need to add the required view model and Automapper mapping as follow (no validator this time..).

public class RentalViewModel
    {
        public int ID { get; set; }
        public int CustomerId { get; set; }
        public int StockId { get; set; }
        public DateTime RentalDate { get; set; }
        public DateTime ReturnedDate { get; set; }
        public string Status { get; set; }
    }
protected override void Configure()
        {
            // code omitted 
            Mapper.CreateMap<Rental, RentalViewModel>();
        }

From the Details view the user has the option to edit the movie by pressing the related button. This button redirects to route /movies/edit/:id where id is selected movie’s ID. Let’s see the related route definition in app.js.

.when("/movies/edit/:id", {
       templateUrl: "scripts/spa/movies/edit.html",
       controller: "movieEditCtrl"
 })

Here we ‘ll see for the first time how an angularJS controller can capture such a parameter from the route. Add the edit.html and its controller movieEditCtrl inside the movies folder.

<div id="editMovieWrapper">
    <hr>
    <div class="row" ng-if="!loadingMovie">
        <!-- left column -->
        <div class="col-xs-3">
            <div class="text-center">
                <img ng-src="../../Content/images/movies/{{movie.Image}}" class="avatar img-responsive" alt="avatar">
                <h6>Change photo...</h6>

                <input type="file" ng-file-select="prepareFiles($files)">
            </div>
        </div>

        <!-- edit form column -->
        <div class="col-xs-9 personal-info">
            <div class="alert alert-info alert-dismissable">
                <a class="panel-close close" data-dismiss="alert">×</a>
                <i class="fa fa-pencil-square-o"></i>
                Edit <strong>{{movie.Title}}</strong> movie. Make sure you fill all required fields.
            </div>
            <form class="form-horizontal" role="form" novalidate angular-validator name="editMovieForm" angular-validator-submit="UpdateMovie()">
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-8">
                            <label class="control-label">Movie title</label>
                            <input class="form-control" name="title" type="text" ng-model="movie.Title"
                                   validate-on="blur" required required-message="'Movie title is required'">
                        </div>

                        <div class="col-xs-4 selectContainer">
                            <label class="control-label">Genre</label>
                            <select ng-model="movie.GenreId" class="form-control black" ng-options="option.ID as option.Name for option in genres" required></select>
                            <input type="hidden" name="GenreId" ng-value="movie.GenreId" />
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-4">
                            <label class="control-label">Director</label>
                            <input class="form-control" type="text" ng-model="movie.Director" name="director"
                                   validate-on="blur" required required-message="'Movie director is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Writer</label>
                            <input class="form-control" type="text" ng-model="movie.Writer" name="writer"
                                   validate-on="blur" required required-message="'Movie writer is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Producer</label>
                            <input class="form-control" type="text" ng-model="movie.Producer" name="producer"
                                   validate-on="blur" required required-message="'Movie producer is required'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label">Release Date</label>
                            <p class="input-group">
                                <input type="text" class="form-control" name="dateReleased" datepicker-popup="{{format}}" ng-model="movie.ReleaseDate" is-open="datepicker.opened" datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                                       validate-on="blur" required required-message="'Date Released is required'" />
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                                </span>
                            </p>
                        </div>

                        <div class="col-xs-6">
                            <label class="control-label">Youtube trailer</label>
                            <input class="form-control" type="text" ng-model="movie.TrailerURI" name="trailerURI"
                                   validate-on="blur" required required-message="'Movie trailer is required'" ng-pattern="/^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+$/"
                                   invalid-message="'You must enter a valid YouTube URL'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label class="control-label">Description</label>
                    <textarea class="form-control" ng-model="movie.Description" name="description" rows="3"
                              validate-on="blur" required required-message="'Movie description is required'" />
                </div>

                <div class="form-group col-xs-12">
                    <label class="control-label">Rating</label>
                    <span component-rating="{{movie.Rating}}" ng-model="movie.Rating" class="form-control"></span>
                </div>
                <br />
                <div class="form-group col-xs-4">
                    <label class="control-label"></label>
                    <div class="">
                        <input type="submit" class="btn btn-primary" value="Update" />
                        <span></span>
                        <a class="btn btn-default" ng-href="#/movies/{{movie.ID}}">Cancel</a>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <hr>
</div>

spa-webapi-angular-08
We used an ng-file-select directive which in conjunction with the 3rd party library angular-file-upload will handle the movie image uploading through a Web API controller action. You can read more about file uploading using Web API and angularJS here, where I described the process step by step.

(function (app) {
    'use strict';

    app.controller('movieEditCtrl', movieEditCtrl);

    movieEditCtrl.$inject = ['$scope', '$location', '$routeParams', 'apiService', 'notificationService', 'fileUploadService'];

    function movieEditCtrl($scope, $location, $routeParams, apiService, notificationService, fileUploadService) {
        $scope.pageClass = 'page-movies';
        $scope.movie = {};
        $scope.genres = [];
        $scope.loadingMovie = true;
        $scope.isReadOnly = false;
        $scope.UpdateMovie = UpdateMovie;
        $scope.prepareFiles = prepareFiles;
        $scope.openDatePicker = openDatePicker;

        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        var movieImage = null;

        function loadMovie() {

            $scope.loadingMovie = true;

            apiService.get('/api/movies/details/' + $routeParams.id, null,
            movieLoadCompleted,
            movieLoadFailed);
        }

        function movieLoadCompleted(result) {
            $scope.movie = result.data;
            $scope.loadingMovie = false;

            loadGenres();
        }

        function movieLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function genresLoadCompleted(response) {
            $scope.genres = response.data;
        }

        function genresLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function loadGenres() {
            apiService.get('/api/genres/', null,
            genresLoadCompleted,
            genresLoadFailed);
        }

        function UpdateMovie() {
            if (movieImage) {
                fileUploadService.uploadImage(movieImage, $scope.movie.ID, UpdateMovieModel);
            }
            else
                UpdateMovieModel();
        }

        function UpdateMovieModel() {
            apiService.post('/api/movies/update', $scope.movie,
            updateMovieSucceded,
            updateMovieFailed);
        }

        function prepareFiles($files) {
            movieImage = $files;
        }

        function updateMovieSucceded(response) {
            console.log(response);
            notificationService.displaySuccess($scope.movie.Title + ' has been updated');
            $scope.movie = response.data;
            movieImage = null;
        }

        function updateMovieFailed(response) {
            notificationService.displayError(response);
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $scope.datepicker.opened = true;
        };

        loadMovie();
    }

})(angular.module('homeCinema'));

The movieEditController sets a $scope variable named isReadOnly so that the rating component be editable as we have already discussed. When the user submits the form, if the form is valid it checks if any selected file exists. If so, starts with the image file uploading and continues with the movie details updating. If user hasn’t selected an image then only the movie details are being updated (lines: 60-66). For the image file uploading feature, we injected an fileUploadService service in our controller. Let’s create that service inside the services folder.

(function (app) {
    'use strict';

    app.factory('fileUploadService', fileUploadService);

    fileUploadService.$inject = ['$rootScope', '$http', '$timeout', '$upload', 'notificationService'];

    function fileUploadService($rootScope, $http, $timeout, $upload, notificationService) {

        $rootScope.upload = [];

        var service = {
            uploadImage: uploadImage
        }

        function uploadImage($files, movieId, callback) {
            //$files: an array of files selected
            for (var i = 0; i < $files.length; i++) {
                var $file = $files[i];
                (function (index) {
                    $rootScope.upload[index] = $upload.upload({
                        url: "api/movies/images/upload?movieId=" + movieId, // webapi url
                        method: "POST",
                        file: $file
                    }).progress(function (evt) {
                    }).success(function (data, status, headers, config) {
                        // file is uploaded successfully
                        notificationService.displaySuccess(data.FileName + ' uploaded successfully');
                        callback();
                    }).error(function (data, status, headers, config) {
                        notificationService.displayError(data.Message);
                    });
                })(i);
            }
        }

        return service;
    }

})(angular.module('common.core'));

It is time to switch to server side again and support the image file uploading. We need to create two helper classes. The first one is the class that will contain the file upload result. Add the FileUploadResult class inside the Infrastructure.Core folder.

public class FileUploadResult
    {
        public string LocalFilePath { get; set; }
        public string FileName { get; set; }
        public long FileLength { get; set; }
    }

We also need to ensure that the request’s content for file uploading is MIME multipart. Let’s create the following Web API action filter inside the Infrastructure.Core folder.

public class MimeMultipart : System.Web.Http.Filters.ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (!actionContext.Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(
                    new HttpResponseMessage(
                        HttpStatusCode.UnsupportedMediaType)
                );
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {

        }
    }

And here’s the images/upload Web API action in the MoviesController.

        [MimeMultipart]
        [Route("images/upload")]
        public HttpResponseMessage Post(HttpRequestMessage request, int movieId)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                var movieOld = _moviesRepository.GetSingle(movieId);
                if (movieOld == null)
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid movie.");
                else
                {
                    var uploadPath = HttpContext.Current.Server.MapPath("~/Content/images/movies");

                    var multipartFormDataStreamProvider = new UploadMultipartFormProvider(uploadPath);

                    // Read the MIME multipart asynchronously 
                    Request.Content.ReadAsMultipartAsync(multipartFormDataStreamProvider);

                    string _localFileName = multipartFormDataStreamProvider
                        .FileData.Select(multiPartData => multiPartData.LocalFileName).FirstOrDefault();

                    // Create response
                    FileUploadResult fileUploadResult = new FileUploadResult
                    {
                        LocalFilePath = _localFileName,

                        FileName = Path.GetFileName(_localFileName),

                        FileLength = new FileInfo(_localFileName).Length
                    };

                    // update database
                    movieOld.Image = fileUploadResult.FileName;
                    _moviesRepository.Edit(movieOld);
                    _unitOfWork.Commit();

                    response = request.CreateResponse(HttpStatusCode.OK, fileUploadResult);
                }

                return response;
            });
        }

For the Update movie operation we need to add an extra extension method as we did with the Customers. Add the following method inside the EntitiesExtensions class.

public static void UpdateMovie(this Movie movie, MovieViewModel movieVm)
        {
            movie.Title = movieVm.Title;
            movie.Description = movieVm.Description;
            movie.GenreId = movieVm.GenreId;
            movie.Director = movieVm.Director;
            movie.Writer = movieVm.Writer;
            movie.Producer = movieVm.Producer;
            movie.Rating = movieVm.Rating;
            movie.TrailerURI = movieVm.TrailerURI;
            movie.ReleaseDate = movieVm.ReleaseDate;
        }

Check that the Image property is missing since this property is changed only when uploading a movie image. Here’s the Update action in the MoviesController.

        [HttpPost]
        [Route("update")]
        public HttpResponseMessage Update(HttpRequestMessage request, MovieViewModel movie)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
                }
                else
                {
                    var movieDb = _moviesRepository.GetSingle(movie.ID);
                    if (movieDb == null)
                        response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid movie.");
                    else
                    {
                        movieDb.UpdateMovie(movie);
                        movie.Image = movieDb.Image;
                        _moviesRepository.Edit(movieDb);

                        _unitOfWork.Commit();
                        response = request.CreateResponse<MovieViewModel>(HttpStatusCode.OK, movie);
                    }
                }

                return response;
            });
        }

Add movie

The add movie feature is pretty much the same as the edit one. We need one template named add.html for the add operation and a controller named movieAddCtrl. Add the following files inside the movies folder.

<div id="editMovieWrapper">
    <hr>
    <div class="row">
        <!-- left column -->
        <div class="col-xs-3">
            <div class="text-center">
                <img ng-src="../../Content/images/movies/unknown.jpg" class="avatar img-responsive" alt="avatar">
                <h6>Add photo...</h6>

                <input type="file" ng-file-select="prepareFiles($files)">
            </div>
        </div>

        <!-- edit form column -->
        <div class="col-xs-9 personal-info">
            <div class="alert alert-info alert-dismissable">
                <a class="panel-close close" data-dismiss="alert">×</a>
                <i class="fa fa-plus"></i>
                Add <strong>{{movie.Title}}</strong> movie. Make sure you fill all required fields.
            </div>

            <form class="form-horizontal" role="form" novalidate angular-validator name="addMovieForm" angular-validator-submit="AddMovie()">
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6 col-sm-4">
                            <label class="control-label">Movie title</label>
                            <input class="form-control" name="title" type="text" ng-model="movie.Title"
                                   validate-on="blur" required required-message="'Movie title is required'">
                        </div>

                        <div class="col-xs-6 col-sm-4 selectContainer">
                            <label class="control-label">Genre</label>
                            <select ng-model="movie.GenreId" class="form-control black" ng-options="option.ID as option.Name for option in genres" required></select>
                            <input type="hidden" name="GenreId" ng-value="movie.GenreId" />
                        </div>

                        <div class="col-xs-6 col-sm-4">
                            <label class="control-label">Stocks</label>
                            <div class="input-group number-spinner">
                                <span class="input-group-btn">
                                    <button class="btn btn-default" data-dir="dwn"><span class="glyphicon glyphicon-minus"></span></button>
                                </span>
                                <input type="text" class="form-control text-center" id="inputStocks" ng-model="movie.NumberOfStocks">
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" id="btnSetStocks" data-dir="up"><span class="glyphicon glyphicon-plus"></span></button>
                                </span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-4">
                            <label class="control-label">Director</label>
                            <input class="form-control" type="text" ng-model="movie.Director" name="director"
                                   validate-on="blur" required required-message="'Movie director is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Writer</label>
                            <input class="form-control" type="text" ng-model="movie.Writer" name="writer"
                                   validate-on="blur" required required-message="'Movie writer is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Producer</label>
                            <input class="form-control" type="text" ng-model="movie.Producer" name="producer"
                                   validate-on="blur" required required-message="'Movie producer is required'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label">Release Date</label>
                            <p class="input-group">
                                <input type="text" class="form-control" name="dateReleased" datepicker-popup="{{format}}" ng-model="movie.ReleaseDate" is-open="datepicker.opened" datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                                       validate-on="blur" required required-message="'Date Released is required'" />
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                                </span>
                            </p>
                        </div>

                        <div class="col-xs-6">
                            <label class="control-label">Youtube trailer</label>
                            <input class="form-control" type="text" ng-model="movie.TrailerURI" name="trailerURI"
                                   validate-on="blur" required required-message="'Movie trailer is required'" ng-pattern="/^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+$/"
                                   invalid-message="'You must enter a valid YouTube URL'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label class="control-label">Description</label>
                    <textarea class="form-control" ng-model="movie.Description" name="description" rows="3"
                              validate-on="blur" required required-message="'Movie description is required'" />
                </div>

                <div class="form-group col-xs-12">
                    <label class="control-label">Rating</label>
                    <span component-rating="{{movie.Rating}}" ng-model="movie.Rating" class="form-control"></span>
                </div>
                <br/>
                <div class="form-group col-xs-4">
                    <label class="control-label"></label>
                    <div class="">
                        <input type="submit" class="btn btn-primary" value="Submit movie" />
                        <span></span>
                        <a class="btn btn-default" ng-href="#/movies/{{movie.ID}}">Cancel</a>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <hr>
</div>

spa-webapi-angular-26

(function (app) {
    'use strict';

    app.controller('movieAddCtrl', movieAddCtrl);

    movieAddCtrl.$inject = ['$scope', '$location', '$routeParams', 'apiService', 'notificationService', 'fileUploadService'];

    function movieAddCtrl($scope, $location, $routeParams, apiService, notificationService, fileUploadService) {

        $scope.pageClass = 'page-movies';
        $scope.movie = { GenreId: 1, Rating: 1, NumberOfStocks: 1 };

        $scope.genres = [];
        $scope.isReadOnly = false;
        $scope.AddMovie = AddMovie;
        $scope.prepareFiles = prepareFiles;
        $scope.openDatePicker = openDatePicker;
        $scope.changeNumberOfStocks = changeNumberOfStocks;

        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        var movieImage = null;

        function loadGenres() {
            apiService.get('/api/genres/', null,
            genresLoadCompleted,
            genresLoadFailed);
        }

        function genresLoadCompleted(response) {
            $scope.genres = response.data;
        }

        function genresLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function AddMovie() {
            AddMovieModel();
        }

        function AddMovieModel() {
            apiService.post('/api/movies/add', $scope.movie,
            addMovieSucceded,
            addMovieFailed);
        }

        function prepareFiles($files) {
            movieImage = $files;
        }

        function addMovieSucceded(response) {
            notificationService.displaySuccess($scope.movie.Title + ' has been submitted to Home Cinema');
            $scope.movie = response.data;

            if (movieImage) {
                fileUploadService.uploadImage(movieImage, $scope.movie.ID, redirectToEdit);
            }
            else
                redirectToEdit();
        }

        function addMovieFailed(response) {
            console.log(response);
            notificationService.displayError(response.statusText);
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $scope.datepicker.opened = true;
        };

        function redirectToEdit() {
            $location.url('movies/edit/' + $scope.movie.ID);
        }

        function changeNumberOfStocks($vent)
        {
            var btn = $('#btnSetStocks'),
            oldValue = $('#inputStocks').val().trim(),
            newVal = 0;

            if (btn.attr('data-dir') == 'up') {
                newVal = parseInt(oldValue) + 1;
            } else {
                if (oldValue > 1) {
                    newVal = parseInt(oldValue) - 1;
                } else {
                    newVal = 1;
                }
            }
            $('#inputStocks').val(newVal);
            $scope.movie.NumberOfStocks = newVal;
        }

        loadGenres();
    }

})(angular.module('homeCinema'));

There are two things here to notice. The first one is that we have to setup somehow the number of stocks for that movie. Normally, each DVD stock item has its own code but just for convenience, we will create this code automatically in the controller. The other is that since we need the movie to exist before we upload an image, we ensure that the upload image operation comes second by uploading the image (if exists) after the movie has been added to database. When the operation is completed, we redirect to edit the image. Now let’s see the required Add Web API action in the MoviesController.

        [HttpPost]
        [Route("add")]
        public HttpResponseMessage Add(HttpRequestMessage request, MovieViewModel movie)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
                }
                else
                {
                    Movie newMovie = new Movie();
                    newMovie.UpdateMovie(movie);

                    for (int i = 0; i < movie.NumberOfStocks; i++)
                    {
                        Stock stock = new Stock()
                        {
                            IsAvailable = true,
                            Movie = newMovie,
                            UniqueKey = Guid.NewGuid()
                        };
                        newMovie.Stocks.Add(stock);
                    }

                    _moviesRepository.Add(newMovie);

                    _unitOfWork.Commit();

                    // Update view model
                    movie = Mapper.Map<Movie, MovieViewModel>(newMovie);
                    response = request.CreateResponse<MovieViewModel>(HttpStatusCode.Created, movie);
                }

                return response;
            });
        }

Rental history

One last thing remained for our Single Page Application is to display a stastistic rental history for movies that have been rented at least one. We want to create a specific chart for each movie rented, that shows for each date how many rentals occurred. Let’s start from the server-side. We need the following ViewModel class that will hold the information required for each movie’s rental statistics.

public class TotalRentalHistoryViewModel
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Image { get; set; }
        public int TotalRentals
        {
            get
            {
                return Rentals.Count;
            }
            set { }
        }
        public List<RentalHistoryPerDate> Rentals { get; set; }
    }

    public class RentalHistoryPerDate
    {
        public int TotalRentals { get; set; }
        public DateTime Date { get; set; }
    }

Switch to the RentalsController and add the following GetMovieRentalHistoryPerDates private method. If you remember, we have already created a private method GetMovieRentalHistory to get rental history for a specific movie so getting the rental history for all movies should not be a problem.

private List<RentalHistoryPerDate> GetMovieRentalHistoryPerDates(int movieId)
        {
            List<RentalHistoryPerDate> listHistory = new List<RentalHistoryPerDate>();
            List<RentalHistoryViewModel> _rentalHistory = GetMovieRentalHistory(movieId);
            if (_rentalHistory.Count > 0)
            {
                List<DateTime> _distinctDates = new List<DateTime>();
                _distinctDates = _rentalHistory.Select(h => h.RentalDate.Date).Distinct().ToList();

                foreach (var distinctDate in _distinctDates)
                {
                    var totalDateRentals = _rentalHistory.Count(r => r.RentalDate.Date == distinctDate);
                    RentalHistoryPerDate _movieRentalHistoryPerDate = new RentalHistoryPerDate()
                    {
                        Date = distinctDate,
                        TotalRentals = totalDateRentals
                    };

                    listHistory.Add(_movieRentalHistoryPerDate);
                }

                listHistory.Sort((r1, r2) => r1.Date.CompareTo(r2.Date));
            }

            return listHistory;
        }

Only thing required here was to sort the results by ascending dates. And now the action.

        [HttpGet]
        [Route("rentalhistory")]
        public HttpResponseMessage TotalRentalHistory(HttpRequestMessage request)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                List<TotalRentalHistoryViewModel> _totalMoviesRentalHistory = new List<TotalRentalHistoryViewModel>();

                var movies = _moviesRepository.GetAll();

                foreach (var movie in movies)
                {
                    TotalRentalHistoryViewModel _totalRentalHistory = new TotalRentalHistoryViewModel()
                    {
                        ID = movie.ID,
                        Title = movie.Title,
                        Image = movie.Image,
                        Rentals = GetMovieRentalHistoryPerDates(movie.ID)
                    };

                    if (_totalRentalHistory.TotalRentals > 0)
                        _totalMoviesRentalHistory.Add(_totalRentalHistory);
                }

                response = request.CreateResponse<List<TotalRentalHistoryViewModel>>(HttpStatusCode.OK, _totalMoviesRentalHistory);

                return response;
            });
        }

On the front-side we need to create a rental.html template and a rentalStatsCtrl controller. Add the following files inside the rental folder.

<hr />
<div class="row" ng-repeat="movie in rentals" ng-if="!loadingStatistics">
    <div class="panel panel-primary">
        <div class="panel-heading">
            <span>{{movie.Title}}</span>
        </div>
        <div class="panel-body">
            <div class="col-xs-3">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <strong class="ng-binding">{{movie.Title}} </strong>
                    </div>
                    <div class="panel-body">
                        <div class="media">
                            <img class="media-object center-block img-responsive center-block"
                                 ng-src="../../Content/images/movies/{{movie.Image}}" alt="{{movie.Title}}">
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-xs-9">
                <div class="panel panel-default text-center">
                    <div class="panel-body">
                        <div id="statistics-{{movie.ID}}">

                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div> <!-- end row -->

spa-webapi-angular-10

(function (app) {
    'use strict';

    app.controller('rentStatsCtrl', rentStatsCtrl);

    rentStatsCtrl.$inject = ['$scope', 'apiService', 'notificationService', '$timeout'];

    function rentStatsCtrl($scope, apiService, notificationService, $timeout) {
        $scope.loadStatistics = loadStatistics;
        $scope.rentals = [];

        function loadStatistics() {
            $scope.loadingStatistics = true;

            apiService.get('/api/rentals/rentalhistory', null,
            rentalHistoryLoadCompleted,
            rentalHistoryLoadFailed);
        }

        function rentalHistoryLoadCompleted(result) {
            $scope.rentals = result.data;

            $timeout(function () {
                angular.forEach($scope.rentals, function (rental) {
                    if (rental.TotalRentals > 0) {

                        var movieRentals = rental.Rentals;

                        Morris.Line({
                            element: 'statistics-' + rental.ID,
                            data: movieRentals,
                            parseTime: false,
                            lineWidth: 4,
                            xkey: 'Date',
                            xlabels: 'day',
                            resize: 'true',
                            ykeys: ['TotalRentals'],
                            labels: ['Total Rentals']
                        });
                    }
                })
            }, 1000);

            $scope.loadingStatistics = false;
        }

        function rentalHistoryLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        loadStatistics();
    }

})(angular.module('homeCinema'));

You need to create the Morris chart after the data have been loaded, otherwise it won’t work. Notice that we also needed a different id for the element where the chart will be hosted and for that we used the movie’s id.
At this point, you should be able to run the HomeCinema Single Page Application. In case you have any problems, you can always download the source code from my Github account and follow the installation instructions.

Discussion

There are 2 main things I would like to discuss about the application we created. The first one is talk about scaling the application to support more roles. I’m talking about feature-level scaling for example suppose that you have an extra requirement that this web application should be accessible by external users too, the Customers. What would that mean for this application? The first thing popped up to my mind is more templates, more Web API controllers and last but not least even more JavaScript files. Guess what, that’s all true. The thing is that after such requirement our HomeCinema application will not be entirely Single Page Application. It could, but it would be wrong.
Let me explain what I mean by that. From the server side and the Web API framework, we have no restrictions at all. We were clever enough to create a custom membership schema with custom roles and then apply Basic Authentication through message handlers. This means that if you want to restrict new views only to customers, what you need to do is create a new Role named Customer and apply an [Authorize(Roles = “Customer”)] attribute to the respective controllers or actions. The big problem is on the front-end side. You could add more functionality, templates and controllers upon the same root module homeCinema but believe me, very soon you wouldn’t be able to maintain all those files. Instead, you can simply break your application in two Single Page Applications, one responsible for employees and another for customers. This means that you would have two MVC views, not one (one more action and view in the HomeController. Each of these views its a Single Page Application by itself and has its own module. And if you wish, you can go ever further and adapt a new architecture for the front-end side of your application. Let’s say that both the Admin and Customer roles, require really many views which means you still have the maintainability issue. Then you have to break your SPAs even more and have a specific MVC View (spa) for each sub-feature. All the relative sub-views can share the same Layout page which is responsible to render the required JavaScript files. Let’s see the architecture graphically.
spa-webapi-angular-27
_Layout pages may have a RenderScript Razor definitions as follow:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body data-ng-app="adminApp">
        <div class="wrapper">
            <div class="container">
                    <section>
                        @RenderBody()
                    </section>
                </div>
            </div>
        </div>

        @Scripts.Render("~/bundles/vendors")
        <script src="@Url.Content("~/Scripts/spa/app.js")" type="text/javascript"></script>

        @RenderSection("scripts", required: false)
        <script type="text/javascript">
            @RenderSection("customScript", required: false)
        </script>
    </body>
</html>

What we did here is render all the required css and vendor files but all MVC views having this _Layout page can (and must) render their own JavaScript files through the scripts and customScript sections. The first section concerns angularJS required components and the latter concerns custom JavaScript code. Remember the following part we added in the run method of the app.js file?

$(document).ready(function () {
            $(".fancybox").fancybox({
                openEffect: 'none',
                closeEffect: 'none'
            });

            $('.fancybox-media').fancybox({
                openEffect: 'none',
                closeEffect: 'none',
                helpers: {
                    media: {}
                }
            });

            $('[data-toggle=offcanvas]').click(function () {
                $('.row-offcanvas').toggleClass('active');
            });
        });

That’s the type of code that should be written in the customScript section of an MVC view. Now let’s how an MVC View would look like with this architecture.

@section scripts
{
    <script src="@Url.Content("~/Scripts/spa/customController.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/spa/customDirective.js")" type="text/javascript"></script>
}
@section footerScript
{
    angular.bootstrap(document.getElementById("customApp"),['customApp']);

    $(document).ready(function () {
       //...
    });
}
<div ng-app="customApp" id="customApp">
    <div ng-view></div>
</div>

One last thing I would like to discuss is the way data repositories are injected into Web API controllers. We followed a generic repository pattern and all we have to do in order to use a specific repository is inject it to the constructor. But let’s take a look the RentalsController constructor.

        private readonly IEntityBaseRepository<Rental> _rentalsRepository;
        private readonly IEntityBaseRepository<Customer> _customersRepository;
        private readonly IEntityBaseRepository<Stock> _stocksRepository;
        private readonly IEntityBaseRepository<Movie> _moviesRepository;

        public RentalsController(IEntityBaseRepository<Rental> rentalsRepository,
            IEntityBaseRepository<Customer> customersRepository, IEntityBaseRepository<Movie> moviesRepository,
            IEntityBaseRepository<Stock> stocksRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _rentalsRepository = rentalsRepository;
            _moviesRepository = moviesRepository;
            _customersRepository = customersRepository;
            _stocksRepository = stocksRepository;
        }

It’s kind of a mess, isn’t it? What if a controller requires much more repositories? Well, there’s another trick you can do to keep your controllers cleaner as much as possible and this is via the use of a Generic DataRepository Factory. Let’s see how we can accomplish it and re-write the RentalsController.
First, we need to add a new HttpRequestMessage extension which will allow us to resolve instances of IEntityBaseRepository<T> so switch to RequestMessageExtensions file and add the following method.

internal static IEntityBaseRepository<T> GetDataRepository<T>(this HttpRequestMessage request) where T : class, IEntityBase, new()
        {
            return request.GetService<IEntityBaseRepository<T>>();
        }

Here we can see how useful the IEntityBase interface is in our application. We can use this interface to resolve data repositories for our entities. Now let’s create the generic Factory. Add the following file inside the Infrastructure/Core folder.

public class DataRepositoryFactory : IDataRepositoryFactory
    {
        public IEntityBaseRepository<T> GetDataRepository<T>(HttpRequestMessage request) where T : class, IEntityBase, new()
        {
            return request.GetDataRepository<T>();
        }
    }

    public interface IDataRepositoryFactory
    {
        IEntityBaseRepository<T> GetDataRepository<T>(HttpRequestMessage request) where T : class, IEntityBase, new();
    }

This factory has a generic method of type IEntityBase that invokes the extension method we wrote before. We will use this method in the ApiControllerBase class, which the base class for our Web API controllers. Before doing this, switch to AutofacWebapiConfig class where we configured the dependency injection and add the following lines before the end of the RegisterServices function.

// code omitted

            // Generic Data Repository Factory
            builder.RegisterType<DataRepositoryFactory>()
                .As<IDataRepositoryFactory>().InstancePerRequest();

            Container = builder.Build();

            return Container;

I don’t want to change the current ApiControllerBase implementation so I will write a new one just for the demonstration. For start let’s see its definition along with its variables.

public class ApiControllerBaseExtended : ApiController
    {
        protected List<Type> _requiredRepositories;

        protected readonly IDataRepositoryFactory _dataRepositoryFactory;
        protected IEntityBaseRepository<Error> _errorsRepository;
        protected IEntityBaseRepository<Movie> _moviesRepository;
        protected IEntityBaseRepository<Rental> _rentalsRepository;
        protected IEntityBaseRepository<Stock> _stocksRepository;
        protected IEntityBaseRepository<Customer> _customersRepository;
        protected IUnitOfWork _unitOfWork;

        private HttpRequestMessage RequestMessage;

        public ApiControllerBaseExtended(IDataRepositoryFactory dataRepositoryFactory, IUnitOfWork unitOfWork)
        {
            _dataRepositoryFactory = dataRepositoryFactory;
            _unitOfWork = unitOfWork;
        }

        private void LogError(Exception ex)
        {
            try
            {
                Error _error = new Error()
                {
                    Message = ex.Message,
                    StackTrace = ex.StackTrace,
                    DateCreated = DateTime.Now
                };

                _errorsRepository.Add(_error);
                _unitOfWork.Commit();
            }
            catch { }
        }
    }

This base class holds references for all types of Data repositories that your application may need. The most important variable is the _requiredRepositories which eventually will hold the types of Data repositories a Web API action may require. its constructor has only two dependencies one of type IDataRepositoryFactory and another of IUnitOfWork. The first one is required to resolve the data repositories using the new extension method and the other is the one for committing database changes. Now let’s see the esense of this base class, the extended CreateHttpResponse method. Add the following methods in to the new base class as well.

protected HttpResponseMessage CreateHttpResponse(HttpRequestMessage request, List<Type> repos, Func<HttpResponseMessage> function)
        {
            HttpResponseMessage response = null;

            try
            {
                RequestMessage = request;
                InitRepositories(repos);
                response = function.Invoke();
            }
            catch (DbUpdateException ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.BadRequest, ex.InnerException.Message);
            }
            catch (Exception ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }

            return response;
        }
        
        private void InitRepositories(List<Type> entities)
        {
            _errorsRepository = _dataRepositoryFactory.GetDataRepository<Error>(RequestMessage);

            if (entities.Any(e => e.FullName == typeof(Movie).FullName))
            {
                _moviesRepository = _dataRepositoryFactory.GetDataRepository<Movie>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(Rental).FullName))
            {
                _rentalsRepository = _dataRepositoryFactory.GetDataRepository<Rental>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(Customer).FullName))
            {
                _customersRepository = _dataRepositoryFactory.GetDataRepository<Customer>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(Stock).FullName))
            {
                _stocksRepository = _dataRepositoryFactory.GetDataRepository<Stock>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(User).FullName))
            {
                _stocksRepository = _dataRepositoryFactory.GetDataRepository<Stock>(RequestMessage);
            }
        }

This method is almost the same as the relative method in the ApiControllerBase class, except that is accepts an extra parameter of type List<Type>. This list will be used to initialized any repositories the caller action requires using the private InitRepositories method. Now let’s see how the RentalsController Web API controller could be written using this new base class. I created a new RentalsExtendedController class so that you don’t have to change the one you created before. Let’s the the new definition. Ready?

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/rentalsextended")]
    public class RentalsExtendedController : ApiControllerBaseExtended
    {
        public RentalsExtendedController(IDataRepositoryFactory dataRepositoryFactory, IUnitOfWork unitOfWork)
            : base(dataRepositoryFactory, unitOfWork) { }
    }

Yup, that’s it, no kidding. Now let’s re-write the Rent action that rents a specific movie to a customer. This method requires 3 data repositories of types Customer, Stock and Rental.

        [HttpPost]
        [Route("rent/{customerId:int}/{stockId:int}")]
        public HttpResponseMessage Rent(HttpRequestMessage request, int customerId, int stockId)
        {
            _requiredRepositories = new List<Type>() { typeof(Customer), typeof(Stock), typeof(Rental) };

            return CreateHttpResponse(request, _requiredRepositories, () =>
            {
                HttpResponseMessage response = null;

                var customer = _customersRepository.GetSingle(customerId);
                var stock = _stocksRepository.GetSingle(stockId);

                if (customer == null || stock == null)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid Customer or Stock");
                }
                else
                {
                    if (stock.IsAvailable)
                    {
                        Rental _rental = new Rental()
                        {
                            CustomerId = customerId,
                            StockId = stockId,
                            RentalDate = DateTime.Now,
                            Status = "Borrowed"
                        };

                        _rentalsRepository.Add(_rental);

                        stock.IsAvailable = false;

                        _unitOfWork.Commit();

                        RentalViewModel rentalVm = Mapper.Map<Rental, RentalViewModel>(_rental);

                        response = request.CreateResponse<RentalViewModel>(HttpStatusCode.Created, rentalVm);
                    }
                    else
                        response = request.CreateErrorResponse(HttpStatusCode.BadRequest, "Selected stock is not available anymore");
                }

                return response;
            });
        }

The only thing an action needs to do is initialize the list of data repositories needs to perform. If you download the source code of this application from my Github account you will find that I have re-written the RentalsController and the MoviesController classes with the extended base class. It’s up to you which of the base classes you prefer to use.

That’s it, we finally finished this post having described step by step how to build a Single Page Application using Web API and AngularJS. Let me remind you that this post has an e-book PDF version which you can download for free. You can download the source code for this project we built here where you will also find instructions on how to run the HomeCinema application. I hope you enjoyed this post or its e-book version as much I did writing both of them. Please take a moment and post your review at the comments section below.
spa-webapi-angular-28

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

TypeScript, AngularJS, Gulp and Bower in Visual Studio 2015

$
0
0

Either you are a Back-end Web developer or a Front-end one, you must have realized that there are some frameworks and tools that have gained the poll position in Web application development. Which are these? Go ahead, search and download a client-side library, plugin or package. It’s 100% percentage sure, that you will be prompted to run one of the following commands.

npm install --package
bower install --package

If you haven’t heard of those terms yet or you have but don’t know exactly how to use them, that’s OK, this is what we are gonna see on this post. Beside Bower and NPM there two more players that are getting hotter nowadays. These are the Gulp build system and the Typescript. The former is perfectly bound to npm and bower allowing to create and automate tasks that are processing downloaded packages or custom CSS and JavaScript files. The latter seemed to got even more famous with the Angular 2 release whitch is known to be built in Typescript. Moreover, we are really happy that all those amazing tools are integrated in Visual Studio 2015 edition. So, let’s see now what we are going to do on this post.

We are going to create a Single Page Application using AngularJS and Typescript. We will use Bower and NPM to install or download all the plugins and packages required for the application. We will use Gulp to process all the JavaScript and CSS files required to run on the client side. We ‘ll do all these using Visual Studio 2015 edition so in case you haven’t already installed it, go ahead and do it here. The intent of this app is to simulate chsakell’s Blog Recent Posts section.
typescript-gulp-bower

Prerequisites

We ‘ll take a step back before start building our application in order to install all the pre-mentioned tools for two reasons. The first one is that you will find many projects on the internet that use those tools but aren’t developed in Visual Studio. Hence, you need to know how to kick of those projects. The second reason is that I want us to run the custom commands that Visual Studio runs behind the scenes when using NPM, Bower and Gulp in the application we are going to build. Let’s start:

NPM

node-npm
So what exactly is NPM? NPM is the Node.js Package Manager. In other words, NPM is to Node.js what Nuget Package Manager is to Visual Studio. And what is a Package Manager? A Package Manager is responsible to automate the installation and tracking of external packages. It can locate an online package, select and install a specific version. In order to install NPM you need to install Node.js so go ahead to its official website and install it here. After installing Node.js type the following command in console and ensure that you have NPM installed as well.

npm -v

Bower

bower

Bower is another Package Manager for the Web and as it’s official website says, Bower is optimized for the front-end. You will probably wonder.. wait a minute, you said NPM is also a Package Manager, so when do I know which one to use? Well, the answer is pretty much simple. You use NPM for installing packages that run on the server side and Bower for packages that will eventually run on the client. Let’s explain it with an example. The application we are going to build, requires bootstrap and angularJS. Since those packages are going to be rendered on the client, we will download them using Bower. Yet, before those JavaScript and CSS files sent to client’s browser, we need to process them using Gulp. The processing will be done on the server side of course and hence, Gulp and all the relative plugins we ‘ll use to pre-process files are going to be downloaded through NPM. You can install Bower using the NPM by typing the following command in the console:
npm install -g bower

Gulp

gulp
Gulp is a task runner that uses Node.js and works in a Steamline way. The main differences with it’s competitor Grunt, is that Gulp is code and stream based while Grunt is configuration and file based. You can think Gulp as a pipe, where you input some source files and at the end of the pipe you expect an output. For example, assuming that you have 5 custom JavaScript files that you want to be rendered on a page. You want to ensure that there is no compile error in your JavaScript, then you want to remove any comments or white spaces exist on those files and finally, concatenate those files to one single file. You can do all these stuff by creating a single Gulp task.
typescript-gulp-bower-01
Install Gulp globally by typing the following command in console.

npm install -g gulp

Typescript

typescript
Typescript is the famous superset of JavaScript that compiles to plain JavaScript. Its main features are that it is strongly typed and provides class-based object-orientation. This open-source programming language, helps you build better code by type checking the code during development providing developers with powerfull refactoring features. Typescript comes out of the box with Visual Studio 2013 Update 2 and of course Visual Studio 2015. We will install it though globally
through NPM by typing the following command in console.

npm install -g typescript

If you are about to code in Typescript without using any external library, you are good to go. All you need to do is create a Typescript file and start coding. You can read more about Typescript on its official page
here. If you need to use an external library such as angularJS then you have to install-download the respective Typescript Definition File. Typescript Definition files have the extension .d.ts and describe the types defined in an external library. They are only used during development, not in deployment. You can find such files in a specific repository named DefinitelyTyped here. You can download and use a specific Typescript Definition file using the Typescript Definition Manager (yup.. another package manager..). This package manager locates and installs d.ts directly from the DefinitelyTyped repository. Install it by typing the following command in console.

npm install tsd -g

Two last things I would recommend you to install are Web Essentians for Visual Studio and Git. Web Essentians provides you with many features when writting CSS, JavaScript, TypeScript and so on.. You can install it from here.

Start Coding!

Open Visual Studio 2015 and create an Empty ASP.NET Web Application having the Web API and MVC checkboxes checked. Add a new MVC Controller named HomeController in the Controllers folder and create (if not exists) and Index action with the respective Views/Home/Index.cshtml view. The first thing we will do, is set Bower and download all the required Web dependencies that our SPA requires to run. Those dependencies are the JavaScript and CSS libraries that will be rendered on the client. Right click on the project and select New item…. Then search for Bower and select the Bower Configuration File. Leaving the default name as it is will add the following Bower.json configuration file at the root of application’s directory.

{
   "name": "ASP.NET",
   "private": true,
   "dependencies": {
     }
}

There are many attributes that you can set in this .json file but we will only use the dependencies value to download the required libraries. Let’s start by installing angularJS. Change the bower.json as follow.

{
   "name": "ASP.NET",
   "private": true,
    "dependencies": {
      "angular": "1.4.5"
     }
}

Notice that by the time you start typing the dependency you want to download, you have auto-complete intellisense experience. This helps a lot tracking the required dependency.
typescript-gulp-bower-02
typescript-gulp-bower-03
When you save the bower.json file the magic will happen. Visual Studio will download angularJS files. You will notice that VS downloads the dependencies in a root/lib directory by default.
typescript-gulp-bower-04
This directory is defined in the .bowerrc file. Now, if you are already familiar with Bower you would know that by default the depenencies are downloaded in a bower_components directory. The truth is that I’m kind of used with that behavior so go ahead and change the .bowerrc file as follow.

{
    "directory": "bower_components"
}

Remove the wwwroot/lib folder from your solution, just edit the bower.json file (add a comment for example) and save again. This time Visual Studio will download angularJS in a bower_components folder. Let’s see what exactly Visual Studio does behind the scenes. Our application needs the angular-route module as well so let’s install it by typing the bower commands ourselfs this time. Open a terminal as administrator and switch to our application’s root folder. Type the following command:

bower install --save angular-route

The command will update the bower.json file by adding the required dependency. It will also download the angular-route module inside the bower_components folder. Let’s see the complete bower.json file.

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "angular": "1.4.5",
    "angular-route": "~1.4.5",
    "bootstrap": "3.3.5"
  }
}

Make sure you save the file and download all the required dependencies. Create an app folder in the root of your application and add inside it our first TypeScript file, named app.ts. Because of the fact app.ts is the first TypeScript file we added, Visual Studio will inform us that the project has been configured to support TypeScript and will suggest to search for typings from the Nuget package manager. Press NO, we will do it ourselves after we understand why we need to install them.
typescript-gulp-bower-05
At the moment, if you try to create an angular module will will notice that TypeScript does not recognize the word angular and that’s because TypeScript doesn’t recognize external library types without their respective .d.ts files. Hence, since we haven’t installed the required TypeScript Definition Files for angularJS, angular is an unknown word to TypeScript.
typescript-gulp-bower-06
You have two choices here. Either install those files using Nuget Package Manager witch will download those files inside a Scripts/typings folder or use the Typescript Definition Manager we installed in previous step, and type the following command in the console, while at the root of the application.

tsd install angular --resolve --save

Notice that this time we don’t use the npm command but tsd in order to ask TypeScript Definition Manager to install the TypeScript Definition Files for angular. By passing –resolve and –save as parameters, TypeScript Definition Manager will resolve any dependencies that the angular definition files require and download them as well. The –save parameter will save the result in a tsd.d.ts file. This file contains the references path so that TypeScript can find the .d.ts files. Personally, I always prefer to install the TypeScript Definition Files using Nuget cause I have noticed some intellisense issues with the first one. Open Nuget Package Manager, search the term angularjs.typescript and install the respective package. This will also install jQuery relative TypeScript Definition Files since angular’s d.ts is depended on it (if prompted to install jQuery d.ts files click OK).
typescript-gulp-bower-07
After installing the packages make sure the typings have been added in a Scripts/typings folder and TypeScript now recognizes angular.
typescript-gulp-bower-08
We will proceed with defining our main angularJS module inside the app.ts TypeScript file. One important thing to remember when coding with TypeScript and angularJS is to always encapsulate your code in TypeScript modules. You will see that we are going to use Interfaces, Classes and many variables through our application and TypeScript modules will help us to encapsulate all of theses since TypeScript module can only access code within the same TypeScript module by default. In order to access an Interface or Class that belongs to a different TypeScript module you need to expose it using the export keyword. TypeScript module defines a unique namespace and since in angularJS is good practice to structure the spa by feature, guess what: we can also define a unique TypeScript module that actually encapsulates the angularJS components for a specific feature. In our application (which is quite small), we will define a top level module, the main module which we can call it app. For common services or directives we will have the TypeScript modules app.common.services and app.common.directives respectively. We only need to display, edit and add Posts in this application so we can create the relative TypeScript modules, app.PostList, app.PostEdit and app.PostAdd.

Another important thing you need to remember, is that by surrounding your code within a TypeScript module the resulted transpiled JavaScript code is contained in an IIFE (Immediately Invoked Function Expression). Let’s see the definition of our main module with the routing configuration as well.

module app {
    class Config {
        constructor($routeProvider: ng.route.IRouteProvider) {
            $routeProvider
                .when("/", {
                    templateUrl: "/app/posts/list.html",
                    controller: "PostsCtrl as vm"
                })
                .when("/edit/:id", {
                    templateUrl: "/app/posts/edit.html",
                    controller: "PostEditCtrl as vm"
                })
                .when("/add", {
                    templateUrl: "/app/posts/add.html",
                    controller: "PostAddCtrl as vm"
                })
                .otherwise({ redirectTo: '/' });
        }
    }
    Config.$inject = ['$routeProvider'];

    var mainApp = angular.module('chsakellBlogApp', ['ngRoute']);
    mainApp.config(Config);
}

We used a TypeScript class to define the routing configuration. In order to accomplish it, we injected the $routeProvider of type ng.route.IRouteProvider in its constructor (by the way, ng is another name for angular). In the same way we used to inject services on the function definition, we inject the services from the TypeScript Definition Files on the Class constructors. I would like to show you now how this file would transpile into pure JavaScript. As we have already mentioned, we use Visual Studio 2015 to build this app, but I used Visual Studio 2013 to take the following screenshot cause it has a preview for TypeScript files. Notice that our code was encapsulated in an IIFE.
typescript-gulp-bower-10
Let’s proceed by defining the Post domain entity that we are going to use through the app. Add a domain folder under app and create the following TypeScript file named IEntity.ts.

module app.domain {
    export interface IEntity { }

    export class EntityBase implements IEntity {
        constructor() { }
    }
} 

We will enforce all of our domain objects to extend the app.domain.EntityBase class. We encapsulated the code in a new TypeScript module named app.domain and we declared an interface and a class that are going to be available outside this module, because we used the export keyword. Add the Post.ts file inside the app/domain folder as well.

module app.domain {
    export interface IPost {
        Id?: number;
        Title: string;
        Author: string;
        AuthorGravatar: string;
        URI: string;
        DatePublished: Date;
        ImageURI: string;
        Contents: string;
    }

    export class Post extends app.domain.EntityBase implements IPost {
        constructor(public Title: string,
            public Author: string,
            public AuthorGravatar: string,
            public URI: string,
            public DatePublished: Date,
            public ImageURI: string,
            public Contents: string,
            public Id?: number) {

            super();

            this.Id = Id;
            this.Title = Title;
            this.Author = Author;
            this.AuthorGravatar = AuthorGravatar;
            this.URI = URI;
            this.DatePublished = DatePublished;
            this.ImageURI = ImageURI;
            this.Contents = Contents;
        }
    }
}

By implementing an interface we force a class to define certain properties or functions. In the Post class, we used public properties in the constructor which automatically creates the relative properties. We will see different pattern on the controller classes. You may have noticed that a TypeScript module can split among different files. Let’s create the first service now. Create a new folder named common under app and add a new folder named services under common. Add the following constantService.ts TypeScript file.

module app.common.services {

    interface IConstant {
        apiPostURI: string;
    }

    export class ConstantService implements IConstant {
        apiPostURI: string;

        constructor() {
            this.apiPostURI = '/api/posts/';
        }
    }

    angular.module('chsakellBlogApp')
        .service('constantService', ConstantService);
}

We created the ConstantService to define any constant api URIs to use across our application. You can see that we chose to create the property outside the constructor and that we only exposed the actual service, not the interface. The next would be to create a dataService able to communicate with a Web API backend infrastructure but since we haven’t create the latter yet, let’s do it now. Add the Post.cs file inside the Models folder.

public class Post
    {
        #region Properties
        public int Id { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string AuthorGravatar { get; set; }
        public string URI { get; set; }
        public DateTime DatePublished { get; set; }
        public string ImageURI { get; set; }
        public string Contents { get; set; }
        #endregion
    }

Normally, you would fetch data from a database but just for simplicity we will use some static data. Create an DataRepository class inside the App_Start folder as follow:

public class DataRepository
    {
        public static List<Post> Posts = new List<Post>()
        {
            new Post() {
                Id = 1,
                Title = "Building Single Page Applications using Web API and angularJS (Free e-book)",
                Author = "Chris S.",
                AuthorGravatar = "http://1.gravatar.com/avatar/cb562a6de0640f743a272496ccfc210e",
                URI = "http://chsakell.com/2015/08/23/building-single-page-applications-using-web-api-and-angularjs-free-e-book/",
                DatePublished = new DateTime(2015, 9, 23),
                ImageURI = "https://chsakell.files.wordpress.com/2015/08/spa-webapi-angular-011.png",
                Contents = "Single Page Applications are getting more and more attractive nowadays for two basic reasons. Website users have always preferred a fluid user experience than one with page reloads and the incredible growth of several JavaScript frameworks such as angularJS. This growth in conjunction with all the powerful server side frameworks makes Single Page Application development a piece of cake. This post is the online version of the free e-book and describes step by step how to build a production-level SPA using ASP.NET Web API 2 and angularJS. You have two choices. Either grab a coffee and keep going on with this version or simply download the e-book and enjoy it whenever and wherever you want."
            },
            new Post() {
                Id = 2,
                Title = "Dependency injection in WCF",
                Author = "Chris S.",
                AuthorGravatar = "http://1.gravatar.com/avatar/cb562a6de0640f743a272496ccfc210e",
                URI = "http://chsakell.com/2015/07/04/dependency-injection-in-wcf/",
                DatePublished = new DateTime(2015, 7, 4),
                ImageURI = "https://chsakell.files.wordpress.com/2015/07/wcf-dependency-injection-03.png",
                Contents = "Dependency injection is a software design pattern that implements inversion of control for resolving dependencies and is highly recommended for building scalable, testable and maintainable applications. In this very blog we have seen many times this pattern, mostly in ASP.NET MVC and ASP.NET Web API related posts where for example dependencies (data repositories or middle services) were injected into MVC constructors. We haven’t seen this pattern though in applications using the Windows Communication Framework. I decided to write this post cause I believe there many interesting things to cover when it comes to “marry” Dependency Injection and WCF. "
            },
            new Post() {
                Id = 3,
                Title = "ASP.NET Web API Unit Testing",
                Author = "Chris S.",
                AuthorGravatar = "http://1.gravatar.com/avatar/cb562a6de0640f743a272496ccfc210e",
                URI = "http://chsakell.com/2015/05/10/asp-net-web-api-unit-testing/",
                DatePublished = new DateTime(2015, 5, 10),
                ImageURI = "https://chsakell.files.wordpress.com/2015/05/web-api-unit-testing.png",
                Contents = "Unit testing can be beneficial to many aspects in software develepment, from the lowest level that is the source code to the highest level and the end user’s experience. Writing automated tests helps finding defects earlier in the development lifecycle process which leads to fewer late nights or weekend work (happier developers). Since defects are resolved before production, less defects reach end users (happier clients). It also increases reliability of source code, since if the base code doesn’t change all tests should always return the same results. Last but not least, anyone that decides to write unit tests is also forced to write testable code which leads to better software development practices."
            },
            new Post() {
                Id = 4,
                Title = "ASP.NET Web API feat. OData ",
                Author = "Chris S.",
                AuthorGravatar = "http://1.gravatar.com/avatar/cb562a6de0640f743a272496ccfc210e",
                URI = "http://chsakell.com/2015/04/04/asp-net-web-api-feat-odata/",
                DatePublished = new DateTime(2015, 4, 4),
                ImageURI = "https://chsakell.files.wordpress.com/2015/04/webapi-odata-13.png",
                Contents = "OData is an open standard protocol allowing the creation and consumption of queryable and interoperable RESTful APIs. It was initiated by Microsoft and it’s mostly known to .NET Developers from WCF Data Services. There are many other server platforms supporting OData services such as Node.js, PHP, Java and SQL Server Reporting Services. More over, Web API also supports OData and this post will show you how to integrate those two."
            },
            new Post() {
                Id = 5,
                Title = "ASP.NET MVC Solution Architecture – Best Practices",
                Author = "Chris S.",
                AuthorGravatar = "http://1.gravatar.com/avatar/cb562a6de0640f743a272496ccfc210e",
                URI = "http://chsakell.com/2015/02/15/asp-net-mvc-solution-architecture-best-practices/",
                DatePublished = new DateTime(2015, 2, 15),
                ImageURI = "https://chsakell.files.wordpress.com/2015/02/mvc-architecture-01.png",
                Contents = "Choosing the right architecture for Web Applications is a must, especially for large scale ones. Using the default Visual Studio ASP.NET MVC Web Application project templates, adding controllers with Scaffolding options, just to bootstrap your application and create pages and data in just a few minutes, sounds awesome for sure, but let’s be honest it’s not always the right choise. Peeking all the default options, keeping business, data and presentation logic in the same project will impact several factors in your solutions, such as scalability, usability or testability. In this post, will see how to keep things clean, creating a highly loosely coupled ASP.NET MVC Solution, where Data Access, Business and Presentation layers are defined in the right manner. "
            }
        };
    }

Create the Web API controller named PostsController.

public class PostsController : ApiController
    { 
        // GET api/posts
        public IHttpActionResult Get()
        {
            var posts = DataRepository.Posts;
            return Ok(posts);
        }

        // GET api/posts/5
        public IHttpActionResult Get(int id)
        {
            var post = DataRepository.Posts.FirstOrDefault(p => p.Id == id);

            if (post != null)
            {
                return Ok(post);
            }
            else
            {
                return NotFound();
            }
        }

        // POST api/posts
        public IHttpActionResult Post([FromBody]Post post)
        {
            var max = DataRepository.Posts.Max(p => p.Id);
            post.Id = max + 1;

            DataRepository.Posts.Add(post);
            return Ok(post);
        }

        // PUT api/posts/5
        public IHttpActionResult Put(int id, [FromBody]Post post)
        {
            Post _post = DataRepository.Posts.FirstOrDefault(p => p.Id == post.Id);

            if (_post != null)
            {
                for (int index = 0; index < DataRepository.Posts.Count; index++)
                {
                    if (DataRepository.Posts[index].Id == id)
                    {
                        DataRepository.Posts[index] = post;
                        return Ok();
                    }
                }
            }

            return NotFound();
        }

        // DELETE api/posts/5
        public IHttpActionResult Delete(int id)
        {
            if(DataRepository.Posts.Any(p => p.Id == id))
            {
                Post _post = DataRepository.Posts.First(p => p.Id == id);
                DataRepository.Posts.Remove(_post);

                return Ok();
            }

            return NotFound();
        }
    }

The controller implements the basic CRUD operations to be invoked from the anguralJS services. Let’s switch back to the front-end and create an angularJS service able to communicate with the server by sending HTTP requests. Add the following dataService.tsTypeScript file inside the common/services folder.

module app.common.services {

    interface IDataService {
        get(resource: string): ng.IPromise<app.domain.EntityBase[]>;
    }

    export class DataService implements IDataService {

        private httpService: ng.IHttpService;
        private qService: ng.IQService;

        static $inject = ['$http', '$q'];
        constructor($http: ng.IHttpService, $q: ng.IQService) {
            this.httpService = $http;
            this.qService = $q;
        }

        get(resource: string): ng.IPromise<app.domain.EntityBase[]> {
            var self = this;

            var deferred = self.qService.defer();

            self.httpService.get(resource).then(function (result: any) {
                deferred.resolve(result.data);
            }, function (error) {
                deferred.reject(error);
            });

            return deferred.promise;
        }
    }

    angular.module('chsakellBlogApp')
        .service('dataService', DataService);
} 

An interface declares the functionality you want to expose through the class that implements it. On purpose, I haven’t pasted all the code yet so that we can focus on the most important parts first. First thing to notice is that app.domain.EntityBase TypeScript class is accessible because we used the export keyword. The DataService is being injected with two familiar (I hope) services, $http and $q using the relative TypeScript Definitions ng.IHttpService and ng.IQService respectively. Here we can see for the first time the static injection pattern above the constructor declaration. We will follow this pattern in our controllers as well. Let’s see the full service implementation now.

module app.common.services {

    interface IDataService {
        get(resource: string): ng.IPromise<app.domain.EntityBase[]>;
        getSingle(resource: string): ng.IPromise<app.domain.EntityBase>;
        add(resource: string, entity: app.domain.IEntity): ng.IPromise<app.domain.EntityBase>;
        update(resource: string, entity: app.domain.IEntity): ng.IPromise<app.domain.EntityBase>;
        remove(resource: string): ng.IPromise<any>;
    }

    export class DataService implements IDataService {

        private httpService: ng.IHttpService;
        private qService: ng.IQService;

        static $inject = ['$http', '$q'];
        constructor($http: ng.IHttpService, $q: ng.IQService) {
            this.httpService = $http;
            this.qService = $q;
        }

        get(resource: string): ng.IPromise<app.domain.EntityBase[]> {
            var self = this;

            var deferred = self.qService.defer();

            self.httpService.get(resource).then(function (result: any) {
                deferred.resolve(result.data);
            }, function (error) {
                deferred.reject(error);
            });

            return deferred.promise;
        }

        getSingle(resource: string): ng.IPromise<app.domain.EntityBase> {
            var self = this;

            var deferred = self.qService.defer();

            self.httpService.get(resource).then(function (result: any) {
                deferred.resolve(result.data);
            }, function (error) {
                deferred.reject(error);
            });

            return deferred.promise;
        }

        add(resource: string, entity: app.domain.IEntity): ng.IPromise<app.domain.EntityBase> {
            var self = this;
            var deferred = self.qService.defer();

            self.httpService.post(resource, entity)
                .then(function (result) {
                    deferred.resolve(result.data);
                }, function (error) {
                    deferred.reject(error);
                });

            return deferred.promise;
        }

        update(resource: string, entity: app.domain.IEntity): ng.IPromise<app.domain.EntityBase> {
            var self = this;
            var deferred = self.qService.defer();

            self.httpService.put(resource, entity)
                .then(function (data) {
                    deferred.resolve(data);
                }, function (error) {
                    deferred.reject(error);
                });

            return deferred.promise;
        }

        remove(resource: string): ng.IPromise<any> {
            var self = this;

            var deferred = self.qService.defer();

            self.httpService.delete(resource)
                .then(function (data) {
                    deferred.resolve(data);
                }, function (error) {
                    deferred.reject(error);
                });

            return deferred.promise;
        }
    }

    angular.module('chsakellBlogApp')
        .service('dataService', DataService);
} 

As we mentioned, our SPA has only one feature, the Posts, so go ahead and add a new folder named posts under the app. As opposed from the app.ts the controller that displays the posts is named postsCtrl. Add the following TypeScript app/posts/postsCtrl.ts file.

module app.postList {

    interface IPostsViewModel {
        posts: app.domain.IPost[];
        remove(Id: number): void;
    }

    class PostsCtrl implements IPostsViewModel {

        posts: app.domain.IPost[];

        static $inject = ['constantService', 'dataService'];
        constructor(private constantService: app.common.services.ConstantService,
            private dataService: app.common.services.DataService) {
            this.getPosts();
        }

        remove(Id: number): void {
            var self = this; // Attention here.. check 'this' in TypeScript and JavaScript

            this.dataService.remove(this.constantService.apiPostURI + Id)
                .then(function (result) {
                    self.getPosts();
                });
        }

        getPosts(): void {
            this.dataService.get(this.constantService.apiPostURI).then((result: app.domain.IPost[]) => {
                this.posts = result;
            });
        }
    }
    angular.module('chsakellBlogApp')
        .controller('PostsCtrl', PostsCtrl);
}

Here we can see how we injected our custom services constantService and dataService to make the API calls. Any property you need to be exposed through your controller needs to be declared in the interface being implemented. Here we expose a posts property and a remove method. Let’s view the postAddCtrl controller. Create the following file inside the app/posts folder as well.

module app.postAdd {

    interface IAddPostViewModel {
        post: app.domain.IPost;
        add(): void;
    }

    class PostAddCtrl implements IAddPostViewModel {

        post: app.domain.IPost;

        static $inject = ['$location', 'constantService', 'dataService'];
        constructor(private $location: ng.ILocationService,
            private constantService: app.common.services.ConstantService,
            private dataService: app.common.services.DataService) {
        }


        add(): void {
            this.dataService.add(this.constantService.apiPostURI, this.post)
                .then((result: app.domain.IPost) => {
                    alert(result.Title + ' submitted successfully');
                    this.$location.path('/');
                });
        }
    }
    angular.module('chsakellBlogApp')
        .controller('PostAddCtrl', PostAddCtrl);
}

The only new thing to notice here is the way we declared the success callback method we invoke after the post is submitted. We know that on success, we expect a result of type app.domain.IPost and hence we used it. I left the postEditCtrl controller last because it makes uses of an interesting service, the ng.route.IRouteParamsService. The controller needs this service in order to catch the parameter id when we routing to /edit/:id.

module app.postEdit {

    interface IEditPostViewModel {
        post: app.domain.IPost;
        update(): void;
    }

    interface IPostParams extends ng.route.IRouteParamsService {
        id: number;
    }

    class PostEditCtrl implements IEditPostViewModel {

        post: app.domain.IPost;
        private postId: number;

        static $inject = ['$routeParams', '$location', 'constantService', 'dataService'];
        constructor(private $routeParams: IPostParams,
            private $location: ng.ILocationService,
            private constantService: app.common.services.ConstantService,
            private dataService: app.common.services.DataService) {
            this.postId = $routeParams.id;

            dataService.getSingle(this.constantService.apiPostURI + this.postId)
                .then((result: app.domain.IPost) => {
                    this.post = result;
                });
        }

        update(): void {
            this.dataService.update(this.constantService.apiPostURI + this.postId, this.post)
                .then((result: app.domain.IPost) => {
                    this.$location.path('/');
                });
        }
    }
    angular.module('chsakellBlogApp')
        .controller('PostEditCtrl', PostEditCtrl);
}

The key to capture route params is to create an interface that extends the ng.route.IRouteParamsService service. Any additional route parameter you would like to be exposed by the route, needs to be declared in the IPostParams interface. Those controllers we created are bound to certain templates inside the app/posts/ folder. I don’t want to paste all the html code here so please get the following templates directly from Github. I have also created a site.css stylesheet file inside a Styles folder:

I will paste though the initial code for Views/Home/Index.cshtml page, which is the landing page that bootstraps our Single Page Application. The reason I want to paste the code is to remind you that we haven’t finished yet building our application. We want to process all the JavaScript and CSS files automatically with Gulp and not manually paste them as follow.


@{
    Layout = null;
}

<!DOCTYPE html>

<html ng-app="chsakellBlogApp">
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <link href="~/bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
    <link href="~/Styles/site.css" rel="stylesheet" />

    <script src="~/bower_components/jquery/dist/jquery.js"></script>
    <script src="~/bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/bower_components/angular/angular.js"></script>
    <script src="~/bower_components/angular-route/angular-route.js"></script>

    <script src="~/app/app.js"></script>
    <script src="~/app/domain/IEntity.js"></script>
    <script src="~/app/domain/Post.js"></script>
    <script src="~/app/common/services/constantService.js"></script>
    <script src="~/app/common/services/dataService.js"></script>
    <script src="~/app/posts/postsCtrl.js"></script>
    <script src="~/app/posts/postAddCtrl.js"></script>
    <script src="~/app/posts/postEditCtrl.js"></script>

</head>
<body>
    <div id="wrap">
        <div id="main" class="container">
            <div class="navbar navbar-default navbar-fixed-top">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand active" href="#/">TypeScript SPA</a>
                </div>
                <div class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <li><a href="#/add" class="active">Add Post<i class="fa fa-info-circle fa-fw pull-right"></i></a></li>
                        <li><a href="#/">About<i class="fa fa-info-circle fa-fw pull-right"></i></a></li>
                    </ul>
                    <ul class="nav navbar-nav navbar-right" ng-if="userData.isUserLoggedIn">
                        <li class="userinfo"><a href="#/">{{username}}<i class="fa fa-user fa-fw"></i></a></li>
                    </ul>
                    <a href="https://www.facebook.com/chsakells.blog" target="_blank" class="pull-right">
                        <img src="https://chsakell.files.wordpress.com/2015/08/facebook.png" />
                    </a>
                    <a href="https://twitter.com/chsakellsBlog" target="_blank" class="pull-right">
                        <img src="https://chsakell.files.wordpress.com/2015/08/twitter-small.png" />
                    </a>
                </div>
            </div>

            <ng-view></ng-view>
        </div>
    </div>
    <footer class="navbar navbar-fixed-bottom">
        <div class="text-center">
            <h4 class="white">
                <a href="http://chsakell.com/" target="_blank">chsakell's Blog</a>
                <i>Anything around ASP.NET MVC,Web API, WCF, Entity Framework & C#</i>
            </h4>
        </div>
    </footer>
</body>
</html>

When you drag a TypeScript file inside an html page, Visual Studio will adds the corresponding JavasScript transpiled file. If you wish you can build and run your application to check how it looks like.
typescript-gulp-bower-11
typescript-gulp-bower-12

Entering Gulp

Let’s take a look again the JavaScript references that our main page requires.

    <link href="~/bower_components/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
    <link href="~/Styles/site.css" rel="stylesheet" />

    <script src="~/bower_components/jquery/dist/jquery.js"></script>
    <script src="~/bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/bower_components/angular/angular.js"></script>
    <script src="~/bower_components/angular-route/angular-route.js"></script>

    <script src="~/app/app.js"></script>
    <script src="~/app/domain/IEntity.js"></script>
    <script src="~/app/domain/Post.js"></script>
    <script src="~/app/common/services/constantService.js"></script>
    <script src="~/app/common/services/dataService.js"></script>
    <script src="~/app/posts/postsCtrl.js"></script>
    <script src="~/app/posts/postAddCtrl.js"></script>
    <script src="~/app/posts/postEditCtrl.js"></script>

I divided those references for a reason. In the way I see them, there are three types-groups of references to be injected in our page. The first one, is the CSS required styles, the second one is the External libraries JavaScript files and last but not least, the custom angularJS code typed in TypeScript. So I am thinking to create a specific Gulp task to run for each of those groups: Here’s the task’s description:

  1. Gather all required files
  2. Concatenate them into a single file
  3. Remove any comments or white spaces if possible
  4. Inject the resulted file in the appropriate position in the page

Is that possible? Of course it is, Gulp makes those type of tasks a piece of cake. To start with, add a gulpfile.js file at the root of your Web application. In Visual Studio 2015, you can do it by adding a Gulp Configuration file.
typescript-gulp-bower-13
Here’s how the default gulpfile.js looks like.

/*
This file in the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');

gulp.task('default', function () {
    // place code for your default task here
});

We need to install Gulp before actually run a Gulp task so how we do it? Using NPM through Visual Studio. In the same way you created the gulpfile.js add a new NPM configuration file at the root of your application.
typescript-gulp-bower-14
Change the package.json file created as follow:

{
    "version": "1.0.0",
    "name": "ASP.NET",
    "private": true,
    "devDependencies": {
        "gulp": "3.9.0"
    }
}

By the time you save the file, VS will download Gulp inside a node_modules/gulp folder.
typescript-gulp-bower-15
In case you weren’t in Visual Studio IDE and you wanted to download the dependencies, all you had to do is open a console, navigate to application’s root folder where the package.json file exists, and type the following command:

npm install

Just to make sure Gulp has been setup correctly change the gulpfile.js file as follow and run the task through the Task Runner Explorer.

var gulp = require('gulp');

gulp.task('default', function () {
    console.log('Hello Gulp!');
});

typescript-gulp-bower-16
In case you wanted to run the default task outside Visual Studio you could type the following command in console, in the same way we did with NPM.

gulp default

typescript-gulp-bower-17
Both of those commands assume that you have NPM and Gulp installed as we described at the beginning of this post. We didn’t say anything about how Gulp works but we will after we create the first task. The first task is going to process the CSS files and finally inject the resulted file in the Index.cshtml page. Let’s view the task.

var gulp = require('gulp');
var inject = require('gulp-inject');
var concat = require('gulp-concat');
var print = require('gulp-print');

gulp.task('css-task', function () {
    var target = gulp.src('./views/home/index.cshtml');

    var customCssStream = gulp.src(['./bower_components/bootstrap/dist/css/bootstrap.min.css',
                                    './Styles/site.css']);

    return target
        .pipe(inject(
            customCssStream.pipe(print())
            .pipe(concat('appStyles.css'))
            .pipe(gulp.dest('.build/css')), { name: 'styles' })
            )
        .pipe(gulp.dest('./views/home/'));
});

I should have highlighted all the lines cause they all require explanations. First of all we create references to the Gulp-plugins that we are going to use. Those plugins must be installed via NPM as well (I will show you the updated package.json later). You create a task using the gulp.task API passing the name of the task and a function which defines what you want the task to do. You use the gulp.src API to define the source files to be contained into the stream. Here we used the bootstrap and the custom css file for our SPA. Next we created the actuall task to be executed. Despite the fact that it seems that the inject command is the first on the pipeline, what’s is inside it comes first. Hence, first we get the css files from the customCssStream and we print them. I added the printing so that you can see that you are processing the right files. Then we concatenate those two files by using the concat Gulp-plugin. The parameter declares the resulted file name. Next, we copy the appStyles.css file inside a .build/css folder using the gulp.dest command. Finally the resulted file is being injected into a region named styles that we defined in our Views/Home/Index.cshtml page, using the inject Gulp-plugin. For all this to work, first you need to install the required plugins using NPM.

{
    "version": "1.0.0",
    "name": "ASP.NET",
    "private": true,
    "devDependencies": {
        "gulp": "3.9.0",
        "gulp-concat": "2.6.0",
        "gulp-inject": "2.2.0",
        "gulp-print": "1.2.0"
    }
}

Add the region for CSS file to be injected into Index.cshtml

<!-- styles:css -->
<!-- endinject -->

When you run the task, the resulted file will be injected in the page as follow:

<!-- styles:css -->
<link rel="stylesheet" href="/.build/css/appStyles.css">
<!-- endinject -->

Of course, you can remove the CSS references we manually added before.
typescript-gulp-bower-18
Let’s proceed with the vendor’s Gulp task.

var gulp = require('gulp');
var inject = require('gulp-inject');
var concat = require('gulp-concat');
var print = require('gulp-print');
var angularFilesort = require('gulp-angular-filesort');

gulp.task('vendors-task', function () {
    var target = gulp.src('./views/home/index.cshtml');

    var vendorStream = gulp.src(['./bower_components/angular-route/angular-route.js',
                                 './bower_components/angular/angular.js',
                                 './bower_components/bootstrap/dist/js/bootstrap.js',
                                 './bower_components/jquery/dist/jquery.js']);

    return target
        .pipe(inject(
            vendorStream.pipe(print())
                        .pipe(angularFilesort())
                        .pipe(concat('vendors.js'))
                        .pipe(gulp.dest('.build/vendors')), { name: 'vendors' }))
        .pipe(gulp.dest('./views/home/'));
});

When coding in angularJS it’s very important to inject files in the right order. This is why we used a Gulp plugin named gulp-angular-filesort. In case you didn’t noticed, on purpose I declared the stream files in wrong order. Comment out the line that sorts the files and your application will break. Moreover, if you wish to see the order that Gulp injects the files, simply comment out the concatenate action. Now let’s see the changes in the package.json and Index.cshtml.

{
    "version": "1.0.0",
    "name": "ASP.NET",
    "private": true,
    "devDependencies": {
        "gulp": "3.9.0",
        "gulp-concat": "2.6.0",
        "gulp-uglify": "1.4.1",
        "gulp-inject": "2.2.0",
        "gulp-print": "1.2.0",
        "gulp-angular-filesort": "1.1.1"
    }
}
<!-- vendors:js -->
    <script src="/.build/vendors.js"></script>
<!-- endinject -->

typescript-gulp-bower-19
As you can see we installed the gulp-uglify plugin. We will use it in the last task for all of our custom JavaScript transpiled files.

var gulp = require('gulp');
var inject = require('gulp-inject');
var concat = require('gulp-concat');
var print = require('gulp-print');
var angularFilesort = require('gulp-angular-filesort');
var uglify = require('gulp-uglify');

gulp.task('spa-task', function () {
    var target = gulp.src('./views/home/index.cshtml');

    var appDomainStream = gulp.src(['./app/domain/*.js']);
    var appStream = gulp.src(['./app/*.js', './app/posts/*.js', './app/common/services/*.js']);

    return target
                .pipe(inject(appDomainStream
                        .pipe(print())
                        .pipe(concat('domain.js'))
                        .pipe(uglify())
                        .pipe(gulp.dest('.build/spa')), { name: 'domain' }))
                        .pipe(gulp.dest('./views/home/'))
                .pipe(inject(appStream
                        .pipe(print())
                        .pipe(concat('app.js'))
                        .pipe(uglify())
                        .pipe(gulp.dest('.build/spa')), { name: 'app' }))
                        .pipe(gulp.dest('./views/home/'))
});
<!-- domain:js -->
    <script src="/.build/spa/domain.js"></script>
<!-- endinject -->
<!-- app:js -->
    <script src="/.build/spa/app.js"></script>
<!-- endinject -->

Be careful when you sorting files with Gulp cause not all files are taken into consideration. For example the domain JavaScript files don’t have any angularJS dependencies so if you try to sort them along with the appStream you may get an error. That’s why we used a different section to render the domain related files first.

That’s it, we finally finished! We have seen so many interesting things on this post but make no mistake: we have only scratched their surface. There are amazing things you can accomplish using those tools and I totally recommend you to study their APIs and plugins. I believe that now you shouldn’t have any problem to kick of any project that has used Bower, Gulp and NPM. You can download the project we built from here. When you open the solution for the first time in Visual Studio 2015, it will automatically detect and install any Bower and NPM dependencies. Build the solution to restore Nuget Packages and run the app!

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

WCF Proxies – From beginner to expert

$
0
0

Windows Communication Foundation is a great framework for building highly secure and reliable services that can integrate across different platforms. One of its great features is that it provides SOAP tooling for automatically creating proxy classes-clients to consume the services. When I say tooling I mean the well known Add Service Reference.. option or the Web Services Description Language Tool (wsdl.exe) that can generate proxy classes. In fact, this is one of the main reasons many developers and architects choose WCF as the base of their Service Oriented Applications. With just one click and no other effort at all, you have the .NET clients ready to be used and consume WCF services. Unfortunately, less developers are aware that proxy clients can be manually (and easily) created giving the opportunity to have full control upon them. This post will show you different ways to create and use that kind of proxies in order to consume your WCF services. Let’s see what we ‘re gonna see in detail:

  • Create and host WCF services: Create a simple WCF service which will be self-hosted in a WPF application.
  • Shared-contract Proxies: Create WCF proxies using a shared WCF Service Contract
  • External Proxies: Create WCF proxies using equivalent Service Contracts with the hosted WCF service
  • Proxy creation methods: Describe how to use ClientBase<T> and ChannelFactory<T> to create WCF proxies
  • Dependency Injection: Proxy injection and deterministic disposal using Autofac IoC Container

wcf-proxies-05
Some basic WCF knowledge is required to follow along this tutorial but not required. Ready? Let’s start!

Create and host WCF services

Open Visual Studio and create a black solution named WcfProxies. First thing we ‘ll do is create the basic data infrastructure for our application. Add a new class library project named WcfProxies.Data in the solution and create a folder named Entities inside it. We ‘ll try to keep things simple as far as data concerns, so that we can focus more on WCF related stuff. Add the following three classes inside the Entities folder you have just created.

public interface IEntity
    {
        int ID { get; set; }
    }
public class Post : IEntity
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public DateTime DateCreated { get; set; }
        public string URI { get; set; }

        public int BlogId { get; set; }

        public int ID
        {
            get { return PostId; }
            set { PostId = value; }
        }
    }
public class Blog : IEntity
    {
        public Blog()
        {
            Posts = new List<Post>();
        }
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string URI { get; set; }
        public string Owner { get; set; }
        public List<Post> Posts { get; set; }
        public int ID
        {
            get { return BlogId; }
            set { BlogId = value; }
        }
    }

We want to enhance Dependency Injection through our application so let’s create an interface that holds the method signatures for retrieving data of type Post and Blog. Add a new folder named Repositories in the same project and create the following files as follow:

public interface IBlogPostRepository
    {
        Post GetPost(int postId);
        Blog GetBlog(int blogId);
        List<Post> GetBlogPosts(int blogId);
    }
public class BlogPostRepository : IBlogPostRepository
    {
        public Post GetPost(int postId)
        {
            Post _post = null;

            Blog _blog = _blogs.FirstOrDefault(b => b.Posts.Any(p => p.ID == postId));
            if (_blog != null)
                _post = _blog.Posts.First(p => p.ID == postId);

            return _post;
        }

        public List<Post> GetBlogPosts(int blogId)
        {
            List<Post> _posts = null;

            Blog _blog = _blogs.FirstOrDefault(b => b.ID == blogId);
            if (_blog != null)
            {
                _posts = new List<Post>();
                foreach (var post in _blog.Posts)
                    _posts.Add(post);
            }

            return _posts;
        }

        public Blog GetBlog(int blogId)
        {
            return _blogs.FirstOrDefault(b => b.ID == blogId);
        }

        static List<Blog> _blogs = new List<Blog>()
        {
            new Blog()
            {
                BlogId = 1,
                Name = "chsakell's Blog",
                Owner = "Chris S.",
                URI = "http://chsakell.com/",
                Posts = new List<Post>()
                {
                    new Post() {
                        PostId = 1,
                        Author = "Chris S.",
                        DateCreated = DateTime.Now.AddDays(-5),
                        Title = "Building SPA using Web API & angularJS",
                        BlogId = 1,
                        URI = "http://wp.me/p3mRWu-PT"
                    },
                    new Post() {
                        PostId = 2,
                        Author = "Chris S.",
                        DateCreated = DateTime.Now.AddDays(-10),
                        Title = "WCF Security using encrypted tokens",
                        BlogId = 2,
                        URI ="http://wp.me/p3mRWu-CI"
                    }
                }
            },
            new Blog()
            {
                BlogId = 2,
                Name = "DotNetCodeGeeks",
                Owner = ".NET Community",
                URI = "http://www.dotnetcodegeeks.com/",
                Posts = new List<Post>()
                {
                    new Post() {
                        PostId = 3,
                        Author = "Chris S.",
                        DateCreated = DateTime.Now.AddMonths(-2),
                        Title = "TypeScript, AngularJS, Gulp and Bower in Visual Studio 2015",
                        BlogId = 2,
                        URI = "http://www.dotnetcodegeeks.com/2015/09/typescript-angularjs-gulp-and-bower-in-visual-studio-2015.html"
                    },
                    new Post() {
                        PostId = 4,
                        Author = "Chris S.",
                        DateCreated = DateTime.Now.AddMonths(-2),
                        Title = "Dependency injection in WCF",
                        BlogId = 2,
                        URI = "http://www.dotnetcodegeeks.com/2015/08/dependency-injection-in-wcf.html"
                    },
                    new Post() {
                        PostId = 5,
                        Author = "Chris S.",
                        DateCreated = DateTime.Now.AddMonths(-2),
                        Title = "ASP.NET Web API feat. OData",
                        BlogId = 2,
                        URI = "http://www.dotnetcodegeeks.com/2015/06/asp-net-web-api-feat-odata.html"
                    }
                }
            },
        };
    }

To keep things as much as simple as possible, we will use a static list of Blog items when requesting data from WCF Services.

WCF Contracts

Let’s proceed with the WCF Contracts. Add a new class library project named WcfProxies.Contracts in the solution. Make sure you add references to System.ServiceModel and System.Runtime.Serialization assemblies. These two core assemblies are more than enough to create and host WCF services. From now on, you have to add references to these assemblies to any project we will create later on this tutorial. The WcfProxies.Contracts project is very important to our application for two reasons. Firstly, it will hold the WCF Data and Service contracts as well for the WCF Services. Secondly, this is the library that will be used by the shared contract Proxies that we will create later. Add a folder named Data in the new project and create the following Data contract.

    [DataContract]
    public class PostData
    {
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string URI { get; set; }
        [DataMember]
        public string Blog { get; set; }
    }

Data Contracts describe the shape of the data being exchanged via WCF services or in other words, the type of the incoming and returned data to and from the WCF services respectively. In order to make a class serializable, you need to decorate it with the attribute [DataContract]. Later, you decorate any public property you want to be serialized with the attribute [DataMember]. Mind that from the .NET 4.0 and later, if you leave those attributes any public member of the class becomes automatically serializable.
Let’s create the Service Contract now. Add a new folder named Services and create the following class.

    [ServiceContract]
    public interface IBlogPostService
    {
        [OperationContract]
        PostData GetPost(int postId);

        [OperationContract]
        IEnumerable<PostData> GetBlogPosts(int blogId);
    }

A Service Contract describes the operations available in a WCF service. It is simply an interface decorated with the attribute [ServiceContract] and all of its members (methods) that needs to be implemented by the WCF service, decorated with the [OperationContract]. In our example, we have only two operation contracts. One to search for a specific post among all blogs and another to retrieve all posts belonging to a specific blog. And by post I mean the post related data in the form of the Data contract PostData we previously created.

WCF Service

Let’s create the implementation class for the service contract. Add a new class library project named WcfProxies.Services and make sure you add the WCF required assemblies System.ServiceModel and System.Runtime.Serialization. This project also needs references to the WcfProxies.Data and WcfProxies.Contracts projects as well, so go ahead and add them. Most developers are used to name the service contract in a form of IServiceContractName following with the implementation name in the form of ServiceContractName so you would expect here to name the implementation of the IBlogPostService interface BlogPostService. I have never found this useful, especially when hosting services via configuration. Instead, I will name the implementation WCF service BlogPostController which actually denotes exactly that this is where all the logic and processing happens in our WCF Service (I’m pretty sure Web API developers will love this naming convention..). Add the BlogPostController in the new project you have just created.

public class BlogPostController : IBlogPostService
    {
        IBlogPostRepository _blogPostRepository;

        public BlogPostController(IBlogPostRepository blogPostRepository)
        {
            this._blogPostRepository = blogPostRepository;
        }

        public PostData GetPost(int postId)
        {
            PostData _post = null;
            Post _postEntity = _blogPostRepository.GetPost(postId);

            if (_postEntity != null)
            {
                Blog _blog = _blogPostRepository.GetBlog(_postEntity.BlogId);
                _post = new PostData()
                {
                    Title = _postEntity.Title,
                    Author = _postEntity.Author,
                    URI = _postEntity.URI,
                    Blog = _blog.Name
                };
            }

            return _post;
        }

        public IEnumerable<PostData> GetBlogPosts(int blogId)
        {
            List<PostData> _posts = null;
            List<Post> _postEntities = _blogPostRepository.GetBlogPosts(blogId);

            if (_postEntities != null)
            {
                _posts = new List<PostData>();
                foreach (var post in _postEntities)
                {
                    Blog _blog = _blogPostRepository.GetBlog(post.BlogId);
                    _posts.Add(new PostData()
                    {
                        Title = post.Title,
                        Author = post.Author,
                        URI = post.URI,
                        Blog = _blog.Name
                    });
                }
            }

            return _posts;
        }
    }

As you can see, an instance of IBlogPostRepository will be automatically injected in to the service controller. We will configure it right away when we create the WPF hosting application.

Self-host WCF Service

Add a new WPF Application project named WcfProxies.WindowsHost in the solution and make sure you add references to System.ServiceModel and System.Runtime.Serialization assemblies. You also need to reference the WcfProxies.Data, WcfProxies.Contracts and WcfProxies.Services projects as well. If you are not familiar with WPF that’s OK, the from will only hold two buttons to start and stop the WCF Service. Change the MainWindow.xaml code as follow (create it if not exists..).

<Window x:Class="WcfProxies.WindowsHost.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WcfProxies.WindowsHost"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button x:Name="btnStartService" Content="Start Service" HorizontalAlignment="Left" Margin="72,32,0,0" VerticalAlignment="Top" Width="379" Height="57" FontSize="32" Click="btnStartService_Click"/>
        <Button x:Name="btnStopService" Content="Stop Service" HorizontalAlignment="Left" Margin="72,125,0,0" VerticalAlignment="Top" Width="379" Height="57" FontSize="32" Click="btnStopService_Click" IsEnabled="False"/>
        <Label x:Name="lblStatus" Content="Service is stopped." HorizontalAlignment="Left" Margin="71,227,0,0" VerticalAlignment="Top" FontSize="26.667" Foreground="#FFD12323"/>

    </Grid>
</Window>

wcf-proxies-01
Before setting the button events we need to install the Autofac.Wcf integration package in order to set the IoC Container and configure the dependency injection. Install it via Nuget Package Manager by searching the term Autofac.Wcf. Switch to the MainWindow.xaml.cs code behind file and set the IoC Container as follow. We will write the button events code later.

public partial class MainWindow : Window
    {
        ServiceHost _host = null;
        ContainerBuilder builder = null;
        IContainer container = null;

        public MainWindow()
        {
            InitializeComponent();

            // Init Autofac.WCF container
            builder = new ContainerBuilder();
            builder.RegisterType<BlogPostController>()
                    .As<IBlogPostService>();
            builder.RegisterType<BlogPostRepository>()
                    .As<IBlogPostRepository>();

            container = builder.Build();
        }

        private void btnStartService_Click(object sender, RoutedEventArgs e)
        {
            // Nothing at the moment..
        }

        private void btnStopService_Click(object sender, RoutedEventArgs e)
        {
            // Nothing at the moment..
        }
    }

With this configuration when a request to WCF service arrives, an instance of BlogPostRepository will be injected directly to the BlogPostController constructor. Before writing the Start & Stop button events we need to declare what type of service we want to host in the App.config configuration file (create it if not exists). More over, we will define the way clients can consume or talk to this service which means what kind of bindings does the WCF service use.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
  </startup>
  <system.serviceModel>
    <services>
      <service name="WcfProxies.Services.BlogPostController">
        <endpoint address="net.tcp://localhost:9001/BlogPostService"
                  binding="netTcpBinding"
                  contract="WcfProxies.Contracts.Services.IBlogPostService"/>
        <endpoint address="http://localhost:9002/BlogPostService"
                  binding="wsHttpBinding"
                  contract="WcfProxies.Contracts.Services.IBlogPostService"/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

For each service you want to host you need to define a service element with a name attribute equal to the fully qualified type of the service. In the same way the service element stands on the top of a service layer, an endpoint element stands in the service contract layer. So if a WCF service implements 3 different service contracts, you can define each of those contracts to be hosted in a different address, by declaring the respective endpoint. In our case we only have one service contract, the IBlogPostService and we want it to be consumed under two different transport mechanisms and addresses. This service will listen tcp requests at net.tcp://localhost:9001/BlogPostService and http requests at http://localhost:9002/BlogPostService. Now you can see how the naming convention we made for the WCF service helped. Time to view the Start & Stop events code.

private void btnStartService_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                _host = new ServiceHost(typeof(BlogPostController));
                // add using statement: using Autofac.Integration.Wcf;
                _host.AddDependencyInjectionBehavior<IBlogPostService>(container);

                _host.Open();

                this.btnStopService.IsEnabled = true;
                this.btnStartService.IsEnabled = false;
                this.lblStatus.Content = "Service is running..";
                this.lblStatus.Foreground = new SolidColorBrush(Colors.RoyalBlue);
            }
            catch (Exception ex)
            {
                container.Dispose();
                MessageBox.Show(ex.Message);
            }
        }

        private void btnStopService_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                _host.Close();

                this.btnStopService.IsEnabled = false;
                this.btnStartService.IsEnabled = true;
                this.lblStatus.Content = "Service is stopped.";
                this.lblStatus.Foreground = new SolidColorBrush(Colors.Crimson);
            }
            catch (Exception ex)
            {
                container.Dispose();
                MessageBox.Show(ex.Message);
            }
        }

Run the WcfProxies.WindowsHost application as administrator and click the Start button. If prompted to allow access through Windows Firewall click OK. At this moment you should be able to host the WCF service in two different endpoints.
wcf-proxies-02
Finally, it’s time for the esense of this post, create the WCF Proxies!

Shared-contract Proxies

So what exactly is a WCF Proxy anyway? A WCF Proxy is a powerful object capable to communicate with WCF Services which means it can manage all the low level core operations required to establish a connection to a WCF Service. Which are these operations? Let’s take a look at the most important of them.

  1. Handshaking and communication channel parameters setting for establishing a normal communication between the client and the hosting service.
  2. Security conformance that is accept and apply all the security parameters required by the hosted WCF Service in order to be consumed
  3. Valid message transmission that is transmitting messages in the protocol(s) defined and supported by the hosted WCF service

If you were to write all this low level code by yourself then good luck with it, it ‘ll take like ages to make a single WCF call. A WCF Proxy needs to know two more things other than establishing a proper, reliable and secure connection with a WCF service. It needs to know what is out there meaning what operations are available and the shape of the data being exchanged. In other words it needs to know the Data and Service contracts. Now, what if I told you that there is a specific abstract generic class inside the System.ServiceModel assembly that can manage all the pre-mentioned low level stuff for you? And more over, what if the generic type parameter was (accidentally) a type of service contract? Then all of our problems would be magically solved right? Yup, that’s the answer.
Add a new class library project named WcfProxies.Proxies inside the solution and add references to System.ServiceModel and System.Runtime.Serialization assemblies. You need to reference the WcfProxies.Data and the WcfProxies.Contracts projects as well. The WcfProxies.Contracts project will actually hold the shared contracts for the Proxy class we will create right away. Add a folder named Shared inside the new project and create the following class.

public class BlogPostClient : ClientBase<IBlogPostService>, IBlogPostService
    {
        public BlogPostClient(string endpoint)
            : base(endpoint)
        { }

        public BlogPostClient(Binding binding, EndpointAddress address)
            : base(binding, address)
        { }
        public IEnumerable<PostData> GetBlogPosts(int blogId)
        {
            return Channel.GetBlogPosts(blogId);
        }

        public PostData GetPost(int postId)
        {
            return Channel.GetPost(postId);
        }
    }

So the abstract class we were talking about is the ClientBase<T> and here the generic type parameter is a type of IBlogPostService. Mind that since the ClientBase<T> class is per service contract you need to create a proxy class per service contract. We set the wrapper class BlogPostClient inherit the ClientBase<IBlogPostService> and also implement the IBlogPostService interface. Note that this interface is the same service contract we used to host the WCF service and hence the Shared-contract proxy. As you may have noticed, you can access the operation’s methods through the ClientBase<T> Channel property. I have also declared two constructors for the wrapper client that invokes the base respective constructors. Let’s use this client, shall we?

Add a new WPF project named WcfProxies.Clients in the solution and add references to System.ServiceModel and System.Runtime.Serialization assemblies. You also need to reference the WcfProxies.Data, WcfProxies.Contracts and WcfProxies.Proxies projects. Change the MainWindow.xaml (create a WPF window named MainWindow if not exists) contents as follow.

<Window x:Class="WcfProxies.Clients.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WcfProxies.Clients"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="740">
    <Grid>
        <Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="130" Margin="31,41,0,0" Stroke="Black" VerticalAlignment="Top" Width="217"/>
        <Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="130" Margin="257,41,0,0" Stroke="Black" VerticalAlignment="Top" Width="217"/>
        <Button x:Name="btnSharedClient" Content="GetPost(1)" HorizontalAlignment="Left" Margin="48,87,0,0" VerticalAlignment="Top" Width="177" Height="55" FontSize="21.333" Click="btnSharedClient_Click" Background="White"/>
        <Button x:Name="btnNonSharedClient" Content="GetBlogPosts(2)" HorizontalAlignment="Left" Margin="276,87,0,0" VerticalAlignment="Top" Width="177" Height="55" FontSize="21.333" Click="btnNonSharedClient_Click" Background="White"/>
        <Label x:Name="labelShared" Content="Shared Client" HorizontalAlignment="Left" Margin="100,56,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.622,-0.497"/>
        <Label x:Name="labelNonShared" Content="Non Shared Client" HorizontalAlignment="Left" Margin="316,56,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.622,-0.497"/>
        <ListBox x:Name="lbxResult" HorizontalAlignment="Left" Height="72" Margin="31,191,0,0" VerticalAlignment="Top" Width="670"/>
        <Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="130" Margin="484,41,0,0" Stroke="Black" VerticalAlignment="Top" Width="217"/>
        <Label x:Name="labelNonShared_Copy" Content="Non Shared Client Injected" HorizontalAlignment="Left" Margin="523,56,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.622,-0.497"/>
        <Button x:Name="btnNonSharedClientInjected" Content="GetBlogPosts(1)" HorizontalAlignment="Left" Margin="507,87,0,0" VerticalAlignment="Top" Width="177" Height="55" FontSize="21.333" Click="btnNonSharedClientInjected_Click" Background="White"/>

    </Grid>
</Window>

wcf-proxies-03
Before setting the shared client‘s button event, we need to configure the WCF proxy. In WCF you can configure services and clients in two ways, either using a configuration file (Web.config or App.config) using a <system.serviceModel> element or programmatically. If you recall, the proxy class we created had two constructors, one that accepts an endpoint’s name and another that accepts a Binding and a EndpointAddress object.

public BlogPostClient(string endpoint)
            : base(endpoint)
        { }

        public BlogPostClient(Binding binding, EndpointAddress address)
            : base(binding, address)
        { }

The first constructor uses WCF configuration set in a config file while the second one set the proxy configuration programmatically so as you realize, we will have the opportunity to view both techniques in practice. Change the App.config in the WcfProxies.Clients project as follow:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
    </startup>
  <system.serviceModel>
    <client>
      <endpoint address="net.tcp://localhost:9001/BlogPostService"
                binding="netTcpBinding"
                contract="WcfProxies.Contracts.Services.IBlogPostService"
                name="tcp"/>
      <endpoint address="http://localhost:9002/BlogPostService"
                binding="wsHttpBinding"
                contract="WcfProxies.Contracts.Services.IBlogPostService"
                name="ws"/>
    </client>
  </system.serviceModel>
</configuration>

The service configuration of a WCF client’s side is almost identical with the configuration in the hosting side. Instead of a service element you define a client element for each service you want the proxy to consume. Of course the client doesn’t have a clue about the actual service implementation and hence you wont see any reference to it or any definition in the configuration. It has though knowledge of the service contracts. We declared almost the same endpoints with the slight difference that we have also gave them a name. Guess what, that’s the name for the constructor that accepts an endpoint’s name to start invoking a WCF service. Switch to the code behind file in MainWindows.xaml.cs and set the as follow:

 private void btnSharedClient_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Instance per endpoint name
                BlogPostClient sharedProxy = new BlogPostClient("tcp");

                PostData _firstPost = sharedProxy.GetPost(1);

                if (_firstPost != null)
                {
                    lbxResult.Items.Clear();
                    lbxResult.Items.Add(_firstPost.Title + " by " + _firstPost.Author);
                }

                sharedProxy.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

This client is going to consume the WCF hosted service via a TCP connection due to the endpoint’s name used on the proxy’s constructor. The information will be read from the respective endpoint inside the App.config WCF configuration. Fire the hosting application, click the Start button and then run the client application. Click the share client button and confirm that all works as expected.
wcf-proxies-04
Assuming you wanted to try the alternate technique and configure the client programmatically just simply change the button’s event as follow:

private void btnSharedClient_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                EndpointAddress address = new EndpointAddress("http://localhost:9002/BlogPostService");
                Binding binding = new WSHttpBinding();
                BlogPostClient sharedProxy = new BlogPostClient(binding, address);

                PostData _firstPost = sharedProxy.GetPost(1);

                if (_firstPost != null)
                {
                    lbxResult.Items.Clear();
                    lbxResult.Items.Add(_firstPost.Title + " by " + _firstPost.Author);
                }

                sharedProxy.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

Here on purpose, not only we set the proxy in code, we also configure it to access the WCF service via http using the wsHttpBinding.

External Proxies

The method we have just described assumes that the client application exist in the same solution since client and host share the same service contract. This isn’t though always the case. There might be architectures that clients are outside of the hosting application or even worst the client application doesn’t even belong in the same company that builds and hosts the service. The question now is what does the external proxy require to know or what is the minimum information you need to share with the people on the other side in order to build the proxy and consume the WCF service? The answer is pretty much the same since all a proxy needs to consume a WCF service is the endpoints information. But there’s a small problem here, the other side doesn’t have direct access to your WcfProxies.Contracts assembly and hence cannot use it to create the proxy. Fortunately, this can be resolved through equivalent service contracts. So when two service contracts are equivalent?

Equivalent service contracts
  1. Service contract names match
  2. Operation contract names match
  3. Namespaces match

Let’s simulate the case. Switch to the WcfProxies.Proxies project again and create a folder named External. Create the following service and data contracts in a IBlogPostService.cs file.

namespace WcfProxies.Proxies.External
{
    [ServiceContract]
    public interface IBlogPostService
    {
        [OperationContract]
        PostData GetPost(int postId);

        [OperationContract]
        IEnumerable<PostData> GetBlogPosts(int blogId);
    }

    [DataContract]
    public class PostData
    {
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string URI { get; set; }
        [DataMember]
        public string Blog { get; set; }
    }
}

As you can see the contracts are identical to those exist in the WcfProxies.Contracts but they certainly doesn’t share any code and certainly have different namespaces (we ‘ll discuss this later), so we can safely say that this could have been coded in an entire different application. As I have already mentioned, I wanted to see two methods for creating proxy classes. We saw the one using the ClientBase<T>, now it’s time to describe the one with the ChannelFactory<T>. Switch to the WcfProxies.Clients project and set the second’s button event (btnNonSharedClient_Click) as follow:

private void btnNonSharedClient_Click(object sender, RoutedEventArgs e)
        {
            IEnumerable<WcfProxies.Proxies.External.PostData> _posts = null;

            try
            {
                ChannelFactory<WcfProxies.Proxies.External.IBlogPostService> factory
                    = new ChannelFactory<WcfProxies.Proxies.External.IBlogPostService>("tcpExt");

                //EndpointAddress address = new EndpointAddress("http://localhost:9002/BlogPostService");
                //Binding binding = new WSHttpBinding();
                //ChannelFactory<WcfProxies.Proxies.External.IBlogPostService> factory
                //    = new ChannelFactory<WcfProxies.Proxies.External.IBlogPostService>(binding, address);

                WcfProxies.Proxies.External.IBlogPostService _externalProxy = factory.CreateChannel();
                _posts = _externalProxy.GetBlogPosts(2);

                if (_posts != null)
                {
                    lbxResult.Items.Clear();
                    foreach (var post in _posts)
                    {
                        lbxResult.Items.Add(post.Title + " by " + post.Author);
                    }

                    factory.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

First of all notice that i have intentionally used the full qualified name of the external service and data contracts, so that we assure to simulate the non-shared proxy case. The generic ChannelFactory<T> of type WcfProxies.Proxies.External.IBlogPostService returns an ClientBase implementation of the WcfProxies.Proxies.External.IBlogPostService service contract, in other words.. a proxy! It supports both the constructors we used in the shared case and in the example I used the one that reads the configuration from the config file. I used a tcpExt name so we have to declare it in the App.config file as well.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
    </startup>
  <system.serviceModel>
    <client>
      <endpoint address="net.tcp://localhost:9001/BlogPostService"
                binding="netTcpBinding"
                contract="WcfProxies.Contracts.Services.IBlogPostService"
                name="tcp"/>
      <endpoint address="http://localhost:9002/BlogPostService"
                binding="wsHttpBinding"
                contract="WcfProxies.Contracts.Services.IBlogPostService"
                name="ws"/>
      <endpoint address="net.tcp://localhost:9001/BlogPostService"
                binding="netTcpBinding"
                contract="WcfProxies.Proxies.External.IBlogPostService"
                name="tcpExt"/>
    </client>
  </system.serviceModel>
</configuration>

Fire the hosting and client applications and try to click the Non Shared Client button. You will notice that you won’t get anything back. Why? Cause not all the required conditions for contract equivalency are met. We are missing the namespace matching and to solve this we have two options. The first one is to assign the same Namespace attribute to all of the Service and Data contracts as follow:

[ServiceContract(Namespace = "http://www.chsakell.com/wcfproxies")]
public interface IBlogPostService
// Code omitted
[DataContract(Namespace = "http://www.chsakell.com/wcfproxies")]
public class PostData
// Code omitted

This must be done both in the WcfProxies.Contracts and the WcfProxies.Proxies projects in both service and data contracts. Go ahead changed them, and try again the previous test. This time the proxy will be able to consume properly the WCF service. The second option which will allow you not to pollute your contracts with attributes is to set a the required mapping between the CLR namespaces and the Contract namespaces inside the Assemblyinfo.cs file for both the WcfProxies.Proxies and WcfProxies.Contracts projects. Add the following line of code at the end of the Assemblyinfo.cs file in the WcfProxies.Proxies project.

[assembly: ContractNamespace("http://www.chsakell.com/wcfproxies", 
    ClrNamespace = "WcfProxies.Proxies.External")]

Add the following line at the same file but this time in the WcfProxies.Contracts project.

[assembly: ContractNamespace("http://www.chsakell.com/wcfproxies",
    ClrNamespace = "WcfProxies.Contracts.Data")]
[assembly: ContractNamespace("http://www.chsakell.com/wcfproxies",
    ClrNamespace = "WcfProxies.Contracts.Services")]

Since in this project we used different CLR namespace for the Data and the Service contract, we needed two ContractNamespace attribute declarations. If you decide to go with this method, make sure you do it in both projects and also remove the Namespace attribute from all the contracts in both sides.

Dependency Injection

The last thing I want you to show is how to inject proxies and automatically free resources consumed by them. In order to accomplish it we need to install the Autofac.Wcf integration package as we did in the hosting application, so go ahead and install it via Nuget Package Manager in the WcfProxies.Clients project. Then, create the following wrapper class inside the WcfProxies.Proxies project in the External folder.

public class BlogPostClientExt
    {
        readonly WcfProxies.Proxies.External.IBlogPostService _blogPostService;

        public BlogPostClientExt(IBlogPostService blogPostService)
        {
            this._blogPostService = blogPostService;
        }

        public PostData GetPost(int postId)
        {
            return _blogPostService.GetPost(postId);
        }

        public IEnumerable<PostData> GetBlogPosts(int blogId)
        {
            return _blogPostService.GetBlogPosts(blogId);
        }
    }

Switch to the MainWindow.xaml.cs file in the WcfProxies.Clients project and set the IoC container as follow:

public partial class MainWindow : Window
    {
        ContainerBuilder builder = null;
        IContainer container = null;

        public MainWindow()
        {
            InitializeComponent();

            // Init Autofac.WCF container
            builder = new ContainerBuilder();
            // Register the channel factory for the service. Make it
            // SingleInstance since you don't need a new one each time.
            builder
              .Register(c => new ChannelFactory<WcfProxies.Proxies.External.IBlogPostService>(
                new WSHttpBinding(),
                new EndpointAddress("http://localhost:9002/BlogPostService")))
              .SingleInstance();
            // Register the service interface using a lambda that creates
            // a channel from the factory. Include the UseWcfSafeRelease()
            // helper to handle proper disposal.
            builder
              .Register(c => c.Resolve<ChannelFactory<WcfProxies.Proxies.External.IBlogPostService>>()
              .CreateChannel())
              .As<WcfProxies.Proxies.External.IBlogPostService>()
              .UseWcfSafeRelease();

            // You can also register other dependencies.
            builder.RegisterType<WcfProxies.Proxies.External.BlogPostClientExt>();

            container = builder.Build();
        }
// Code omitted

Now set the last button click event as follow:

private void btnNonSharedClientInjected_Click(object sender, RoutedEventArgs e)
        {
            IEnumerable<WcfProxies.Proxies.External.PostData> _posts = null;

            try
            {
                using (var lifetime = container.BeginLifetimeScope())
                {
                    var _externalProxyInjected = lifetime.Resolve<WcfProxies.Proxies.External.BlogPostClientExt>();
                    _posts = _externalProxyInjected.GetBlogPosts(1);
                }

                if (_posts != null)
                {
                    lbxResult.Items.Clear();
                    foreach (var post in _posts)
                    {
                        lbxResult.Items.Add(post.Title + " by " + post.Author);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

Discussion

SOAP tooling in or outside of Visual Studio is great but not necessary. We saw in this post that you can create fully functional, extensible and light-weighted WCF proxies in several ways. There are several pros and cons for both SOAP tooling and manual proxy creation so let’s note the most important.

Soap tooling
  • One-click generation option. Proxies can start consuming new operation contracts right away
  • Heavy objects and not easily extensible
  • Cannot share only certain operation contracts existing in a service contract, but always all of them
Manual proxies
  • Light-weighted and fully extensible (Check the IExtensibleDataObject usage)
  • Easy Dependency Injection integration, hence great testability
  • Certain operation contracts can be shared for external clients (in our case you can remove the GetPost operation contract and the external clients could still consume the WCF service)
  • In case of the non-shared proxies, the respective operation contracts must be synchronized (add, edit or remove) after hosting operation contracts change.

Last thing I would like to comment on contract equivalency, is that you can name both Data and Service contracts as you wish, as long as you provide the required name with a attribute as follow:

[ServiceContract(Name = "IBlogPostService")]
public interface IBlogPostServiceRenamed
[DataContract(Name = "PostData")]
public class PostDataRenamed

If you want to read more about Dependency Injection, WCF Unit Testing and IExtensibleDataObject interface, you can read the Depenency injection in WCF post. You can download the source code for this post here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Dependency injection in WCF

$
0
0

Dependency injection is a software design pattern that implements inversion of control for resolving dependencies and is highly recommended for building scalable, testable and maintainable applications. In this very blog we have seen many times this pattern, mostly in ASP.NET MVC and ASP.NET Web API related posts where for example dependencies (data repositories or middle services) were injected into MVC constructors. We haven’t seen this pattern though in applications using the Windows Communication Framework. I decided to write this post cause I believe there many interesting things to cover when it comes to “marry” Dependency Injection and WCF. I will divide this post in the following three main areas:

Solution architecture

We are going to build a Service Oriented Application based on Windows Communication Framework. The architecture is mostly based on the decision that we are going to de-couple client and business entities in our application. Many developers believe that the only way to create WCF clients is to generate them using Visual Studio built in functionality. Of course this method works like a charm but the truth is that it strongly binds both sides of the wire: Proxies will fault if any either client’s data contract or service data contract change. And here is where the power of System.Runtime.Serialization comes into the play supporting Data Contract versioning through the IExtensibleDataObject interface. This interface has one property ExtensionData of type ExtensionDataObject which makes data contract changes non-breakable in both sides. That said we will create both business and client entities in such a way that implement the IExtensibleDataObject interface. WCF Service will serve business entities but proxies (clients) will be able to serialize using their client ones. Business and client entities will share some common properties but nothing stops us to add more or remove some others in either of them. The serialization will work properly. More over you ‘ll see that client entities may have any other functionality you wish such as custom validators which a Web client may need for it’s UI. Data repositories will use business entities mapped to the database using Entity Framework. For this example we ‘ll use Entity Framework in an hybrid way that is design the database in SQL Server and manually add later the respective properties in our business entities. Let’s take a look at the architecture of our application.
wcf-dependency-injection-03

Let’s start building our SOA application. Create an empty solution named WCF.DependencyInjection and add the following solution folders with their respective projects:

  1. Business
    • Business.Entities: Class library (Contains Service side Entities)
    • Business.Services: Class library (Contains WCF Service contracts and implementations)
  2. Client
    • Client.Contracts: Class library (Contains the relevant Client side WCF Contracts)
    • Client.Entities: Class library (Contains client side Entities)
    • Client.Proxies: Class library (Contains WCF client proxies)
  3. Core
    • Core.Common: Class library (Contains Common classes – extensions for our solution)
  4. Hosts
    • WCF.DependencyInjection.Web: Empty ASP.NET Web application (Hosts WCF Services)
  5. Data Access
    • Data.Core: Class library (Contains Entity Framework configurations, Data repositories etc..)
  6. Tests
    • Test.Proxies: Class library (Contains NUnit tests for our solution)

Business.Entities

Switch to Business.Entities and create the following two Entities which will be used as Data Contracts at the business side of the wire.

 [DataContract]
    public class Blog : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public string Owner { get; set; }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }
[DataContract]
    public class Article : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Contents { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public int BlogID { get; set; }

        [DataMember]
        public int ContentLength
        {
            get
            {
                return Contents.Length;
            }
            set { }
        }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }

As you can see both of our entities implement the IExtensibleDataObject interface in order to support Round-Tripping (Forward-Compatible Data Contracts). The same will apply in Client entities too. You need to add reference to System.Runtime.Serialization assembly. In fact, go and add this reference to all of our projects.

Data.Core

We need to create the data access repositories before move to WCF Services so switch to Data.Core project, add reference to Business.Entities and install Entity Framework through Nuget Packages. At this point you will allow me not to paste all the required code for this layer since we have seen the same code implementing generic data repositories in many other posts of this blog. Instead, I will show you only the most important parts that needs to be explained for this solution. More over, I will provide you links for all not shown classes from my github account. Of course once again, all the source code for this post is available (you ‘ll find a link at the bottom of this post).
wcf-dependency-injection-02
Let’s see Entity Framework configurations for our entities.

public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            HasKey(b => b.ID);
            Property(b => b.Name).IsRequired().HasMaxLength(100);
            Property(b => b.URL).IsRequired().HasMaxLength(200);
            Property(b => b.Owner).IsRequired().HasMaxLength(50);

            Ignore(b => b.ExtensionData);
        }
    }
public class ArticleConfiguration : EntityTypeConfiguration<Article>
    {
        public ArticleConfiguration()
        {
            HasKey(a => a.ID);
            Property(a => a.Title).IsRequired().HasMaxLength(100);
            Property(a => a.Contents).IsRequired();
            Property(a => a.Author).IsRequired().HasMaxLength(50);
            Property(a => a.URL).IsRequired().HasMaxLength(200);

            Ignore(a => a.ExtensionData);
            Ignore(a => a.ContentLength);
        }
    }

It’s obvious that I just wanted to point that ExtensionData will not be mapped to a database table column. Now let’s see the DbContext class that we need to access table records through Entity Framework:

public class DIContext : DbContext
    {
        public DIContext()
            : base("DIContext")
        {
            Database.SetInitializer<DIContext>(null);
        }

        public DbSet<Blog> BlogSet { get; set; }
        public DbSet<Article> ArticleSet { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }

We turned off the database initializer and we told Entity Framework not to use the default naming convention when accessing database. Tables will have the same name as our entity classes. Here are all the other required classes:

  1. Data.Core.Infrastructure
  2. Data.Core.Repositories

Database

Let’s see WCFDI database we need for our example. Run the following script to create the database and insert some records as well.

USE [master]
GO
/****** Object:  Database [WCFDI] ******/
CREATE DATABASE [WCFDI]
GO
USE [WCFDI]
GO

-- Create Blog Table
CREATE TABLE [dbo].[Blog](
	[ID] [int] NOT NULL,
	[Name] [nvarchar](100) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[Owner] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Blog] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


-- Crate Article Table
CREATE TABLE [dbo].[Article](
	[ID] [int] NOT NULL,
	[Title] [nvarchar](100) NOT NULL,
	[Contents] [nvarchar](max) NOT NULL,
	[Author] [nvarchar](50) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[BlogID] [int] NOT NULL,
 CONSTRAINT [PK_Article] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
-- Create FK
GO
ALTER TABLE [dbo].[Article]  WITH CHECK ADD  CONSTRAINT [FK_Article_Blog] FOREIGN KEY([BlogID])
REFERENCES [dbo].[Blog] ([ID])
GO
ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_Article_Blog]
GO

-- Add some records
INSERT INTO dbo.Blog
        ( ID, Name, URL, Owner )
VALUES  ( 1, -- ID - int
          N'chsakells blog', -- Name - nvarchar(100)
          N'http://chsakell.com', -- URL - nvarchar(200)
          N'Chris Sakellarios'  -- Owner - nvarchar(50)
          )

INSERT INTO dbo.Article
        ( ID ,
          Title ,
          Contents ,
          Author ,
          URL ,
          BlogID
        )
VALUES  ( 1 , -- ID - int
          N'WCF Dependency Injection' , -- Title - nvarchar(100)
          N'Dependency injection is a software design pattern that implements..' , -- Contents - nvarchar(max)
          N'Christos Sakellarios' , -- Author - nvarchar(50)
          N'http://chsakell.com/2015/07/03/dependency-injection-in-wcf/' , -- URL - nvarchar(200)
          1  -- BlogID - int
        )

Business.Services

Now that we have done with the Data access layer we can proceed implement the core WCF Services that are going to be actually hosted (from Web application or Self hosts in our NUnit Tests). Add references to Data.Core, Business.Entities projects and System.ServiceModel assembly as well. Create a folder named Contracts inside the Business.Services project and add the following Service Contracts

    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }


At the root of this project create Services Implementations:

public class BlogService : IBlogService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IBlogRepository _blogRepository;

        public BlogService(IUnitOfWork unitOfWork, IBlogRepository blogRepository)
        {
            _unitOfWork = unitOfWork;
            _blogRepository = blogRepository;
        }
        public void Add(Blog blog)
        {
            _blogRepository.Add(blog);
            _unitOfWork.Commit();
        }

        public void Update(Blog blog)
        {
            _blogRepository.Update(blog);
            _unitOfWork.Commit();
        }

        public void Delete(Blog blog)
        {
            _blogRepository.Delete(blog);
            _unitOfWork.Commit();
        }

        public Blog GetById(int id)
        {
            return _blogRepository.GetById(id);
        }

        public Blog[] GetAll()
        {
            return _blogRepository.GetAll().ToArray();
        }
    }
public class ArticleService : IArticleService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IArticleRepository _articleRepository;

        public ArticleService(IUnitOfWork unitOfWork, IArticleRepository articleRepository)
        {
            _unitOfWork = unitOfWork;
            _articleRepository = articleRepository;
        }
        public void Add(Article article)
        {
            _articleRepository.Add(article);
            _unitOfWork.Commit();
        }

        public void Update(Article article)
        {
            _articleRepository.Update(article);
            _unitOfWork.Commit();
        }

        public void Delete(Article article)
        {
            _articleRepository.Delete(article);
            _unitOfWork.Commit();
        }

        public Article GetById(int id)
        {
            return _articleRepository.GetById(id);
        }

        public Article[] GetAll()
        {
            return _articleRepository.GetAll().ToArray();
        }
    }

Here we can see for the first time how Data repositories are going to be injected to WCF Services. For this to be completed we ‘ll have to register service’s implementations using an Inversion of Control container. We ‘ll do that later, both in Web host and self hosts using Autofac.

Core.Common

It’s time to create the client side of our SOA but before doing this, let’s create a base class for our client entities that will allow them to provide custom validation using the Fluent Validation. As we have already mentioned, client entities can have much more functionality than the Business Entities, functionality that may be required from client applications, web or desktop ones. For our example, we assume that clients needs to know if an entity is Valid or not based on custom validation rules for the specific client entity. In Core.Common install Fluent Validation through Nuget Packages and create the following class:

public class Validatable
    {
        protected IValidator _validator = null;
        protected IEnumerable<ValidationFailure> _validityErrors = null;

        protected virtual IValidator GetValidator()
        {
            return null;
        }

        public IEnumerable<ValidationFailure> ValidityErrors
        {
            get { return _validityErrors; }
            set { }
        }

        public void Validate()
        {
            if (_validator != null)
            {
                ValidationResult results = _validator.Validate(this);
                _validityErrors = results.Errors;
            }
        }

        public virtual bool IsValid
        {
            get
            {
                if (_validityErrors != null && _validityErrors.Count() > 0)
                    return false;
                else
                    return true;
            }
        }
    }

All that our Client Entities need to have is an override for the GetValidator() virtual method.

Client.Entities

Make sure you also install Fluent Validation package through Nuget Packages and create the following two files.

namespace Client.Entities
{
    public class Blog : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _name;
        string _url;
        string _owner;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Name
        {
            get
            {
                return _name;
            }
            set { _name = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public string Owner
        {
            get
            {
                return _owner;
            }
            set { _owner = value; }
        }

        #endregion

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new BlogValidator();
        }
        #endregion
    }

    // Validator Class
    class BlogValidator : AbstractValidator<Blog>
    {
        public BlogValidator()
        {
            RuleFor(b => b.Name).NotEmpty();
            RuleFor(b => b.Owner).NotEmpty();
            RuleFor(b => b.ID).GreaterThan(0);
        }
    }
}
namespace Client.Entities
{
    public class Article : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _title;
        string _contents;
        string _author;
        string _url;
        int _blogID;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Title
        {
            get
            {
                return _title;
            }
            set { _title = value; }
        }
        public string Contents
        {
            get
            {
                return _contents;
            }
            set { _contents = value; }
        }
        public string Author
        {
            get
            {
                return _author;
            }
            set { _author = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public int BlogID
        {
            get
            {
                return _blogID;
            }
            set { _blogID = value; }
        }
        #endregion 

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new ArticleValidator();
        }
        #endregion
    }

    // Validator Class
    class ArticleValidator : AbstractValidator<Article>
    {
        public ArticleValidator()
        {
            RuleFor(a => a.Author).NotEmpty();
            RuleFor(a => a.BlogID).GreaterThan(0);
            RuleFor(a => a.Contents).NotEmpty();
        }
    }
}

I believe it’s getting clearer now what we are trying to achieve. Those two classes will be used for serializing WCF call results. Clients won’t be tightly bound to WCF Business side entities. The next thing to do, is to create the relevant Client side Service Contracts that use Client Entities.

Client.Contracts

Switch to Client.Contracts and add references to Client.Entities and Core.Common projects. You also need to add reference to System.ServiceModel assembly. Create the following two contracts:

namespace Client.Contracts
{
    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
}
namespace Client.Contracts
{
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }
}

Seems familiar? Of course it does, it’s the same contracts we added in the business side of our SOA except that uses Client Entities.

Client.Proxies

We have created the client service contracts but we haven’t provided yet implementations for those interfaces. Guess what, the classes that will implement those contracts are the WCF Client proxies. Switch to Client.Proxies and add references to Client.Entities, Core.Common, Client.Contracts projects and System.ServiceModel assembly. Now provide the following implementations:

public class BlogClient : ClientBase<IBlogService>, IBlogService
    {
        #region IBlogService implementation
        public void Add(Blog blog)
        {
            Channel.Add(blog);
        }

        public void Update(Blog blog)
        {
            Channel.Update(blog);
        }

        public void Delete(Blog blog)
        {
            Channel.Delete(blog);
        }

        public Blog GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Blog[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }
public class ArticleClient : ClientBase<IArticleService>, IArticleService
    {
        #region IArticleService Members
        public void Add(Article article)
        {
            Channel.Add(article);
        }

        public void Update(Article article)
        {
            Channel.Update(article);
        }

        public void Delete(Article article)
        {
            Channel.Delete(article);
        }

        public Article GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Article[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion 

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }

Business Entities – Client Entities Deserialization

So far so good, but yet we haven’t completed all the missing parts relating to Deserializing Client entities to Business Entities and vise versa. Take a look at the Article entity in both sides, Client and Business. First of all notice that Business Article has DataContract and DataMember attributes to inform what parts of the class will be used for serialization. There isn’t such thing in the client side entity which means that all public properties will be used for serialization. I made that choice in order not to pollute the class since I may add even more functionality later. More over, If you are familiar with WCF Services, you will be aware that client and service data contracts must have the same namespace otherwise the deserialization will fail. At this point our client and business entities belong to different namespaces so it won’t work. We could add a namespace attribute value in the business entities like this:

[DataContract(Namespace="http://www.chsakell.com/WCF_DI")]

But this would require to insert the same attributes in the client side which is something I ‘d like to avoid. To solve this, there is a trick you can do, and register the common namespace for those entities. Switch to the AssemblyInfo.cs file in both of the projects and register there the namespace you want to be used for serialization-deserialization during WCF calls, by adding the following entries.

[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Business.Entities")]
[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Client.Entities")]

WCF Web Host

Switch to WCF.DependencyInjection.Web Web application to host our WCF Services. You need to add references to Business.Services, Data.Core projects and System.ServiceModel assembly. You also need to install Entity Framework and Autofac.Integration.Wcf packages from Nuget Packages as well.
Add two WCF Service files named BlogService.svc and ArticleService. Make sure you remove the *.svc.cs file created for each service and just leave the .svc file. Alter those files as follow:

<%@ ServiceHost 
    Language="C#" 
    Debug="true" 
    Service="Business.Services.Contracts.IBlogService, Business.Services" 
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>
<%@ ServiceHost 
    Language="C#" 
    Debug="true" 
    Service="Business.Services.Contracts.IArticleService, Business.Services" 
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

It’s time to register our services and implementations using Autofac in order for Dependency Injection to work. Create the following Bootstrapper class:

public static class Bootstrapper
    {
        /// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services
            
            builder.RegisterType<BlogService>().As<IBlogService>();
            builder.RegisterType<ArticleService>().As<IArticleService>();

            // register repositories and UnitOfWork
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
            builder.RegisterType<DbFactory>().As<IDbFactory>();
            builder.RegisterType<ArticleRepository>().As<IArticleRepository>();
            builder.RegisterType<BlogRepository>().As<IBlogRepository>();

            // build container
            return builder.Build();
        }
    }

You need to register this container in Application start event so create a Global.asax file (if not exists) and paste the following code that uses Autofac.Integration.Wcf.

protected void Application_Start(object sender, EventArgs e)
        {
            IContainer container = Bootstrapper.BuildContainer();
            AutofacHostFactory.Container = container;
        }

Don’t forget to add a connection string in Web.config file that points to WCFDI database we created before, to be used by Entity Framework.

  <connectionStrings>
    <add name="DIContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=WCFDI;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

At this point you should be able to use dependency injection concerning injecting data repositories to your WCF services.

Test.Proxies

We have done a lot of work structuring our SOA application and now it’s time to test all of our components. We need to test that data repositories are indeed injected and of course view how to configure proxy dependency injection. Switch to Test.Proxies project and for start add references to all of the other projects except the web application. Now install the following Nuget packages:

  1. Autofac WCF Integration
  2. Moq (We ‘ll mock the data repositories)
  3. NUnit
  4. NUnit Test Adapter

Also add reference to System.ServiceModel assembly.
Before creating the relevant Bootstrapper to be used by WCF self hosts, let’s create a class that needs WCF proxies to make WCF calls. Those proxies will have to be injected by Autofac container rather than be manually created.

public class ClientInjectionClass : Disposable
    {
        public Client.Contracts.IBlogService _blogProxy;
        public Client.Contracts.IArticleService _articleProxy;
        public ClientInjectionClass(Client.Contracts.IArticleService articleServiceProxy,
            Client.Contracts.IBlogService blogServiceProxy)
        {
            this._blogProxy = blogServiceProxy;
            this._articleProxy = articleServiceProxy;
        }

        #region IDisposable
        protected override void DisposeCore()
        {
            base.DisposeCore();
            try
            {
                (_blogProxy as Client.Proxies.BlogClient).CleanUp();

                (_articleProxy as Client.Proxies.ArticleClient).CleanUp();
            }
            catch
            {
                _blogProxy = null;
                _articleProxy = null;
            }
        }
        #endregion

        #region Methods

        public Client.Entities.Article[] GetArticles()
        {
            return _articleProxy.GetAll();
        }

        public Client.Entities.Blog GetBlogById(int id)
        {
            return _blogProxy.GetById(id);
        }

        #endregion
    }

Let’s view the Bootstrapper which will mock data repositories, register WCF Services, Client contracts with their respective implementations.

public static class Bootstrapper
    {/// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services
            builder.RegisterType<BlogService>().As<Business.Services.Contracts.IBlogService>();
            builder.RegisterType<ArticleService>().As<Business.Services.Contracts.IArticleService>();

            // register proxies
            builder.Register(c => new ChannelFactory<Client.Contracts.IArticleService>("BasicHttpBinding_IArticleService"))
                .InstancePerLifetimeScope();
            builder.Register(c => new ChannelFactory<Client.Contracts.IBlogService>("BasicHttpBinding_IBlogService"))
                .InstancePerLifetimeScope();

            builder.RegisterType<ArticleClient>().As<Client.Contracts.IArticleService>().UseWcfSafeRelease();
            builder.RegisterType<BlogClient>().As<Client.Contracts.IBlogService>().UseWcfSafeRelease();

            // Unit of Work
            var _unitOfWork = new Mock<IUnitOfWork>();
            builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();
            // DbFactory
            var _dbFactory = new Mock<IDbFactory>();
            builder.RegisterInstance(_dbFactory.Object).As<IDbFactory>();

            //Repositories
            var _articlesRepository = new Mock<IArticleRepository>();
            _articlesRepository.Setup(x => x.GetAll()).Returns(new List<Business.Entities.Article>()
                {
                    new Business.Entities.Article() { 
                        ID = 1, 
                        Author = "Chris Sakellarios", 
                        BlogID = 1, 
                        Contents = "Dependency injection is a software design pattern that implements..", 
                        Title = "WCF Dependency Injection", 
                        URL =" http://chsakell.com/2015/07/03/dependency-injection-in-wcf/"}
                });
            builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();

            var _blogsRepository = new Mock<IBlogRepository>();
            _blogsRepository.Setup(x => x.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Business.Entities.Blog>(
                    id => new Business.Entities.Blog() { 
                        ID = id,
                        Name = "chsakell's blog",
                        Owner = "Chris Sakellarios",
                        URL = "http://chsakell.com/"
                    }));
            builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();

            builder.RegisterType<ClientInjectionClass>();

            // build container
            return builder.Build();
        }
    }

I have highlighted the parts that we register client side service contracts to used for WCF proxy injection because when this is done, clients will search for the respective Endpoint configurations in the configuration file. To make this work, add an App.config file in the Test.Proxies project and paste the following code.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IArticleService" />
        <binding name="BasicHttpBinding_IBlogService" />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:18850/ArticleService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IArticleService"
          contract="Client.Contracts.IArticleService" name="BasicHttpBinding_IArticleService" />
      <endpoint address="http://localhost:18850/BlogService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBlogService"
          contract="Client.Contracts.IBlogService" name="BasicHttpBinding_IBlogService" />
    </client>
  </system.serviceModel>
</configuration>

Create a ProxiesTests.cs to write our NUnit Tests. First of all we need to Setup the self host and the IoC Container before each of our test runs. At the end of the test we can close the host. for those two we ‘ll use NUnit’s Setup and TearDown attributes.

    [TestFixture]
    public class ProxiesTests
    {
        IContainer container = null;
        ServiceHost svcArticleHost = null;
        ServiceHost svcBlogHost = null;
        Uri svcArticleServiceURI = new Uri("http://localhost:18850/ArticleService.svc");
        Uri svcBlogServiceURI = new Uri("http://localhost:18850/BlogService.svc");
        

        [SetUp]
        public void Setup()
        {
            try
            {
                container = Bootstrapper.BuildContainer();

                svcArticleHost = new ServiceHost(typeof(ArticleService), svcArticleServiceURI);
                svcBlogHost = new ServiceHost(typeof(BlogService), svcBlogServiceURI);

                svcArticleHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IArticleService>(container);
                svcBlogHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IBlogService>(container);

                svcArticleHost.Open();
                svcBlogHost.Open();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }

        [TearDown]
        public void TearDown()
        {
            try
            {
                if (svcArticleHost != null && svcArticleHost.State != CommunicationState.Closed)
                    svcArticleHost.Close();

                if (svcBlogHost != null && svcBlogHost.State != CommunicationState.Closed)
                    svcBlogHost.Close();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
            finally
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }
}

Before running tests, make sure you run Visual Studio as administrator for self host to work. Let’s write a test to check that self host works as expected:

        [Test]
        public void test_self_host_connection()
        {
            Assert.That(svcArticleHost.State, Is.EqualTo(CommunicationState.Opened));
            Assert.That(svcBlogHost.State, Is.EqualTo(CommunicationState.Opened));
        }

Test that ArticleClient and BlogClient proxies are injected properly when resolving Client.Contracts.IArticleService and Client.Contracts.IBlogService client service contracts:

        [Test]
        public void test_article_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IArticleService proxy
                = container.Resolve<Client.Contracts.IArticleService>();

                Assert.IsTrue(proxy is ArticleClient);
            }
        }

        [Test]
        public void test_blog_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IBlogService proxy
                = container.Resolve<Client.Contracts.IBlogService>();

                Assert.IsTrue(proxy is BlogClient);
            }
        }

Test proxy state expected behavior:

        [Test]
        public void test_article_proxy_state()
        {
            Client.Contracts.IArticleService proxy;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                CommunicationState state = (proxy as ArticleClient).State;
                Assert.That(state, Is.EqualTo(CommunicationState.Created));

                // Open connection
                (proxy as ArticleClient).Open();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Opened));
                // Close connection
                (proxy as ArticleClient).Close();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            }
        }

Test data repositories injection in WCF Services:

        [Test]
        public void test_article_proxy_getall()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            // Close connection
            if ((proxy as ArticleClient).State == CommunicationState.Opened)
                (proxy as ClientBase<Client.Contracts.IArticleService>).Close();
        }

Test that proxies are injected and cleaned properly in ClientInjectionClass.

        [Test]
        public void test_constructor_injected_proxy()
        {
            ClientInjectionClass _testClass = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                using (_testClass = new ClientInjectionClass(container.Resolve<Client.Contracts.IArticleService>(),
                   container.Resolve<Client.Contracts.IBlogService>()))
                {
                    {
                        Client.Entities.Article[] _articles = _testClass.GetArticles();
                        Client.Entities.Blog _blog = _testClass.GetBlogById(1);

                        Assert.That(_articles.Count(), Is.EqualTo(1));
                        Assert.That(_blog, Is.Not.Null);
                        Assert.That(_blog.IsValid, Is.EqualTo(true));
                    }
                }
            }

            Assert.That((_testClass._articleProxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            Assert.That((_testClass._blogProxy as BlogClient).State, Is.EqualTo(CommunicationState.Closed));
        }

In case you have noticed, I added an extra property named ContentLength at the Business Article entity, a property that doesn’t exist in the client Article entity. Still serialization – deserialization works like a charm due to the IExtensibleDataObject interface the both implement. Let’s write a test to view that Article client holds this property in it’s ExtensionData property. To read this property we need a method that uses reflection and you can view it from here.

        [Test]
        public void test_article_extension_data_not_empty()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            var contentLength = Extensions.GetExtensionDataMemberValue(articles.First(), "ContentLength");

            Assert.That(articles.First().Contents.Length, Is.EqualTo(Int32.Parse(contentLength.ToString())));
        }

Discussion

We have seen many interesting concepts in this article, from WCF Dependency Injection integration and client proxy injection to de-coupled and extensible Data Contracts. There is one thing I would like to discuss here and it has to do about dependency injection in constructors. Days ago, a fellow asked me what if a class required ten or even twenty repositories? Do I have to pollute my constructor that much? The answer is not necessary. There is another more abstract and generic pattern that you can use where you create a generic Factory capable of resolving custom IDataRepository interfaces. In other words, you can create an interface like this:

public interface IDataRepositoryFactory
    {
        T GetCustomDataRepository<T>() where T : IDataRepository;
    }

And then implement it as follow:

public class DataRepositoryFactory : IDataRepositoryFactory
    {
        T IDataRepositoryFactory.GetCustomDataRepository<T>()
        {
            return Container.Resole<T>();
        }
    }

Then all you have to do is register this high level interface using an IoC and use this in your constructors instead of custom ones. When you need a custom repository, for example an IArticleRepository you get it like this:

private IArticleRepository _articleRepository;
private IBlogRepository _blogRepository;
private IDataRepositoryFactory _repositoryFactory;

// constructor
public ArticleService(IDataRepositoryFactory repositoryFactory)
        {
            _repositoryFactory= repositoryFactory;
        }

// method that requires IArticleRepository
public Article[] GetAll()
{
  _articleRepository = _repositoryFactory.GetCustomDataRepository<IArticleRepository>();
  return _articleRepository.GetAll();
}
// method that requires IBlogRepository
public Blog[] GetAll()
{
  _blogRepository= _repositoryFactory.GetCustomDataRepository<IBlogRepository>();
  return _blogRepository.GetAll();
}

That’s it, we ‘re done! I hope you enjoyed reading this post as much as I did writing it. You can download the source code for this post here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Cross-platform Single Page Applications with ASP.NET 5, Angular 2 & TypeScript

$
0
0

ASP.NET 5 and Angular 2 are probably the hottest new frameworks in terms of both of them are entirely re-written from scratch. We, as web developers are more than eager to test both of these two super promising frameworks and get ready to build cutting-edge applications. After writing the Building Single Page Applications using Web API and angularJS post which was fortunately widely liked by thousands of readers, I have started receiving comments in order to upgrade the same application we built on the previous post to ASP.NET 5, Angular 2 and of course Typescript. Though it would be nice to do it and have two different versions of the same application, I have finally decided to build an entirely different application cause it’s always nice to be creative and new stuff give you that opportunity. Once again, this post gonna be quite large so we will break it in to the following sections. You are strongly recommended to grab a cup of coffee before start reading the rest of this post:

  • What are we going to build: Present our Single Page Application application’s features with several screenshots.
  • What are we going to use: Describe the technologies, frameworks, languages and tools we ‘ll use to build the application.
  • Prerequisites: Prepare our development environment to build a cross-platform ASP.NET 5 application.
  • Create and configure the application: Start with an empty ASP.NET 5 application and configure it step by step for using ASP.NET MVC 6. We will also install all the server and client packages we ‘ll need to build the SPA (project.json, package.json (NPM), gulpfile.js (Gulp), bower.json (Bower)).
  • Entity Framework 7: Configure EF 7 Code First migrations and write a database initializer. We will also create the back-end infrastructure Entities, Reposistories and set the Dependency Injection as well.
  • Single Page Application: Start building the Photo Gallery Single Page Application.
  • Discussion: Discuss the choices we made and what comes next.

Are you ready? Let’s start!
aspnet5-agnular2-03

What are we going to build

We will create a Single Page Application to display a gallery’s photos. The landing (default) view will present some information about the app such as the technologies used for building the application.
aspnet5-agnular2-04
When user clicks the Photos tab the corresponding component is being rendered, displaying some photos to the user. Pagination is also supported.
aspnet5-agnular2-05
The Albums tab displays using pagination features the albums existing in the gallery. In order to view this component the user needs to be Authorized. This means that the user will be automatically redirected to the Login component if he hasn’t logged in yet.
aspnet5-agnular2-06
An authenticated user can click and display a specific albums’s photos where he can also remove any of them. Before removing a photo a confirmation popup message appears. More over, a notification service display success or error messages.
aspnet5-agnular2-07
Sign in and Registration components are pretty much self-explanatory. Validation messages will inform the user for required fields.
aspnet5-agnular2-08
aspnet5-agnular2-09
All views should be rendered smoothly in all types of screens (desktops, tablets, mobiles..) giving the sense of a responsive application.
aspnet5-angular2-29
aspnet5-angular2-30

What are we going to use

The essence of post is the technologies, frameworks and tools we are going to use to build the PhotoGallery Single Page Application. It’s gonna be a pure modern web application built with the latest patterns and tools exist at the time this post is being written. Let’s view all of them in detail.

  1. ASP.NET 5: Microsoft’s redesigned, open source and cross-platform framework.
  2. Entity Framework 7: The latest version of the Entity Framework Object Relational Mapper.
  3. Angular 2: The famous AngularJS re-written and re-designed from scratch. Angular 2 is a development platform for building mobile and desktop applications
  4. TypeScript: A typed super-set of JavaScript that compiles to plain JavaScript. One of the best ways to write JavaScript applications.
  5. NPM: A Package Manager responsible to automate the installation and tracking of external packages.
  6. Bower: A Package Manager for the Web and as it’s official website says, optimized for the front-end.
  7. Gulp: A task runner that uses Node.js and works in a Streamline way.
    1. This post assumes that you have at least a basic understanding of the last four pre-mentioned languages & tools (TypeScript, NPM, Bower and Gulp) but in case you don’t, that’s OK, this post will get you ready right away.

      Prerequisites

      In order to build modern and cross-platform Web Applications using the pre-mentioned tools, you need to prepare your development environment first. Let’s view them one by one.

      1. Integrated development environment (IDE): In this post we ‘ll use Visual Studio 2015 which you can download from here but you can also use Visual Studio Code which you can download from here. The latter is one way option for MAC and Linux users. At the end, the project should be able to run outside Visual Studio as well. In case you chose to install Visual Studio 2015, just make sure to specify that you want to include the Microsoft Web Developer Tools.
      2. ASP.NET 5. That’s the super promising, open-source and cross-platform Microsoft’s framework. Depending on which platform you want to start coding ASP.NET 5 applications, you need to follow the respective steps described here. Make sure to follow the exact instructions cause otherwise you may face unexpected errors. In my case I installed ASP.NET 5 on a Windows 10 PRO computer. After doing that I noticed that there was a notification on Visual Studio 2015 about an ASP.NET 5 RC 1 update, so I installed it as well.
        aspnet5-angularjs-14
        And the update..
        aspnet5-agnular2-02
        If Microsoft releases a new version, I will always try to update the source-code.
      3. NPM. We need to install NPM which is the Node.js Package Manager. Though Visual Studio 2015 has build in support for installing NPM or Bower packages, there are many times that it fails and then you need to run manually the command from a console. Installing node.js from here will also install NPM as well. Check it by typing the following command in a console.
        npm -v
        
      4. Bower, Gulp, TypeScript, Typescript Definition Manager. Visual Studio can also run Gulp tasks right from its IDE but we supposed to make this app cross-platform available so make sure you install globally all the following by typing the commands on the console:
        npm install -g bower
        
        npm install -g gulp
        
        npm install -g typescript
        
        npm install -g tsd
        

      Create and configure the application

      It is time to create the PhotoGallery Single Page Application which is going to be an ASP.NET MVC 6 application. In this section will create the solution and configure the ASP.NET 5 application to make use of the ASP.NET MVC 6 services. More over we will setup all the client tools (NPM/Bower packages, Gulp and Typescript) so we can start coding in both server and client side accordingly. In Visual Studio 2015 create an ASP.NET Web Application by selecting the ASP.NET 5 empty template. Here is the default structure of an empty ASP.NET 5 application.
      aspnet5-angularjs-15
      Feel free to remove the Project_Readme.html file. The project.json is a very important file where we declare what packages we want our application to use and what are the frameworks our application will target. As the ASP.NET Team recommends we are going to target both .NET and .NET Core runtimes. In case you haven’t heard of the words DNVM and DNX here’s a few words.

  • DNVM is the is the .NET Version Management tool that allows as to specify and select which version of the .NET Execution Environment (DNX) to use
  • DNX or the .NET Execution Environment is the combination of the SDK and the runtime environment that allows to build cross platform .NET applications and as you may have noticed from the previous screenshot, you can have multiple versions installed on your machine

  • Alter the project.json file as follow and click save.

{
    "webroot": "wwwroot",
    "userSecretsId": "PhotoGallery",
    "version": "1.0.0-*",
    "compilationOptions": {
        "emitEntryPoint": true
    },

    "dependencies": {
        "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
        "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
        "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
        "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
        "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
        "Microsoft.Framework.DependencyInjection": "1.0.0-beta8",
        "EntityFramework.Commands": "7.0.0-rc1-final",
        "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
        "Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
        "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
        "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
        "AutoMapper.Data": "1.0.0-beta1",
        "Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final"
    },

    "commands": {
        "web": "Microsoft.AspNet.Server.Kestrel",
        "ef": "EntityFramework.Commands"
    },

    "frameworks": {
        "dnx451": { },
        "dnxcore50": {
            "dependencies": {
                "System.Security.Cryptography.Algorithms": "4.0.0-beta-23516"
            }
        }
    },

    "exclude": [
        "wwwroot",
        "node_modules"
    ],
    "publishExclude": [
        "**.user",
        "**.vspscc"
    ]
}

By the time you save the file, Visual Studio will try to restore all the packages.
aspnet5-angularjs-16
If you were outside Visual Studio you would have to run the following command:

dnu restore

..where DNU is the .NET Development Utility to build, package and publish DNX projects. Let’s explain a little what we did in the project.json. First we declared some variables such as the webroot which we ‘ll read its value from the gulpfile.js later when we ‘ll declare some tasks. The userSecretsId is required in order to run Entity Framework Code First migrations related commands (acts as a unique ID for the application). Then we declared the dependencies or the packages if you prefer that our application needs. The first two of them are for hosting the app with or without IIS. The .Mvc related packages are required to add and use MVC services to the services container or in other words.. to use MVC 6 in our application. Then we declared the Entity Framework related packages in order to use EF 7 for data accessing. The Extensions packages will help us to easily access some .json files from the code. As you may have noticed we will make use of the Automapper package as well to map EF entities to the relative ViewModel objects. Last but not least is the Microsoft.AspNet.Authentication.Cookies which will ‘ll use in order to set Cookie based authentication. Then we have two commands: The web command is used to host the application using the .NET cross-platform Web server Kestrel. If i was to start my application outside IIS or Visual Studio, all I have to do is run the following command in the terminal.

dnx web

Mind that you need to navigate to the root of your application folder (where the package.json exists) before running this command. We ‘ll use the EF command to run Entity Framework migration commands later on. The last thing I want to comment on the project.json file is the following lines of code.

"frameworks": {
        "dnx451": { },
        "dnxcore50": {
            "dependencies": {
                "System.Security.Cryptography.Algorithms": "4.0.0-beta-23516"
            }
        }
    }

With ASP.NET 5 we can target multiple frameworks something that allow us to host our applications in different hosting environments. We can target the full .NET version and a specific version of the .NET Core as well. You may ask yourself why I have declared the System.Security.Cryptography.Algorithms package just for the .NET Core. Well.. the answer is simple. During the development of this application I found that a specific class wasn’t available for both target frameworks. Of course this means that the application couldn’t compile.
aspnet5-angular2-17
In order to solve this, I had to provide a different implementation per target framework for a specific function. Since I only needed the package for .NET Core, I declared the dependency in the respective framework. But how is it possible to provide different implementation for two target frameworks? Easily, using directives. Let’s see the part of the code that required this resolution.

public string CreateSalt()
        {
            var data = new byte[0x10];
#if DNXCORE50
            var cryptoServiceProvider = System.Security.Cryptography.RandomNumberGenerator.Create();
                cryptoServiceProvider.GetBytes(data);
                return Convert.ToBase64String(data);
#endif
#if DNX451
            using (var cryptoServiceProvider = new RNGCryptoServiceProvider())
            {
                cryptoServiceProvider.GetBytes(data);
                return Convert.ToBase64String(data);
            }
#endif
        }

Awesome right? Let’s continue. Right click your application (not the solution) and add a new item of type NPM Configuration File. Leave its default name package.json. From now and on when I say application root i mean the src/PhotoGallery folder which is the actual root of the PhotoGallery application. Change the contents of the package.json as follow.

{
    "version": "1.0.0",
    "name": "ASP.NET",
    "private": true,
    "dependencies": {
        "@reactivex/rxjs": "5.0.0-alpha.10",
        "angular2": "2.0.0-beta.0",
        "systemjs": "0.19.9",
        "bootstrap": "3.3.5",
        "jquery": "2.1.4",
        "body-parser": "1.14.1",
        "fancybox": "3.0.0",
        "es6-promise": "^3.0.2",
        "es6-shim": "^0.33.3",
        "reflect-metadata": "0.1.2",
        "rxjs": "5.0.0-beta.0",
        "zone.js": "0.5.10"
    },
    "devDependencies": {
        "typescript": "1.6.2",
        "gulp": "3.9.0",
        "gulp-typescript": "2.9.2",
        "gulp-watch": "4.3.5",
        "merge": "1.2.0",
        "del" : "2.1.0"
    }
}

By the time you save the file, Visual Studio will try to restore the NPM packakges into a node_modules folder. Unfortunately for some reason, it may fail to restore all packages.
aspnet5-angular2-18
All you have to do, is open the terminal, navigate at the application’s root folder and run the following command.

npm install

As soon as all packages have been restored, Visual Studio will also detect it and stop complaining (it may crash during package restoring but that’s OK..). As far as what packages we declared, it’s pretty obvious. We needed the required packages for Angular 2, some Gulp plugins to write our tasks and a few other ones such as fancybox or jQuery. Let’s configure the libraries we want to download via Bower. Right click the project and add a new item of type Bower Configuration File. Leave the default name bower.json. You will notice that under the bower.json file a .bowerrc file exists. Change it as follow in order to set the default folder when downloading packages via bower.

{
    "directory": "bower_components"
}

Set the bower.json contents as follow.

{
	"name": "ASP.NET",
	"private": true,
    "dependencies": {
        "bootstrap": "3.3.6",
        "components-font-awesome": "4.5.0",
        "alertify.js": "0.3.11"
    }
}

As soon as you save the file VS will try to restore the dependencies inside a bower_components folder. If it fails for the same reason as before, simple run the following command on the terminal.

bower install

Now you can understand why it’s crucial to have those tools globally installed on our computer. Let’s finish this section by writing the gulp tasks. Right click your project and add a new item of type Gulp Configuration File. Leave its default name gulpfile.js and change its contents as follow.

var gulp = require('gulp'),
    ts = require('gulp-typescript'),
    merge = require('merge'),
    fs = require("fs"),
    del = require('del'),
    path = require('path');

eval("var project = " + fs.readFileSync("./project.json"));
var lib = "./" + project.webroot + "/lib/";

var paths = {
    npm: './node_modules/',
    tsSource: './wwwroot/app/**/*.ts',
    tsOutput: lib + 'spa/',
    tsDef: lib + 'definitions/',
    jsVendors: lib + 'js',
    jsRxJSVendors: lib + 'js/rxjs',
    cssVendors: lib + 'css',
    imgVendors: lib + 'img',
    fontsVendors: lib + 'fonts'
};


var tsProject = ts.createProject('./wwwroot/tsconfig.json');

gulp.task('setup-vendors', function (done) {
    gulp.src([
      'node_modules/angular2/bundles/js',
      'node_modules/angular2/bundles/angular2.*.js*',
      'node_modules/angular2/bundles/http.*.js*',
      'node_modules/angular2/bundles/router.*.js*',
      'node_modules/es6-shim/es6-shim.js*',
      'node_modules/systemjs/dist/*.*',
      'node_modules/jquery/dist/jquery.*js',
      'bower_components/bootstrap/dist/js/bootstrap*.js',
      'node_modules/fancybox/dist/js/jquery.fancybox.pack.js',
      'bower_components/alertify.js/lib/alertify.min.js',
      'node_modules/angular2/bundles/angular2-polyfills.js',
      'node_modules/systemjs/dist/system.src.js',
      'node_modules/rxjs/bundles/Rx.js',
      'node_modules/angular2/bundles/angular2.dev.js'
    ]).pipe(gulp.dest(paths.jsVendors));

    gulp.src([
       'node_modules/rxjs/**/*.js'
    ]).pipe(gulp.dest(paths.jsRxJSVendors));

    gulp.src([
      'bower_components/bootstrap/dist/css/bootstrap.css',
      'node_modules/fancybox/dist/css/jquery.fancybox.css',
      'bower_components/components-font-awesome/css/font-awesome.css',
      'bower_components/alertify.js/themes/alertify.core.css',
      'bower_components/alertify.js/themes/alertify.bootstrap.css',
      'bower_components/alertify.js/themes/alertify.default.css'
    ]).pipe(gulp.dest(paths.cssVendors));

    gulp.src([
      'node_modules/fancybox/dist/img/blank.gif',
      'node_modules/fancybox/dist/img/fancybox_loading.gif',
      'node_modules/fancybox/dist/img/fancybox_loading@2x.gif',
      'node_modules/fancybox/dist/img/fancybox_overlay.png',
      'node_modules/fancybox/dist/img/fancybox_sprite.png',
      'node_modules/fancybox/dist/img/fancybox_sprite@2x.png'
    ]).pipe(gulp.dest(paths.imgVendors));

    gulp.src([
      'node_modules/bootstrap/fonts/glyphicons-halflings-regular.eot',
      'node_modules/bootstrap/fonts/glyphicons-halflings-regular.svg',
      'node_modules/bootstrap/fonts/glyphicons-halflings-regular.ttf',
      'node_modules/bootstrap/fonts/glyphicons-halflings-regular.woff',
      'node_modules/bootstrap/fonts/glyphicons-halflings-regular.woff2',
      'bower_components/components-font-awesome/fonts/FontAwesome.otf',
      'bower_components/components-font-awesome/fonts/fontawesome-webfont.eot',
      'bower_components/components-font-awesome/fonts/fontawesome-webfont.svg',
      'bower_components/components-font-awesome/fonts/fontawesome-webfont.ttf',
      'bower_components/components-font-awesome/fonts/fontawesome-webfont.woff',
      'bower_components/components-font-awesome/fonts/fontawesome-webfont.woff2',
    ]).pipe(gulp.dest(paths.fontsVendors));
});

gulp.task('compile-typescript', function (done) {
    var tsResult = gulp.src([
       "node_modules/angular2/bundles/typings/angular2/angular2.d.ts",
       "node_modules/angular2/bundles/typings/angular2/http.d.ts",
       "node_modules/angular2/bundles/typings/angular2/router.d.ts",
       //"node_modules/@reactivex/rxjs/dist/es6/Rx.d.ts",
       "wwwroot/app/**/*.ts"
    ])
     .pipe(ts(tsProject), undefined, ts.reporter.fullReporter());
    return tsResult.js.pipe(gulp.dest(paths.tsOutput));
});

gulp.task('watch.ts', ['compile-typescript'], function () {
    return gulp.watch('wwwroot/app/**/*.ts', ['compile-typescript']);
});

gulp.task('watch', ['watch.ts']);

gulp.task('clean-lib', function () {
    return del([lib]);
});

gulp.task('build-spa', ['setup-vendors', 'compile-typescript']);

I know that it may seems large and difficult to understand but believe me it’s not. Before explaining the tasks we wrote I will show you the result of those tasks in solution level. This will make it easier to understand what those tasks are trying to accomplish.
aspnet5-angular2-19
Pay attention at the wwwroot/lib folder. This folder is where the client-side dependency packages will end. The lib/css will hold files such as bootstrap.css, font-awsome.css and so on.. Similarly, the lib/js will hold all the JavaScript files we need to write Amgular 2 applications using TypeScript. You may wonder where exactly are we going to write the actual angular SPA? The spa will exist under the wwwroot/app folder with all the custom Typescript files. A specific task named compile-typescript will compile those files into pure JavaScript and place them in to the wwwroot/lib/spa. Let’s view the Gulp tasks:

  1. setup-vendors: Place all required external JavaScript, CSS, images and font files into the corresponding folder under lib.
  2. compile-typescript: Compiles all Typescript files under wwwroot/app folder and place the resulted ones into the respective folder under wwwroot/lib/spa/
  3. watch.ts: A listener to watch for Typescript file changes. If a change happens the run the compile-typescript task. This task will help you a lot during development.
  4. clean-lib: Deletes all the files under wwwroot/lib folder.
  5. build-spa: Runs the setup-vendors and compile-typescript tasks.

It wasn’t that bad right? Now take a notice at the following line inside the gulpfile.js.

var tsProject = ts.createProject('./wwwroot/tsconfig.json');

In order to compile Typescript you need some Typescript specific compiler options. This is what the tsconfig.json file is for. Right click the wwwroot folder and create a new item of type Typescript JSON Configuration file. Leave the default name tsconfig.json and paste the following contents.

{
    "compilerOptions": {
        "noImplicitAny": false,
        "module": "system",
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "noEmitOnError": false,
        "removeComments": false,
        "sourceMap": true,
        "target": "es5"
    },
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}

This configuration file not only will be used for compilation options but it will also be used from Visual Studio for intellisense related Typescript issues. Build the application and open the Task-runner window. Run the build-spa or the setup-vendors task. I know there are no Typescript files to compile but you can see the automatically created folders under wwwroot/lib with the files we defined. In case you have any troubles running the task, you can open a terminal and run the task as follow:

gulp setup-vendors

aspnet5-angular2-20

Entity Framework 7

The final goal of this section is to configure the services that our application will use, inside the Startup.cs file. At this point if you run the PhotoGallery application you will get a Hello World! message coming from the following code in the Startup class.

public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        }

Let’s start by creating the Entities which will eventually be mapped to a database. Create a folder Entities at the root of the application and paste the following files/classes.

public interface IEntityBase
    {
        int Id { get; set; }
    }
public class Photo : IEntityBase
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Uri { get; set; }
        public virtual Album Album { get; set; }
        public int AlbumId { get; set; }
        public DateTime DateUploaded { get; set; }
    }
public class Album : IEntityBase
    {
        public Album()
        {
            Photos = new List<Photo>();
        }
        public int Id { get; set; }
        public string Title { get; set; }

        public string Description { get; set; }
        public DateTime DateCreated { get; set; }
        public virtual ICollection<Photo> Photos { get; set; }
    }
public class Error : IEntityBase
    {
        public int Id { get; set; }
        public string Message { get; set; }
        public string StackTrace { get; set; }
        public DateTime DateCreated { get; set; }
    }
public class Role : IEntityBase
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
public class UserRole : IEntityBase
    {
        public int Id { get; set; }
        public int UserId { get; set; }
        public int RoleId { get; set; }
        public virtual Role Role { get; set; }
    }
public class User : IEntityBase
    {
        public User()
        {
            UserRoles = new List<UserRole>();
        }
        public int Id { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public string HashedPassword { get; set; }
        public string Salt { get; set; }
        public bool IsLocked { get; set; }
        public DateTime DateCreated { get; set; }

        public virtual ICollection<UserRole> UserRoles { get; set; }
    }

The schema we want to create is very simple. A Photo entity belongs to a single Album and an Album may have multiple Photo entities. A User may have multiple roles through many UserRole entities.
aspnet5-angular2-21
Let’s create the DbContext class that will allow us to access entities from the database. Create a folder named Infrastructure under the root of the application and add the following PhotoGalleryContext class.

public class PhotoGalleryContext : DbContext
    {
        public DbSet<Photo> Photos { get; set; }
        public DbSet<Album> Albums { get; set; }
        public DbSet<User> Users { get; set; }
        public DbSet<Role> Roles { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }
        public DbSet<Error> Errors { get; set; }

        public PhotoGalleryContext(DbContextOptions options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Photos
            modelBuilder.Entity<Photo>().Property(p => p.Title).HasMaxLength(100);
            modelBuilder.Entity<Photo>().Property(p => p.AlbumId).IsRequired();

            // Album
            modelBuilder.Entity<Album>().Property(a => a.Title).HasMaxLength(100);
            modelBuilder.Entity<Album>().Property(a => a.Description).HasMaxLength(500);
            modelBuilder.Entity<Album>().HasMany(a => a.Photos).WithOne(p => p.Album);

            // User
            modelBuilder.Entity<User>().Property(u => u.Username).IsRequired().HasMaxLength(100);
            modelBuilder.Entity<User>().Property(u => u.Email).IsRequired().HasMaxLength(200);
            modelBuilder.Entity<User>().Property(u => u.HashedPassword).IsRequired().HasMaxLength(200);
            modelBuilder.Entity<User>().Property(u => u.Salt).IsRequired().HasMaxLength(200);

            // UserRole
            modelBuilder.Entity<UserRole>().Property(ur => ur.UserId).IsRequired();
            modelBuilder.Entity<UserRole>().Property(ur => ur.RoleId).IsRequired();

            // Role
            modelBuilder.Entity<Role>().Property(r => r.Name).IsRequired().HasMaxLength(50);
        }
    }

You should be able to resolve all the required namespaces because we have already installed the required packages. We ‘ll proceed with the data repositories and a membership service as well. Add a folder named Repositories with a subfolder named Abstract under Infrastructure. Add the following to files/classes.

public interface IEntityBaseRepository<T> where T : class, IEntityBase, new()
    {
        IEnumerable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
        Task<IEnumerable<T>> AllIncludingAsync(params Expression<Func<T, object>>[] includeProperties);
        IEnumerable<T> GetAll();
        Task<IEnumerable<T>> GetAllAsync();
        T GetSingle(int id);
        T GetSingle(Expression<Func<T, bool>> predicate);
        T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);
        Task<T> GetSingleAsync(int id);
        IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
        Task<IEnumerable<T>> FindByAsync(Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Edit(T entity);
        void Commit();
    }

Notice that I have added some operations with includeProperties parameters. I did this cause Entity Framework 7 doesn’t support lazy loading by default and I’ m not even sure if it will in the future. With that kind of operations you can load any navigation properties you wish. For example if you want to load all the photos in an album you can write something like this.

Album _album = _albumRepository.GetSingle(a => a.Id == id, a => a.Photos);

Here we used the following operation.

T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);

..passing as includeProperties the Photos collection of the album.

public interface IAlbumRepository : IEntityBaseRepository<Album> { }

    public interface ILoggingRepository : IEntityBaseRepository<Error> { }

    public interface IPhotoRepository : IEntityBaseRepository<Photo> { }

    public interface IRoleRepository : IEntityBaseRepository<Role> { }

    public interface IUserRepository : IEntityBaseRepository<User>
    {
        User GetSingleByUsername(string username);
        IEnumerable<Role> GetUserRoles(string username);
    }

    public interface IUserRoleRepository : IEntityBaseRepository<UserRole> { }

Add the implementations of those interfaces under the Infrastructure/Repositories folder.

public class EntityBaseRepository<T> : IEntityBaseRepository<T>
            where T : class, IEntityBase, new()
    {

        private PhotoGalleryContext _context;

        #region Properties
        public EntityBaseRepository(PhotoGalleryContext context)
        {
            _context = context;
        }
        #endregion
        public virtual IEnumerable<T> GetAll()
        {
            return _context.Set<T>().AsEnumerable();
        }

        public virtual async Task<IEnumerable<T>> GetAllAsync()
        {
            return await _context.Set<T>().ToListAsync();
        }
        public virtual IEnumerable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = _context.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query.AsEnumerable();
        }

        public virtual async Task<IEnumerable<T>> AllIncludingAsync(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = _context.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return await query.ToListAsync();
        }
        public T GetSingle(int id)
        {
            return _context.Set<T>().FirstOrDefault(x => x.Id == id);
        }

        public T GetSingle(Expression<Func<T, bool>> predicate)
        {
            return _context.Set<T>().FirstOrDefault(predicate);
        }

        public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = _context.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }

            return query.Where(predicate).FirstOrDefault();
        }

        public async Task<T> GetSingleAsync(int id)
        {
            return await _context.Set<T>().FirstOrDefaultAsync(e => e.Id == id);
        }
        public virtual IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate)
        {
            return _context.Set<T>().Where(predicate);
        }

        public virtual async Task<IEnumerable<T>> FindByAsync(Expression<Func<T, bool>> predicate)
        {
            return await _context.Set<T>().Where(predicate).ToListAsync();
        }

        public virtual void Add(T entity)
        {
            EntityEntry dbEntityEntry = _context.Entry<T>(entity);
            _context.Set<T>().Add(entity);
        }

        public virtual void Edit(T entity)
        {
            EntityEntry dbEntityEntry = _context.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Modified;
        }
        public virtual void Delete(T entity)
        {
            EntityEntry dbEntityEntry = _context.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Deleted;
        }

        public virtual void Commit()
        {
            _context.SaveChanges();
        }
    }
public class PhotoRepository : EntityBaseRepository<Photo>, IPhotoRepository
    {
        public PhotoRepository(PhotoGalleryContext context)
            : base(context)
        { }
    }
public class AlbumRepository : EntityBaseRepository<Album>, IAlbumRepository
    {
        public AlbumRepository(PhotoGalleryContext context)
            : base(context)
        { }
    }
public class LoggingRepository : EntityBaseRepository<Error>, ILoggingRepository
    {
        public LoggingRepository(PhotoGalleryContext context)
            : base(context)
        { }

        public override void Commit()
        {
            try
            {
                base.Commit();
            }
            catch { }
        }
    }

Notice that we don’t want to get an exception when logging errors..

public class RoleRepository : EntityBaseRepository<Role>, IRoleRepository
    {
        public RoleRepository(PhotoGalleryContext context)
            : base(context)
        { }
    }
public class UserRoleRepository : EntityBaseRepository<UserRole>, IUserRoleRepository
    {
        public UserRoleRepository(PhotoGalleryContext context)
            : base(context)
        { }
    }
public class UserRepository : EntityBaseRepository<User>, IUserRepository
    {
        IRoleRepository _roleReposistory;
        public UserRepository(PhotoGalleryContext context, IRoleRepository roleReposistory)
            : base(context)
        {
            _roleReposistory = roleReposistory;
        }

        public User GetSingleByUsername(string username)
        {
            return this.GetSingle(x => x.Username == username);
        }

        public IEnumerable<Role> GetUserRoles(string username)
        {
            List<Role> _roles = null;

            User _user = this.GetSingle(u => u.Username == username, u => u.UserRoles);
            if(_user != null)
            {
                _roles = new List<Role>();
                foreach (var _userRole in _user.UserRoles)
                    _roles.Add(_roleReposistory.GetSingle(_userRole.RoleId));
            }

            return _roles;
        }
    }

There are so many ways to implement data repositories that I won’t even discuss. We have seen much better and scalable implementations many times on this blog but that’s more than enough for this application. Let’s create now the two services required for membership purposes. Add a folder named Services under Infrastructure and create a subfolder named Abstact with the following interfaces.

public interface IEncryptionService
    {
        /// <summary>
        /// Creates a random salt
        /// </summary>
        /// <returns></returns>
        string CreateSalt();
        /// <summary>
        /// Generates a Hashed password
        /// </summary>
        /// <param name="password"></param>
        /// <param name="salt"></param>
        /// <returns></returns>
        string EncryptPassword(string password, string salt);
    }
public interface IMembershipService
    {
        MembershipContext ValidateUser(string username, string password);
        User CreateUser(string username, string email, string password, int[] roles);
        User GetUser(int userId);
        List<Role> GetUserRoles(string username);
    }

Add their implementations under Infrastructure/Services folder.

public class EncryptionService : IEncryptionService
    {
        public string CreateSalt()
        {
            var data = new byte[0x10];
#if DNXCORE50
            var cryptoServiceProvider = System.Security.Cryptography.RandomNumberGenerator.Create();
                cryptoServiceProvider.GetBytes(data);
                return Convert.ToBase64String(data);
#endif
#if DNX451
            using (var cryptoServiceProvider = new RNGCryptoServiceProvider())
            {
                cryptoServiceProvider.GetBytes(data);
                return Convert.ToBase64String(data);
            }
#endif
        }

        public string EncryptPassword(string password, string salt)
        {
            using (var sha256 = SHA256.Create())
            {
                var saltedPassword = string.Format("{0}{1}", salt, password);
                byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
                return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
            }
        }
    }

That’s the part of the code I mentioned when installing packages for a specific runtime only.

public class MembershipService : IMembershipService
    {
        #region Variables
        private readonly IUserRepository _userRepository;
        private readonly IRoleRepository _roleRepository;
        private readonly IUserRoleRepository _userRoleRepository;
        private readonly IEncryptionService _encryptionService;
        #endregion
        public MembershipService(IUserRepository userRepository, IRoleRepository roleRepository,
        IUserRoleRepository userRoleRepository, IEncryptionService encryptionService)
        {
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _userRoleRepository = userRoleRepository;
            _encryptionService = encryptionService;
        }

        #region IMembershipService Implementation

        public MembershipContext ValidateUser(string username, string password)
        {
            var membershipCtx = new MembershipContext();

            var user = _userRepository.GetSingleByUsername(username);
            if (user != null && isUserValid(user, password))
            {
                var userRoles = GetUserRoles(user.Username);
                membershipCtx.User = user;

                var identity = new GenericIdentity(user.Username);
                membershipCtx.Principal = new GenericPrincipal(
                    identity,
                    userRoles.Select(x => x.Name).ToArray());
            }

            return membershipCtx;
        }
        public User CreateUser(string username, string email, string password, int[] roles)
        {
            var existingUser = _userRepository.GetSingleByUsername(username);

            if (existingUser != null)
            {
                throw new Exception("Username is already in use");
            }

            var passwordSalt = _encryptionService.CreateSalt();

            var user = new User()
            {
                Username = username,
                Salt = passwordSalt,
                Email = email,
                IsLocked = false,
                HashedPassword = _encryptionService.EncryptPassword(password, passwordSalt),
                DateCreated = DateTime.Now
            };

            _userRepository.Add(user);

            _userRepository.Commit();

            if (roles != null || roles.Length > 0)
            {
                foreach (var role in roles)
                {
                    addUserToRole(user, role);
                }
            }

            _userRepository.Commit();

            return user;
        }

        public User GetUser(int userId)
        {
            return _userRepository.GetSingle(userId);
        }

        public List<Role> GetUserRoles(string username)
        {
            List<Role> _result = new List<Role>();

            var existingUser = _userRepository.GetSingleByUsername(username);

            if (existingUser != null)
            {
                foreach (var userRole in existingUser.UserRoles)
                {
                    _result.Add(userRole.Role);
                }
            }

            return _result.Distinct().ToList();
        }
        #endregion

        #region Helper methods
        private void addUserToRole(User user, int roleId)
        {
            var role = _roleRepository.GetSingle(roleId);
            if (role == null)
                throw new Exception("Role doesn't exist.");

            var userRole = new UserRole()
            {
                RoleId = role.Id,
                UserId = user.Id
            };
            _userRoleRepository.Add(userRole);

            _userRepository.Commit();
        }

        private bool isPasswordValid(User user, string password)
        {
            return string.Equals(_encryptionService.EncryptPassword(password, user.Salt), user.HashedPassword);
        }

        private bool isUserValid(User user, string password)
        {
            if (isPasswordValid(user, password))
            {
                return !user.IsLocked;
            }

            return false;
        }
        #endregion
    }

Data repositories will automatically be injected into MembershipService instances. This will be configured in the Startup services later on. And of course we need the MembershipContext which holds the IPrincipal information for the current user. Add the class inside a new folder named Core under Infrastructure.

public class MembershipContext
    {
        public IPrincipal Principal { get; set; }
        public User User { get; set; }
        public bool IsValid()
        {
            return Principal != null;
        }
    }

One last thing remained to do before configuring the services is to create a Database Initializer class to run the first time you run the application. The initializer will store some photos, create an admin role and a default user (username: chsakell, password: photogallery). Of course when we finish the app you can register your own users as well. Add the DbInitializer under the Infrastructure folder.

public static class DbInitializer
    {
        private static PhotoGalleryContext context;
        public static void Initialize(IServiceProvider serviceProvider, string imagesPath)
        {
            context = (PhotoGalleryContext)serviceProvider.GetService<PhotoGalleryContext>();

            InitializePhotoAlbums(imagesPath);
            InitializeUserRoles();

        }

        private static void InitializePhotoAlbums(string imagesPath)
        {
            if (!context.Albums.Any())
            {
                List<Album> _albums = new List<Album>();

                var _album1 = context.Albums.Add(
                    new Album
                    {
                        DateCreated = DateTime.Now,
                        Title = "Album 1",
                        Description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
                    }).Entity;
                var _album2 = context.Albums.Add(
                    new Album
                    {
                        DateCreated = DateTime.Now,
                        Title = "Album 2",
                        Description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
                    }).Entity;
                var _album3 = context.Albums.Add(
                    new Album
                    {
                        DateCreated = DateTime.Now,
                        Title = "Album 3",
                        Description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
                    }).Entity;
                var _album4 = context.Albums.Add(
                    new Album
                    {
                        DateCreated = DateTime.Now,
                        Title = "Album 4",
                        Description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
                    }).Entity;

                _albums.Add(_album1); _albums.Add(_album2); _albums.Add(_album3); _albums.Add(_album4);

                string[] _images = Directory.GetFiles(Path.Combine(imagesPath, "wwwroot\\images"));
                Random rnd = new Random();

                foreach (string _image in _images)
                {
                    int _selectedAlbum = rnd.Next(1, 4);
                    string _fileName = Path.GetFileName(_image);

                    context.Photos.Add(
                        new Photo()
                        {
                            Title = _fileName,
                            DateUploaded = DateTime.Now,
                            Uri = _fileName,
                            Album = _albums.ElementAt(_selectedAlbum)
                        }
                        );
                }

                context.SaveChanges();
            }
        }

        private static void InitializeUserRoles()
        {
            if (!context.Roles.Any())
            {
                // create roles
                context.Roles.AddRange(new Role[]
                {
                new Role()
                {
                    Name="Admin"
                }
                });

                context.SaveChanges();
            }

            if (!context.Users.Any())
            {
                context.Users.Add(new User()
                {
                    Email = "chsakells.blog@gmail.com",
                    Username = "chsakell",
                    HashedPassword = "9wsmLgYM5Gu4zA/BSpxK2GIBEWzqMPKs8wl2WDBzH/4=",
                    Salt = "GTtKxJA6xJuj3ifJtTXn9Q==",
                    IsLocked = false,
                    DateCreated = DateTime.Now
                });

                // create user-admin for chsakell
                context.UserRoles.AddRange(new UserRole[] {
                new UserRole() {
                    RoleId = 1, // admin
                    UserId = 1  // chsakell
                }
            });
                context.SaveChanges();
            }
        }
    }

Make sure you add a using statement for the following namespace.

using Microsoft.Framework.DependencyInjection;

The photos will be initialized from a folder wwwroot/images where I have already stored some images. This means that you need to add an images folder under wwwroot and add some images. You can find the images I placed here. I recommend you to copy-paste at least the thumbnail-default.png and the aspnet5-agnular2-03.png images cause the are used directly from the app.
aspnet5-angular2-22
Now let’s switch and change the Startup class as follow:

public class Startup
    {
        private static string _applicationPath = string.Empty;
        public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
        {
            _applicationPath = appEnv.ApplicationBasePath;
            // Setup configuration sources.

            var builder = new ConfigurationBuilder()
                .SetBasePath(appEnv.ApplicationBasePath)
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

            if (env.IsDevelopment())
            {
                // This reads the configuration keys from the secret store.
                // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
                builder.AddUserSecrets();
            }
            builder.AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; set; }
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            // Add Entity Framework services to the services container.
            services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<PhotoGalleryContext>(options =>
                    options.UseSqlServer(Configuration["Data:PhotoGalleryConnection:ConnectionString"]));

            // Repositories
            services.AddScoped<IPhotoRepository, PhotoRepository>();
            services.AddScoped<IAlbumRepository, AlbumRepository>();
            services.AddScoped<IUserRepository, UserRepository>();
            services.AddScoped<IUserRoleRepository, UserRoleRepository>();
            services.AddScoped<IRoleRepository, RoleRepository>();
            services.AddScoped<ILoggingRepository, LoggingRepository>();

            // Services
            services.AddScoped<IMembershipService, MembershipService>();
            services.AddScoped<IEncryptionService, EncryptionService>();

            services.AddAuthentication();

            // Polices
            services.AddAuthorization(options =>
            {
                // inline policies
                options.AddPolicy("AdminOnly", policy =>
                {
                    policy.RequireClaim(ClaimTypes.Role, "Admin");
                });

            });

            // Add MVC services to the services container.
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app)
        {
            // Add the platform handler to the request pipeline.
            app.UseIISPlatformHandler();

            // Add static files to the request pipeline.
            app.UseStaticFiles();

            //AutoMapperConfiguration.Configure();

            app.UseCookieAuthentication(options =>
            {
                options.AutomaticAuthenticate = true;
                options.AutomaticChallenge = true;
            });

            // Custom authentication middleware
            //app.UseMiddleware<AuthMiddleware>();
            // Add MVC to the request pipeline.
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                // Uncomment the following line to add a route for porting Web API 2 controllers.
                //routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
            });

            DbInitializer.Initialize(app.ApplicationServices, _applicationPath);
        }

        // Entry point for the application.
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }

Lots of stuff here so let’s explain from the top to bottom. The constructor simply sets the base path for discover files for file-based providers. Then sets an appsettings.json file to this configuration which means that we can read this file through a instance of IConfigurationRoot. The question is what do we want the appsettings.json for? Well.. we want it to store the connection string over there (Say good riddance to Web.config..). Add an appsettings.json JSON file under the root of PhotoGallery application and paste the following code.

{
  "Data": {
    "PhotoGalleryConnection": {
      "ConnectionString": "Server=(localdb)\\v11.0;Database=PhotoGallery;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  }
}

Alter the connection string to reflect your database environment. Moving on, the ConfigureServices function adds the services we want to the container. Firstly, we add Entity Framework services by configuring the connection string for the PhotoGalleryContext DbContext. Notice how we read the appsettings.json file to capture the connection string value. Then we registered all the data repositories and services we created before to be available through dependency injection. That’s a new feature in ASP.NET 5 and it’s highly welcomed. We added Authentication and Authorization services as well by registering a new Policy. The Policy is a new authorization feature in ASP.NET 5 where you can declare requirements that must be met for authorizing a request. In our case we created a new policy named AdminOnly by declaring that this policy requires that the user making the request must be assigned to role Admin. If you think about it this is quite convenient. Before ASP.NET 5, if you wanted to authorized certain roles you would have to write something like this.

[Authorize(Roles="Admin, OtherRole, AnotherRole")]
public class AdminController : Controller
{
   // . . .
}

Now you can create a Policy where you can declare that if one of those roles are assigned then the request is marked as authorized. Last but not least, we added the MVC services to the container. The Configure method is much simpler, we declared that we can serve static files, we added Cookie based authentication to the pipeline and of course we defined a default MVC route. At the end, we called the database initializer we wrote to bootstrap some data when the application fires for the first time. I have left an AutoMapperConfiguration.Configure() call commented out but we ‘ll un-commented it when the time comes. At this point we can run some Entity Framework commands and initialize the database. In order to enable migrations open a terminal at the root of the application where the project.json leaves and run the command:

dnx ef migrations add initial

This command will enable migrations and create the first one as well. The ef command isn’t any other than the one defined on the project.json.

"commands": {
        "web": "Microsoft.AspNet.Server.Kestrel",
        "ef": "EntityFramework.Commands"
    },

aspnet5-angular2-23
In order to update and sync the database with the model run the following command:

dnx ef database update

aspnet5-angular2-24

Single Page Application

This section is where all the fun happen, where ASP.NET MVC 6, Angular 2 and Typescript will fit together. Since this is an MVC 6 application it makes sense to start with HomeController MVC controller class, so go ahead and add it under a Controllers folder at the root of PhotoGallery.

public class HomeController : Controller
    {
        // GET: /<controller>/
        public IActionResult Index()
        {
            return View();
        }
    }

We have to manually create the Index.cshtml view but first let’s create a common layout page. Add a Views folder at the root and add a new item of type MVC View Start Page in it. Leave the default name _ViewStart.cshtml.

@{
    Layout = "_Layout";
}

Add a folder named Shared inside the Views folder and create a new item of type MVC View Layout Page named _Layout.cshtml. This is an important item in our application cause this page can act as the entry point for multiple SPAs in our application, in case we decided to scale it.

<!DOCTYPE html>

<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>@ViewBag.Title</title>
    @*<base href="/">*@

    <link href="~/lib/css/bootstrap.css" rel="stylesheet" />
    <link href="~/lib/css/font-awesome.css" rel="stylesheet" />
    @RenderSection("styles", required: false)

    <script src="~/lib/js/angular2-polyfills.js"></script>
    <script src="~/lib/js/system.src.js"></script>
    <script src="~/lib/js/Rx.js"></script>
    <script src="~/lib/js/angular2.dev.js"></script>

    <script src="~/lib/js/jquery.js"></script>
    <script src="~/lib/js/bootstrap.js"></script>

    <script>
        System.config({
            map: {
                'rxjs': 'lib/js/rxjs',
                'angular2/angular2': '~/lib/js/angular2.dev.js',
                'angular2/http': '~/lib/js/http.dev.js',
                'angular2/router': '~/lib/js/router.dev.js'
            },
            packages: {
                'app': { defaultExtension: 'js' },
                'rxjs': { defaultExtension: 'js' }
            },
            defaultJSExtensions: true

        });
    </script>
    <script src="~/lib/js/angular2.dev.js"></script>
    <script src="~/lib/js/http.dev.js"></script>
    <script src="~/lib/js/router.dev.js"></script>
</head>
<body>
    <div>
        @RenderBody()
    </div>

    @RenderSection("scripts", required: false)
    <script type="text/javascript">
        @RenderSection("customScript", required: false)
    </script>
</body>
</html>

If you take a look at this page you will notice that this page contains everything that an Angular 2 application needs to get bootstrapped. No top-bar or side-bar components exist here but only Angular 2 related stuff. More over there are some custom sections that an MVC View can use to inject any custom required scripts or stylesheets. One more thing to notice is the src references that starts with ~/lib which actually point to wwwroot/lib. Add a folder named Home inside the Views and create the MVC View Page named Index.cshtml. Alter it as follow.

@{
    ViewBag.Title = "PhotoGallery";
}

@section styles
{
    <link href="~/lib/css/bootstrap.css" rel="stylesheet" />
    <link href="~/lib/css/jquery.fancybox.css" rel="stylesheet" />
    <link href="~/css/site.css" rel="stylesheet" />
    <link href="~/lib/css/alertify.core.css" rel="stylesheet" />
    <link href="~/lib/css/alertify.bootstrap.css" rel="stylesheet" />
}

<div id="nav-separator"></div>

<photogallery-app>
    <div class="loading">Loading</div>
</photogallery-app>

@section scripts
{
    <script src="~/lib/js/jquery.fancybox.pack.js"></script>
    <script src="~/lib/js/alertify.min.js"></script>
}

@section customScript
{
    System.import('./lib/spa/app.js').catch(console.log.bind(console));
    $(document).ready(function() {
    $('.fancybox').fancybox();
    });
}

The highlighted lines are the important ones. Apparently there is going to be an Angular 2 component with a selector of photogallery-app bootstrapped from a file named app.js. This file will be the compiled JavaScript of a Typescript file named app.ts. I have also included a custom css file named site.css which you can find here. Place this file in a new folder named css under wwwroot. You may expect me now to show you the app.ts code but believe it’s better to leave it last. The reason is that it requires files (or components if you prefer) that we haven’t coded yet. Instead we ‘ll view all the components one by one explaining its usage in our spa. First of all I want to show you the final result or the architecture of PhotoGallery spa in the Angular 2 level. This will make easier for you to understand the purpose of each component.
aspnet5-angular2-25
The design is quite simple. The components folder hosts angular 2 components with a specific functionality and html template as well. Notice that the .html templates have an arrow which is new feature in Visual Studio. It means that this .html file has a related Typescript file with the same name under it. When we want to render the Home view, then the home.html template will be rendered and the code behind file the template will be a Home Angular 2 component. I have placed all components related to membership under an account folder. The core folder contains reusable components and classes to be shared across the spa. Let’s start with the simplest one the domain classes. Add a folder named app under wwwroot and a sub-folder named core. Then create the domain folder under core. Add a new Typescript file name photo.ts to represent photo items.

export class Photo {
    Id: number;
    Title: string;
    Uri: string;
    AlbumId: number;
    AlbumTitle: string;
    DateUploaded: Date

    constructor(id: number,
        title: string,
        uri: string,
        albumId: number,
        albumTitle: string,
        dateUploaded: Date) {
        this.Id = id;
        this.Title = title;
        this.Uri = uri;
        this.AlbumId = albumId;
        this.AlbumTitle = albumTitle;
        this.DateUploaded = dateUploaded;
    }
}

Since this is the first Typescript file we added in our spa, you can test the Gulp task compile-typescript and ensure that everything works fine with Typescript compilation.
aspnet5-angular2-26
You could run the task from command line as well.

gulp compile-typescript

This photo Typescript class will be used to map photo items come from the server and more particularly, ViewModel photo items. For each Typescript domain class there will be a corresponding ViewModel class on the server-side. Create a folder named ViewModels under the root of PhotoGallery application and add the first ViewModel PhotoViewModel.

public class PhotoViewModel
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Uri { get; set; }
        public int AlbumId { get; set; }
        public string AlbumTitle { get; set; }
        public DateTime DateUploaded { get; set; }
    }

Similarly, add the album.ts, registration.ts and user.ts Typescript files inside the domain and the corresponding C# ViewModels inside the ViewModels folders.

export class Album {
    Id: number;
    Title: string;
    Description: string;
    Thumbnail: string;
    DateCreated: Date;
    TotalPhotos: number;

    constructor(id: number,
        title: string,
        description: string,
        thumbnail: string,
        dateCreated: Date,
        totalPhotos: number) {
        this.Id = id;
        this.Title = title;
        this.Description = description;
        this.Thumbnail = thumbnail;
        this.DateCreated = dateCreated;
        this.TotalPhotos = totalPhotos;
    }
}
export class Registration {
    Username: string;
    Password: string;
    Email: string;

    constructor(username: string,
        password: string,
        email: string) {
        this.Username = username;
        this.Password = password;
        this.Email = email;
    }
}
export class User {
    Username: string;
    Password: string;

    constructor(username: string,
        password: string) {
        this.Username = username;
        this.Password = password;
    }
}
public class AlbumViewModel
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Thumbnail { get; set; }
        public DateTime DateCreated { get; set; }
        public int TotalPhotos { get; set; }
    }
public class RegistrationViewModel
    {
        [Required]
        public string Username { get; set; }
        [Required]
        public string Password { get; set; }
        [Required]
        [EmailAddress]
        public string Email { get; set; }
    }
public class LoginViewModel
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }

Also add the operationResult.ts Typescript file inside the domain.

export class OperationResult {
    Succeeded: boolean;
    Message: string;

    constructor(succeeded: boolean, message: string) {
        this.Succeeded = succeeded;
        this.Message = message;
    }
}

We will add the coresponding ViewModel inside a new folder named Core under Infrastructure cause this isn’t an Entity mapped to our database. Name the class GenericResult.

public class GenericResult
    {
        public bool Succeeded { get; set; }
        public string Message { get; set; }
    }

The next thing we are going implement are some services using the @Injectable() attribute. (I’m gonna call them services during the post but there are actually injectable modules..) We ‘ll start with a service for making Http requests to the server, named dataService. Add a folder named services under app/core and create the following Typescript file.

import { Http, Response, Request } from 'angular2/http';
import { Injectable } from 'angular2/core';

@Injectable()
export class DataService {

    public _pageSize: number;
    public _baseUri: string;

    constructor(public http: Http) {

    }

    set(baseUri: string, pageSize?: number): void {
        this._baseUri = baseUri;
        this._pageSize = pageSize;
    }

    get(page: number) {
        var uri = this._baseUri + page.toString() + '/' + this._pageSize.toString();

        return this.http.get(uri)
            .map(response => (<Response>response));
    }

    post(data?: any, mapJson: boolean = true) {
        if (mapJson)
            return this.http.post(this._baseUri, data)
                .map(response => <any>(<Response>response).json());
        else
            return this.http.post(this._baseUri, data);
    }

    delete(id: number) {
        return this.http.delete(this._baseUri + '/' + id.toString())
            .map(response => <any>(<Response>response).json())
    }

    deleteResource(resource: string) {
        return this.http.delete(resource)
            .map(response => <any>(<Response>response).json())
    }
}

We imported the required modules from ‘angular2/http’ and ‘angular2/core’ and we decorated our DataService class with the @Injectable attribute. With that we will be able to inject instances of DataService in the constructor of our components in the same way we injected here http. This is how Angular 2 works, we import whichever module we want to use. I have written here some get and CRUD operations but you can add any others if you wish. Let’s implement a membershipService which allow us to sign in and log off from our application. Add the membershipService.ts file in the services folder as well.

import { Http, Response, Request } from 'angular2/http';
import { Injectable } from 'angular2/core';
import { DataService } from './dataService';
import { Registration } from '../domain/registration';
import { User } from '../domain/user';

@Injectable()
export class MembershipService {

    private _accountRegisterAPI: string = 'api/account/register/';
    private _accountLoginAPI: string = 'api/account/authenticate/';
    private _accountLogoutAPI: string = 'api/account/logout/';

    constructor(public accountService: DataService) { }

    register(newUser: Registration) {

        this.accountService.set(this._accountRegisterAPI);
        
        return this.accountService.post(JSON.stringify(newUser));
    }

    login(creds: User) {
        this.accountService.set(this._accountLoginAPI);
        return this.accountService.post(JSON.stringify(creds));
    }

    logout() {
        this.accountService.set(this._accountLogoutAPI);
        return this.accountService.post(null, false);
    }

    isUserAuthenticated(): boolean {
        var _user: User = localStorage.getItem('user');
        if (_user != null)
            return true;
        else
            return false;
    }

    getLoggedInUser(): User {
        var _user: User;

        if (this.isUserAuthenticated()) {
            var _userData = JSON.parse(localStorage.getItem('user'));
            _user = new User(_userData.Username, _userData.Password);
        }

        return _user;
    }
}

Quite interesting service right? Notice how we imported our custom domain classes and the dataService as well. We also marked this service with the @Injectable() attribute as well. This service is going to be injected in to the Login and Register components later on. The login and register operations of the service, simply sets the api URI and makes a POST request to the server. We also created two functions to check if the user is authenticated and if so get user’s properties. We will see them in action later. There are two more services to implement but since we finished the membershipService why don’t we implement the corresponding server-side controller? Add the following AccountController Web API Controller class inside the Controllers folder.

[Route("api/[controller]")]
    public class AccountController : Controller
    {
        private readonly IMembershipService _membershipService;
        private readonly IUserRepository _userRepository;
        private readonly ILoggingRepository _loggingRepository;

        public AccountController(IMembershipService membershipService,
            IUserRepository userRepository,
            ILoggingRepository _errorRepository)
        {
            _membershipService = membershipService;
            _userRepository = userRepository;
            _loggingRepository = _errorRepository;
        }


        [HttpPost("authenticate")]
        public async Task<IActionResult> Login([FromBody] LoginViewModel user)
        {
            IActionResult _result = new ObjectResult(false);
            GenericResult _authenticationResult = null;

            try
            {
                MembershipContext _userContext = _membershipService.ValidateUser(user.Username, user.Password);

                if (_userContext.User != null)
                {
                    IEnumerable<Role> _roles = _userRepository.GetUserRoles(user.Username);
                    List<Claim> _claims = new List<Claim>();
                    foreach (Role role in _roles)
                    {
                        Claim _claim = new Claim(ClaimTypes.Role, "Admin", ClaimValueTypes.String, user.Username);
                        _claims.Add(_claim);
                    }
                    await HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
                        new ClaimsPrincipal(new ClaimsIdentity(_claims, CookieAuthenticationDefaults.AuthenticationScheme)));


                    _authenticationResult = new GenericResult()
                    {
                        Succeeded = true,
                        Message = "Authentication succeeded"
                    };
                }
                else
                {
                    _authenticationResult = new GenericResult()
                    {
                        Succeeded = false,
                        Message = "Authentication failed"
                    };
                }
            }
            catch (Exception ex)
            {
                _authenticationResult = new GenericResult()
                {
                    Succeeded = false,
                    Message = ex.Message
                };

                _loggingRepository.Add(new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now });
                _loggingRepository.Commit();
            }

            _result = new ObjectResult(_authenticationResult);
            return _result;
        }

        [HttpPost("logout")]
        public async Task<IActionResult> Logout()
        {
            try
            {
                await HttpContext.Authentication.SignOutAsync("Cookies");
                return Ok();
            }
            catch (Exception ex)
            {
                _loggingRepository.Add(new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now });
                _loggingRepository.Commit();

                return HttpBadRequest();
            }

        }

        [Route("register")]
        [HttpPost]
        public IActionResult Register([FromBody] RegistrationViewModel user)
        {
            IActionResult _result = new ObjectResult(false);
            GenericResult _registrationResult = null;

            try
            {
                if (ModelState.IsValid)
                {
                    User _user = _membershipService.CreateUser(user.Username, user.Email, user.Password, new int[] { 1 });

                    if (_user != null)
                    {
                        _registrationResult = new GenericResult()
                        {
                            Succeeded = true,
                            Message = "Registration succeeded"
                        };
                    }
                }
                else
                {
                    _registrationResult = new GenericResult()
                    {
                        Succeeded = false,
                        Message = "Invalid fields."
                    };
                }
            }
            catch (Exception ex)
            {
                _registrationResult = new GenericResult()
                {
                    Succeeded = false,
                    Message = ex.Message
                };

                _loggingRepository.Add(new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now });
                _loggingRepository.Commit();
            }

            _result = new ObjectResult(_registrationResult);
            return _result;
        }
    }

Here we can see the magic of Dependency Injection in ASP.NET 5. Required repositories and services are automatically injected into the constructor. The Login, Logout and Register actions are self-explanatory. Let’s finish with the Angular services by adding the following two helpers into the app/core/services folder.

import { Injectable } from 'angular2/core';
import {Router} from 'angular2/router'

@Injectable()
export class UtilityService {

    private _router: Router;

    constructor(router: Router) {
        this._router = router;
    }

    convertDateTime(date: Date) {
        var _formattedDate = new Date(date.toString());
        return _formattedDate.toDateString();
    }

    navigate(path: string) {
        this._router.navigate([path]);
    }

    navigateToSignIn() {
        this.navigate('/Account/Login');
    }
}

This service will be used for common functions such as converting Date objects or changing route in our spa.

import { Injectable } from 'angular2/core';

@Injectable()
export class NotificationService {
    private _notifier: any = alertify;
	
    constructor() {
    }

    printSuccessMessage(message: string) {
        this._notifier.success(message);
    }

    printErrorMessage(message: string) {
        this._notifier.error(message);
    }

    printConfirmationDialog(message: string, okCallback: () => any) {
        this._notifier.confirm(message, function (e) {
            if (e) {
                okCallback();
            } else {
            }
        });
    }
}

This is a very interesting service as well and I created it for a reason. I wanted to support notifications in our SPA so that user receives success or error notifications. The thing is that I couldn’t find any external library available to use it with Typescript so I thought.. why not use a JavaScript one? After all, Typescript or not at the end only JavaScript survives. I ended up with the alertify.js which we have already included in our app using bower. I studied a little the library and I figured out that there is an alertify object that has all the required notification functionality. And this is what we did in our Angular service. We made the trick to store this variable as any so that we can call its methods without any intellisense errors.

private _notifier: any = alertify;

On the other hand the alertify variable is unknown to the NotificationService class and we get an intellisense error. More over if you try to compile the Typescript using the compile-typescript task you will also get a relative error Cannot find name ‘alertify’.
aspnet5-angular2-27
The question is.. can we live with that? YES we can.. but we have to make sure that we will allow Typescript to be compiled and emit outputs despite the type checking errors occurred. How did we achieve it? We have already declared it in the tsconfig.json file as follow:

"compilerOptions": {
        "noImplicitAny": false,
        "module": "system",
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "noEmitOnError": false,
        "removeComments": false,
        "sourceMap": true,
        "target": "es5"
    }

Of course, when the page loads, no JavaScript errors appear on the client.

Angular 2 Components

The next thing we are going to implement are the basic Components of our application. Each component encapsulates a specific logic and is responsible to render the appropriate template as well. Let’s number them along with their specifications (click them to view the template they render).

  1. AppRoot: The SPA’s root components. It bootstraps the application with the required modules and sets the starter route configuration.
  2. Home: The default component rendering when the application starts.
  3. Photos: The component responsible to display PhotoGallery photos. It supports pagination and is available to unauthenticated users.
  4. Albums: The component responsible to display PhotoGallery albums. It requires authorization and supports pagination as well.
  5. AlbumPhotos: Displays a specific album’s photos. User can remove photos from this album. Popup notifications are supported.
  6. Account: The root component related to membership. It has its own route configuration for navigating to Login or Register components.
  7. Login: Displays a login template and allows users to authenticate themselves. After authentication redirect to home page.
  8. Register: It holds the registration logic and template.

Add a folder named components under wwwroot/app and create the first component Home in a home.ts Typescript file.

import {Component} from 'angular2/core';

@Component({
    selector: 'home',
    templateUrl: './app/components/home.html',
    directives: []
})
export class Home {

    constructor() {

    }

This is by far the simplest Angular component you will ever seen. We used the @Component annotation and we declared that when this component is active, a template named home.html existing inside app/components will be rendered. Add the home.html template underapp/components. This is simple html with no Angular related code and that’s why I won’t paste it here. You can copy-paste its code though from here. The next component will spice things up. It’s the Photos, the one responsible to display all photos existing in PhotoGallery app with pagination support as well. It will be the first time that we ‘ll use the dataService injectable to make some HTTP GET requests to the server. Add the photos.ts file under app/components folder.

import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {Router, RouterLink} from 'angular2/router'
import { Photo } from '../core/domain/photo';
import { Paginated } from '../core/common/paginated';
import { DataService } from '../core/services/dataService';

@Component({
    selector: 'photos',
    providers: [DataService],
    templateUrl: './app/components/Photos.html',
    bindings: [DataService],
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, RouterLink]
})
export class Photos extends Paginated {
    private _photosAPI: string = 'api/photos/';
    private _photos: Array<Photo>;

    constructor(public photosService: DataService) {
        super(0, 0, 0);

        photosService.set(this._photosAPI, 12);
        this.getPhotos();
    }

    getPhotos(): void {
        this.photosService.get(this._page)
            .subscribe(res => {

                var data: any = res.json();

                this._photos = data.Items;
                this._page = data.Page;
                this._pagesCount = data.TotalPages;
                this._totalCount = data.TotalCount;
            },
            error => console.error('Error: ' + error));
    }

    search(i): void {
        super.search(i);
        this.getPhotos();
    };
}

Notice here that we imported more angular modules to use on this component. Modules such as CORE_DIRECTIVES, FORM_DIRECTIVES are needed in order to use angular build-in directives such as *ngFor on the template. We also imported routing related modules so that we can create a routerLink on the template that will navigate us back to the default route, Home. We need to declare those modules in the directives array of the component. The getPhotos() function invokes the dataService.get method which returns an Objervable<any>. Then we used the .subscribe function which registers handlers for handling emitted values, error and completions from that observable and executes the observable’s subscriber function. We used it to bound the result come from the server to the Array of photos items to be displayed. We also set some properties such as this._page or this._pageCount which are being used for pagination purposes. At the moment you cannot find those properties cause they are inherited from the Paginated class. I have created this re-usable class in order to be used from all components need to support pagination. It doesn’t matter if the component needs to iterate photos, albums or tomatos.. this re-usable component holds the core functionality to support pagination for any type of items. Add the paginated.ts Typescript file inside a new folder named common under app/core.

export class Paginated {
    public _page: number = 0;
    public _pagesCount: number = 0;
    public _totalCount: number = 0;

    constructor(page: number, pagesCount: number, totalCount: number) {
        this._page = page;
        this._pagesCount = pagesCount;
        this._totalCount = totalCount;
    }

    range(): Array<any> {
        if (!this._pagesCount) { return []; }
        var step = 2;
        var doubleStep = step * 2;
        var start = Math.max(0, this._page - step);
        var end = start + 1 + doubleStep;
        if (end > this._pagesCount) { end = this._pagesCount; }

        var ret = [];
        for (var i = start; i != end; ++i) {
            ret.push(i);
        }

        return ret;
    };

    pagePlus(count: number): number {
        return + this._page + count;
    }

    search(i): void {
        this._page = i;
    };
}

The properties of t this class are used to render a paginated list (we ‘ll view it soon in action). When the user click a number or an first/previous/next/last arrow, the component that inherits this class, the only thing needs to do is to set the current page and make the request. All the other stuff happens automatically. Let’s view the app/components/photos.html template now.

<div class="container">

    <div class="row">
        <div class="col-lg-12">
            <h1 class="page-header">
                Photo Gallery photos
                <small><i>(all albums displayed)</i></small>
            </h1>
            <ol class="breadcrumb">
                <li>
                    <a [routerLink]="['/Home']">Home</a>
                </li>
                <li class="active">Photos</li>
            </ol>
        </div>
    </div>

    <div class="row">
        <div class="col-lg-3 col-md-4 col-xs-6 picture-box" *ngFor="#image of _photos">
            <a class="fancybox" rel="gallery" href="{{image.Uri}}">
                <img src="{{image.Uri}}" alt="{{image.Title}}" class="img img-responsive full-width thumbnail" />
            </a>
        </div>
    </div>
</div>

<footer class="navbar navbar-fixed-bottom">
    <div class="text-center">
        <div ng-hide="(!_pagesCount || _pagesCount < 2)" style="display:inline">
            <ul class="pagination pagination-sm">
                <li><a *ngIf="page != 0_" (click)="search(0)"><<</a></li>
                <li><a *ngIf="_page != 0" (click)="search(_page-1)"><</a></li>
                <li *ngFor="#n of range()" [ngClass]="{active: n == _page}">
                    <a (click)="search(n)" *ngIf="n != _page">{{n+1}}</a>
                    <span *ngIf="n == _page">{{n+1}}</span>
                </li>
                <li><a *ngIf="_page != (_pagesCount - 1)" (click)="search(pagePlus(1))">></a></li>
                <li><a *ngIf="_page != (_pagesCount - 1)" (click)="search(_pagesCount - 1)">>></a></li>
            </ul>
        </div>
    </div>
</footer>

The [routerLink] will create a link to the Home component. We used the new angular directive to iterate an array of photo items.

 *ngFor="#image of _photos">

The footer element is the one that will render the paginated list depending on the total items fetched from the server and the paginated properties being set. Those properties are going to be set from the server using a new class named PaginatedSet.
Let’s switch and prepare the server-side infrastructure for this component. Add the following class inside the Infrastructure/Core folder.

public class PaginationSet<T>
    {
        public int Page { get; set; }

        public int Count
        {
            get
            {
                return (null != this.Items) ? this.Items.Count() : 0;
            }
        }

        public int TotalPages { get; set; }
        public int TotalCount { get; set; }

        public IEnumerable<T> Items { get; set; }
    }

And now the controller. Add the PhotosController Web API Controller class inside the Controllers folder.

[Route("api/[controller]")]
    public class PhotosController : Controller
    {
        IPhotoRepository _photoRepository;
        ILoggingRepository _loggingRepository;
        public PhotosController(IPhotoRepository photoRepository, ILoggingRepository loggingRepository)
        {
            _photoRepository = photoRepository;
            _loggingRepository = loggingRepository;
        }

        [HttpGet("{page:int=0}/{pageSize=12}")]
        public PaginationSet<PhotoViewModel> Get(int? page, int? pageSize)
        {
            PaginationSet<PhotoViewModel> pagedSet = null;

            try
            {
                int currentPage = page.Value;
                int currentPageSize = pageSize.Value;

                List<Photo> _photos = null;
                int _totalPhotos = new int();


                _photos = _photoRepository
                    .AllIncluding(p => p.Album)
                    .OrderBy(p => p.Id)
                    .Skip(currentPage * currentPageSize)
                    .Take(currentPageSize)
                    .ToList();

                _totalPhotos = _photoRepository.GetAll().Count();

                IEnumerable<PhotoViewModel> _photosVM = Mapper.Map<IEnumerable<Photo>, IEnumerable<PhotoViewModel>>(_photos);

                pagedSet = new PaginationSet<PhotoViewModel>()
                {
                    Page = currentPage,
                    TotalCount = _totalPhotos,
                    TotalPages = (int)Math.Ceiling((decimal)_totalPhotos / currentPageSize),
                    Items = _photosVM
                };
            }
            catch (Exception ex)
            {
                _loggingRepository.Add(new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now });
                _loggingRepository.Commit();
            }

            return pagedSet;
        }
    }

The Get action returns a PaginatedSet containing the PhotoViewModel items to be displayed and the required paginated information to create a client paginated list. The thing is that we haven’t set any Automapper configuration yet and if you recall, I have told you to keep an Automapper related line in the Startup class commented out until the time comes. Well.. why don’t we configure it right now? Add a new folder named Mappings under Infrastructure and create the following class.

public class DomainToViewModelMappingProfile : Profile
    {
        protected override void Configure()
        {
            Mapper.CreateMap<Photo, PhotoViewModel>()
               .ForMember(vm => vm.Uri, map => map.MapFrom(p => "/images/" + p.Uri));

            Mapper.CreateMap<Album, AlbumViewModel>()
                .ForMember(vm => vm.TotalPhotos, map => map.MapFrom(a => a.Photos.Count))
                .ForMember(vm => vm.Thumbnail, map => 
                    map.MapFrom(a => (a.Photos != null && a.Photos.Count > 0) ?
                    "/images/" + a.Photos.First().Uri :
                    "/images/thumbnail-default.png"));
        }
    }

The code creates mappings for both Photo to PhotoViewModel and Album to AblumViewModel objects. For the former we only add an images prefix for the photo URI so that can be mapped to wwwroot/images folder and for the latter we set the TotalPhotos property as well. In the same folder add the AutoMapperConfiguration class as follow.

public class AutoMapperConfiguration
    {
        public static void Configure()
        {
            Mapper.Initialize(x =>
            {
                x.AddProfile<DomainToViewModelMappingProfile>();
            });
        }
    }

Now you can safely uncomment the following line from the Configure(IApplicationBuilder app) function in the Startup class.

AutoMapperConfiguration.Configure();

The albums angular component is almost identical to the photos one with one exception. It uses a routing related variable in order to redirect to the albumphotos component when user clicks on a specific album. Thus before showing the code I believe it is necessary to introduce you to the routing configuration our SPA will follow. Let’s discuss the architecture shown in the following schema.
aspnet5-angular2-28
The idea is that the SPA is getting bootstrapped from an AppRoot component. This component defines a routing configuration through a @RouteConfig angular annotation. There are three types of navigation paths defined on its configuration. The first one is the simplest one where the path points to a single component (leaf) and that component gets activated and render the corresponding html template. The second type is the one where again the path points to a single component (also leaf) but this time it passes an extra parameter as well. Last but not least, the third type is where a path points to component that has its own route configuration which means that this component is expected to define a new routing configuration with the angular @RouteConfig annotation. The @RouteConfig annotation accepts an object with an array of Route objects. Each route object has a path, a component, a name and a path. Let’s create this object which eventually will set the routing configuration for the AppRoot component. Add the routes.ts file under the wwwroot/app folder.

import { Route, Router } from 'angular2/router';
import { Home } from './components/home';
import { Photos } from './components/photos';
import { Albums } from './components/albums';
import { AlbumPhotos } from './components/albumPhotos';
import { Account } from './components/account/account';

export var Routes = {
    home: new Route({ path: '/', name: 'Home', component: Home }),
    photos: new Route({ path: '/photos', name: 'Photos', component: Photos }),
    albums: new Route({ path: '/albums', name: 'Albums', component: Albums }),
    albumPhotos: new Route({ path: '/albums/:id/photos', name: 'AlbumPhotos', component: AlbumPhotos }),
    account: new Route({ path: '/account/...', name: 'Account', component: Account })
};

export const APP_ROUTES = Object.keys(Routes).map(r => Routes[r]);

We set nothing here, we have only defined an object with an array of Routes. The Routes variable reflects exactly the previous picture. Pay attention the way we ‘ll pass an id paramater when we navigate to view a specific album’s photos. Also, we expect that the Account component that gets activated when we navigate to /#/account, has a new routing configuration. At this point we haven’t coded that component yet but that’s OK, we ‘ll do it a little bit later. Let’s continue from where we have stopped, the Albums component. Add the albums.ts Typescript file under app/components.

import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {Router, RouterLink} from 'angular2/router'
import { Album } from '../core/domain/album';
import { Paginated } from '../core/common/paginated';
import { DataService } from '../core/services/dataService';
import { UtilityService } from '../core/services/utilityService';
import { Routes, APP_ROUTES } from '../routes';

@Component({
    selector: 'albums',
    templateUrl: './app/components/albums.html',
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, RouterLink]
})
export class Albums extends Paginated{
    private _albumsAPI: string = 'api/albums/';
    private _albums: Array<Album>;
    private routes = Routes;

    constructor(public albumsService: DataService,
                public utilityService: UtilityService,
                public router: Router) {
        super(0, 0, 0);

        this.routes = Routes;
        albumsService.set(this._albumsAPI, 3);
        this.getAlbums();
    }

    getAlbums(): void {
        this.albumsService.get(this._page)
            .subscribe(res => {               
                var data:any = res.json();
                this._albums = data.Items;
                this._page = data.Page;
                this._pagesCount = data.TotalPages;
                this._totalCount = data.TotalCount;
            },
            error => {

                if (error.status == 401) {
                    this.utilityService.navigateToSignIn();
                }

                console.error('Error: ' + error);
            });
    }

    search(i): void {
        super.search(i);
        this.getAlbums();
    };

    convertDateTime(date: Date) {
        return this.utilityService.convertDateTime(date);
    }
}

As i mentioned this component is identical to the Photos with the exception that has a reference to the APP_ROUTES object we have just coded. I made this just to show you how easily you can use it to navigate to different routes using the RouterLink directive. Another thing to notice is that I have intentionally made the DataService.get() method to return an Observable<Response> rather than the JSON serialized result because I know that my API call my return an unauthorized code. Hence, if an error occurred and the status is 401 we navigate to the login template else we proceed to serialize the result. It’s up to you what you prefer, I just wanted to show you some options that you have. Let’s view the related template albums.html. Add it under the same folder app/components.

<div class="container">
    <div class="row">
        <div class="col-lg-12">
            <h1 class="page-header">
                Photo Gallery albums
                <small>Page {{_page + 1}} of {{_pagesCount}}</small>
            </h1>
            <ol class="breadcrumb">
                <li>
                    <a [routerLink]="['/Home']">Home</a>
                </li>
                <li class="active">Albums</li>
            </ol>
        </div>
    </div>
    <!-- /.row -->
    <div class="row album-box" *ngFor="#album of _albums">
        <div class="col-md-1 text-center">
            <p>
                <i class="fa fa-camera fa-4x"></i>
            </p>
            <p>{{convertDateTime(album.DateCreated)}}</p>
        </div>
        <div class="col-md-5">
            <a class="fancybox" rel="gallery" href="{{album.Thumbnail}}" title="{{album.Title}}">
                <img class="media-object img-responsive album-thumbnail" src="{{album.Thumbnail}}" alt="" />
            </a>
        </div>
        <div class="col-md-6">
            <h3>
                <a [routerLink]="[routes.albumPhotos.name, {id: album.Id}]">{{album.Title}}</a>
            </h3>
            <p>
                Photos: <span class="badge">{{album.TotalPhotos}}</span>
            </p>
            <p>{{album.Description}}</p>
            <a *ngIf="album.TotalPhotos > 0" class="btn btn-primary" [routerLink]="[routes.albumPhotos.name, {id: album.Id}]">View photos <i class="fa fa-angle-right"></i></a>
        </div>
        <hr/>
    </div>
    <hr>
</div>

<footer class="navbar navbar-fixed-bottom">
    <div class="text-center">
        <div ng-hide="(!_pagesCount || _pagesCount < 2)" style="display:inline">
            <ul class="pagination pagination-sm">
                <li><a *ngIf="_page != 0_" (click)="search(0)"><<</a></li>
                <li><a *ngIf="_page != 0" (click)="search(_page-1)"><</a></li>
                <li *ngFor="#n of range()" [ngClass]="{active: n == _page}">
                    <a (click)="search(n)" *ngIf="n != _page">{{n+1}}</a>
                    <span *ngIf="n == _page">{{n+1}}</span>
                </li>
                <li><a *ngIf="_page != (_pagesCount - 1)" (click)="search(pagePlus(1))">></a></li>
                <li><a *ngIf="_page != (_pagesCount - 1)" (click)="search(_pagesCount - 1)">>></a></li>
            </ul>
        </div>
    </div>
</footer>

We assign the value of the [routerLink] directive equal to the name of the route where we want to navigate. We can do it either hard coded..

<a [routerLink]="['/Home']">Home</a>

..or using the routes variable..

<a [routerLink]="[routes.albumPhotos.name, {id: album.Id}]">{{album.Title}}</a>

In the last one we can see how to pass the id parameter required for the albumPhotos route. Let’s implement the Web API Controller AlbumsController now. Add the class inside Controllers folder.

    [Route("api/[controller]")]
    public class AlbumsController : Controller
    {
        private readonly IAuthorizationService _authorizationService;
        IAlbumRepository _albumRepository;
        ILoggingRepository _loggingRepository;
        public AlbumsController(IAuthorizationService authorizationService,
                                IAlbumRepository albumRepository,
                                ILoggingRepository loggingRepository)
        {
            _authorizationService = authorizationService;
            _albumRepository = albumRepository;
            _loggingRepository = loggingRepository;
        }

        [HttpGet("{page:int=0}/{pageSize=12}")]
        public async Task<IActionResult> Get(int? page, int? pageSize)
        {
            PaginationSet<AlbumViewModel> pagedSet = new PaginationSet<AlbumViewModel>();

            try
            {
                if (await _authorizationService.AuthorizeAsync(User, "AdminOnly"))
                {
                    int currentPage = page.Value;
                    int currentPageSize = pageSize.Value;

                    List<Album> _albums = null;
                    int _totalAlbums = new int();


                    _albums = _albumRepository
                        .AllIncluding(a => a.Photos)
                        .OrderBy(a => a.Id)
                        .Skip(currentPage * currentPageSize)
                        .Take(currentPageSize)
                        .ToList();

                    _totalAlbums = _albumRepository.GetAll().Count();

                    IEnumerable<AlbumViewModel> _albumsVM = Mapper.Map<IEnumerable<Album>, IEnumerable<AlbumViewModel>>(_albums);

                    pagedSet = new PaginationSet<AlbumViewModel>()
                    {
                        Page = currentPage,
                        TotalCount = _totalAlbums,
                        TotalPages = (int)Math.Ceiling((decimal)_totalAlbums / currentPageSize),
                        Items = _albumsVM
                    };
                }
                else
                {
                    StatusCodeResult _codeResult = new StatusCodeResult(401);
                    return new ObjectResult(_codeResult);
                }
            }
            catch (Exception ex)
            {
                _loggingRepository.Add(new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now });
                _loggingRepository.Commit();
            }

            return new ObjectResult(pagedSet);
        }


        [HttpGet("{id:int}/photos/{page:int=0}/{pageSize=12}")]
        public PaginationSet<PhotoViewModel> Get(int id, int? page, int? pageSize)
        {
            PaginationSet<PhotoViewModel> pagedSet = null;

            try
            {
                int currentPage = page.Value;
                int currentPageSize = pageSize.Value;

                List<Photo> _photos = null;
                int _totalPhotos = new int();

                Album _album = _albumRepository.GetSingle(a => a.Id == id, a => a.Photos);

                _photos = _album
                            .Photos
                            .OrderBy(p => p.Id)
                            .Skip(currentPage * currentPageSize)
                            .Take(currentPageSize)
                            .ToList();

                _totalPhotos = _album.Photos.Count();

                IEnumerable<PhotoViewModel> _photosVM = Mapper.Map<IEnumerable<Photo>, IEnumerable<PhotoViewModel>>(_photos);

                pagedSet = new PaginationSet<PhotoViewModel>()
                {
                    Page = currentPage,
                    TotalCount = _totalPhotos,
                    TotalPages = (int)Math.Ceiling((decimal)_totalPhotos / currentPageSize),
                    Items = _photosVM
                };
            }
            catch (Exception ex)
            {
                _loggingRepository.Add(new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now });
                _loggingRepository.Commit();
            }

            return pagedSet;
        }
    }

The highlighted lines shows you the way you can check programmatically if the current request is authorized to access a specific part of code. We used an instance of Microsoft.AspNet.Authorization.IAuthorizationService to check if the ClaimsPrincipal user fullfiles the requirements of the AdminOnly policy. If not we return a new object of type StatusCodeResult with a 401 status and a message. You can add that class inside the Infrastructure/Core folder.

public class StatusCodeResult
    {
        private int _status;
        private string _message;
        public int Status {
            get
            {
                return _status;
            }
            private set { }
        }
        public string Message
        {
            get
            {
                return _message;
            }
            private set { }
        }

        public StatusCodeResult(int status)
        {
            if (status == 401)
                _message = "Unauthorized access. Login required";

            _status = status;
        }

        public StatusCodeResult(int code, string message)
        {
            _status = code;
            _message = message;
        }
    }

OK, we prevented access to the api/albums/get API but was this the right way to do it? Of course not because the way we implemented it we do allow the request to dispatch in our controller’s action and later on we forbid the action. What we would like to have though is a cleaner way to prevent unauthorized access before requests reach the action and of course without the use of the Microsoft.AspNet.Authorization.IAuthorizationService. All you have to do, is decorate the action you want to prevent access to with the [Authorize(Policy = “AdminOnly”)] attribute. Do if for both the actions in the AlbumsController. You can now safely remove the code that programmatically checks if the user fullfiles the AdminOnly policy.

        [Authorize(Policy = "AdminOnly")]
        [HttpGet("{page:int=0}/{pageSize=12}")]
        public async Task<IActionResult> Get(int? page, int? pageSize)
        {
            PaginationSet<AlbumViewModel> pagedSet = new PaginationSet<AlbumViewModel>();

            try
            {
			// Code omitted

We will procceed with the albumPhotos angular component which is responsible to display a respective album’s photos using pagination. We will see two new features on this component: The first one is the RouteParams which is an immutable map of parameters for the given route based on the url matcher and optional parameters for that route. We will use it to extract the :id parameter from the route api/albums/:id/photos. The second one is a confirmation message which will ask the user to confirm photo removal. Add the albumsPhotos.ts file inside the app/components folder.

import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {RouterLink, RouteParams} from 'angular2/router'
import { Photo } from '../core/domain/photo';
import { Paginated } from '../core/common/paginated';
import { DataService } from '../core/services/dataService';
import { UtilityService } from '../core/services/utilityService';
import { NotificationService } from '../core/services/notificationService';
import { OperationResult } from '../core/domain/operationResult';

@Component({
    selector: 'album-photo',
    providers: [NotificationService],
    templateUrl: './app/components/albumPhotos.html',
    bindings: [NotificationService],
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, RouterLink]
})
export class AlbumPhotos extends Paginated {
    private _albumsAPI: string = 'api/albums/';
    private _photosAPI: string = 'api/photos/';
    private _albumId: string;
    private _photos: Array<Photo>;
    private _displayingTotal: number;
    private _routeParam: RouteParams;
    private _albumTitle: string;

    constructor(public dataService: DataService,
        public utilityService: UtilityService,
        public notificationService: NotificationService,
        routeParam: RouteParams) {
        super(0, 0, 0);

        this._routeParam = routeParam;
        this._albumId = this._routeParam.get('id');
        this._albumsAPI += this._albumId + '/photos/';
        dataService.set(this._albumsAPI, 12);
        this.getAlbumPhotos();
    }

    getAlbumPhotos(): void {
        this.dataService.get(this._page)
            .subscribe(res => {

                var data: any = res.json();

                this._photos = data.Items;
                this._displayingTotal = this._photos.length;
                this._page = data.Page;
                this._pagesCount = data.TotalPages;
                this._totalCount = data.TotalCount;
                this._albumTitle = this._photos[0].AlbumTitle;
            },
            error => {
                if (error.status == 401) {
                    this.utilityService.navigateToSignIn();
                }

                console.error('Error: ' + error)
            },
            () => console.log(this._photos));
    }

    search(i): void {
        super.search(i);
        this.getAlbumPhotos();
    };

    convertDateTime(date: Date) {
        return this.utilityService.convertDateTime(date);
    }

    delete(photo: Photo) {
        var _removeResult: OperationResult = new OperationResult(false, '');

        this.notificationService.printConfirmationDialog('Are you sure you want to delete the photo?',
            () => {
                this.dataService.deleteResource(this._photosAPI + photo.Id)
                    .subscribe(res => {
                        _removeResult.Succeeded = res.Succeeded;
                        _removeResult.Message = res.Message;
                    },
                    error => console.error('Error: ' + error),
                    () => {
                        if (_removeResult.Succeeded) {
                            this.notificationService.printSuccessMessage(photo.Title + ' removed from gallery.');
                            this.getAlbumPhotos();
                        }
                        else {
                            this.notificationService.printErrorMessage('Failed to remove photo');
                        }
                    });
            });
    }
}

We called the RouteParams.get() method to extract the :id value from the route. When user clicks to delete a photo, a confirmation dialog pops up. This comes from the notificationService.printConfirmationDialog method, that accepts a callback function to be called in case user clicks OK.

printConfirmationDialog(message: string, okCallback: () => any) {
        this._notifier.confirm(message, function (e) {
            if (e) {
                okCallback();
            } else {
            }
        });
    }

If removal succeeded we display a success message and refresh the album photos. It’s time to implement the membership’s related components. As we have allready mentioned, there is a nested routing configuration for those components so let’s start by creating this first. Add a new folder named account under app/components and create the following routes.ts file.

import { Route, Router } from 'angular2/router';
import { Login } from './login';
import { Register } from './register';
import { Home } from '../../components/home';

export var Routes = {
    login: new Route({ path: '/', name: 'Login', component: Login }),
    register: new Route({ path: '/register', name: 'Register', component: Register }),
    home: new Route({ path: '/home', name: 'Home', component: Home })
};

export const APP_ROUTES = Object.keys(Routes).map(r => Routes[r]);

This file is almost identical with the one we created under app with the difference that difines different routes. The @RouteConfig configuration related to those routes will be defined in the Account component. Let’s recall part of the app/routes.ts file.

export var Routes = {
    home: new Route({ path: '/', name: 'Home', component: Home }),
    photos: new Route({ path: '/photos', name: 'Photos', component: Photos }),
    albums: new Route({ path: '/albums', name: 'Albums', component: Albums }),
    albumPhotos: new Route({ path: '/albums/:id/photos', name: 'AlbumPhotos', component: AlbumPhotos }),
    account: new Route({ path: '/account/...', name: 'Account', component: Account })
};

Add the Login component in a login.ts Typescript file under app/components/account.

import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {Router, RouterLink} from 'angular2/router';
import { Routes, APP_ROUTES } from './routes';
import { User } from '../../core/domain/user';
import { OperationResult } from '../../core/domain/operationResult';
import { MembershipService } from '../../core/services/membershipService';
import { NotificationService } from '../../core/services/notificationService';

@Component({
    selector: 'albums',
    providers: [MembershipService, NotificationService],
    templateUrl: './app/components/account/login.html',
    bindings: [MembershipService, NotificationService],
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, RouterLink]
})
export class Login {
    private routes = Routes;
    private _router: Router;
    private _user: User;

    constructor(public membershipService: MembershipService,
                public notificationService: NotificationService,
                router: Router) {
        this._user = new User('', '');
        this.routes = Routes;
        this._router = router;
    }

    login(): void {
        var _authenticationResult: OperationResult = new OperationResult(false, '');

        this.membershipService.login(this._user)
            .subscribe(res => {
                _authenticationResult.Succeeded = res.Succeeded;
                _authenticationResult.Message = res.Message;
            },
            error => console.error('Error: ' + error),
            () => {
                if (_authenticationResult.Succeeded) {
                    this.notificationService.printSuccessMessage('Welcome back ' + this._user.Username + '!');
                    localStorage.setItem('user', JSON.stringify(this._user));
                    this._router.navigate([this.routes.home.name]);
                }
                else {
                    this.notificationService.printErrorMessage(_authenticationResult.Message);
                }
            });
    };
}

We inject the memberhipService in the constructor and when the user clicks to sign in we call the membershipService.login method passing as a parameter user’s username and password. We subscribe the result of the login operation in a OperationResult object and if the authentication succeeded we store user’s credentials in a ‘user’ key using the localStorage.setItem function. Add the related login template in a login.html file under app/account/components.

<div id="loginModal" class="modal show" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                <h1 class="text-center">
                    <span class="fa-stack fa-1x">
                        <i class="fa fa-circle fa-stack-2x text-primary"></i>
                        <i class="fa fa-user fa-stack-1x fa-inverse"></i>
                    </span>Login
                </h1>
            </div>
            <div class="modal-body">
                <form class="form col-md-12 center-block" #hf="ngForm">
                    <div class="form-group">
                        <input type="text" class="form-control input-lg" placeholder="Username" [(ngModel)]="_user.Username"
                               name="username" ngControl="username" required #username="ngForm" />
                        <div [hidden]="username.valid || username.untouched" class="alert alert-danger">
                            Username is required
                        </div>
                    </div>
                    <div class="form-group">
                        <input type="password" class="form-control input-lg" placeholder="Password" [(ngModel)]="_user.Password"
                               name="password" ngControl="password" required #password="ngForm"/>
                        <div [hidden]="password.valid || password.untouched" class="alert alert-danger">
                            Password is required
                        </div>
                    </div>
                    <div class="form-group">
                        <button class="btn btn-primary btn-lg btn-block" (click)="login()" [disabled]="!hf.form.valid">Sign In</button>
                        <span class="pull-right">
                            <a [routerLink]="[routes.register.name]">Register</a>
                        </span>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <div class="col-md-12">
                    <a class="btn btn-danger pull-left" [routerLink]="[routes.home.name]" data-dismiss="modal" aria-hidden="true">Cancel</a>
                </div>
            </div>
        </div>
    </div>
</div>

The highlighted lines shows some interesting features in Angular 2. We used two-way databinding using the NgModel directive and the NgControl directive that tracks change state and validity of form controls and updates the control with special Angular CSS classes as well. We used the # symbol to create local template variables for the form and the two input controls and we used them to validate those controls. For example we defined a local variable username..

#username

.. and we used it to show or hide a validity error as follow..

<div [hidden]="username.valid || username.untouched" class="alert alert-danger">
     Username is required
</div>

We also disable the Sign in button till the form is valid.

<button class="btn btn-primary btn-lg btn-block" (click)="login()" [disabled]="!hf.form.valid">Sign In</button>

aspnet5-angular2-31
The Register component and its template follow the same logic as the login’s one so just create the register.ts and register.html files under the app/components/account folder.

import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {ROUTER_PROVIDERS, ROUTER_DIRECTIVES, Router} from 'angular2/router'
import { Routes, APP_ROUTES } from './routes';
import { Registration } from '../../core/domain/registration'
import { OperationResult } from '../../core/domain/operationResult'
import { MembershipService } from '../../core/services/membershipService';
import { NotificationService } from '../../core/services/notificationService';

@Component({
    selector: 'register',
    providers: [MembershipService, NotificationService],
    templateUrl: './app/components/account/register.html',
    bindings: [MembershipService, NotificationService],
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, ROUTER_DIRECTIVES]
})
export class Register {

    private routes = Routes;
    private _router: Router;
    private _newUser: Registration;

    constructor(public membershipService: MembershipService,
                public notificationService: NotificationService,
                router: Router) {
        this._newUser = new Registration('', '', '');
        this._router = router;
        this.routes = Routes;
    }

    register(): void {
        var _registrationResult: OperationResult = new OperationResult(false, '');
        this.membershipService.register(this._newUser)
            .subscribe(res => {
                _registrationResult.Succeeded = res.Succeeded;
                _registrationResult.Message = res.Message;

            },
            error => console.error('Error: ' + error),
            () => {
                if (_registrationResult.Succeeded) {
                    this.notificationService.printSuccessMessage('Dear ' + this._newUser.Username + ', please login with your credentials');
                    this._router.navigate([this.routes.login.name]);
                }
                else {
                    this.notificationService.printErrorMessage(_registrationResult.Message);
                }
            });
    };
}
<div id="registerModal" class="modal show" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                <h1 class="text-center">
                    <span class="fa-stack fa-1x">
                        <i class="fa fa-circle fa-stack-2x text-primary"></i>
                        <i class="fa fa-user-plus fa-stack-1x fa-inverse"></i>
                    </span>Register
                </h1>
            </div>
            <div class="modal-body">
                <form class="form col-md-12 center-block" #hf="ngForm">
                    <div class="form-group">
                        <input type="text" class="form-control input-lg" placeholder="Username" [(ngModel)]="_newUser.Username" 
                               name="username" ngControl="username" required #username="ngForm">
                        <div [hidden]="username.valid || username.untouched" class="alert alert-danger">
                            Username is required
                        </div>
                    </div>
                    <div class="form-group">
                        <input type="email" class="form-control input-lg" placeholder="Email" [(ngModel)]="_newUser.Email" 
                               name="email" ngControl="email" required #email="ngForm">
                        <div [hidden]="email.valid || email.untouched" class="alert alert-danger">
                            Email is required
                        </div>
                    </div>
                    <div class="form-group">
                        <input type="password" class="form-control input-lg" placeholder="Password" [(ngModel)]="_newUser.Password" 
                               name="password" ngControl="password" required #password="ngForm">
                        <div [hidden]="password.valid || password.untouched" class="alert alert-danger">
                            Password is required
                        </div>
                    </div>
                    <div class="form-group">
                        <button class="btn btn-primary btn-lg btn-block" (click)="register()" [disabled]="!hf.form.valid">Register</button>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <div class="col-md-12">
                    <a class="btn btn-danger pull-left" [routerLink]="['/Account/Login']" data-dismiss="modal" aria-hidden="true">Cancel</a>
                </div>
            </div>
        </div>
    </div>
</div>

Let us create the Account component where all the routing configuration related to membership is getting bootstrapped. Add the account.ts file under app/components/account.

import {Component} from 'angular2/core'
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common'
import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_BINDINGS} from 'angular2/router';
import { Routes, APP_ROUTES } from './routes';

@Component({
    selector: 'account',
    templateUrl: './app/components/account/account.html',
    directives: [ROUTER_DIRECTIVES, CORE_DIRECTIVES, FORM_DIRECTIVES]
})
@RouteConfig(APP_ROUTES)
export class Account {
    constructor() {

    }
}

We used the @RouteConfig decorator to configure the routing when this component gets activated. Let’s recall the routes under account and explain how this works.

export var Routes = {
    login: new Route({ path: '/', name: 'Login', component: Login }),
    register: new Route({ path: '/register', name: 'Register', component: Register }),
    home: new Route({ path: '/home', name: 'Home', component: Home })
};

First of all mind that the browser’s URL will be at least [www.website.com/#/account] so that those routes start working. When the browser URL for this application becomes /register (www.website.com/#/account/register), the router matches that URL to the RouteDefintion named Register and displays the Register in a RouterOutlet that we are going to place in the account.html template. Here’s the account.html template under app/components/account.

<div class="container">
    <router-outlet></router-outlet>
</div>

You can think the router-outlet something like the angularJS 1 ng-view. When the route changes the respective template to be rendered is placed inside this element. Of course you can add any other html elements outside the router-element. Those elements are going to be rendered all the time either you are on the Login or the Register template cause the account.html template is the host’s component template. We declared that the default component when the Account is activated is the Login.

login: new Route({ path: '/', name: 'Login', component: Login })

Finally, it’s high time to gather all those together and write the root component AppRoot which will bootstrap our Single Page Application. Add the app.ts Typescript file under app folder.

import {provide, Component, View} from 'angular2/core';
import {CORE_DIRECTIVES} from 'angular2/common';
import {bootstrap} from 'angular2/platform/browser';
import {HTTP_BINDINGS, HTTP_PROVIDERS, Headers, RequestOptions, BaseRequestOptions} from 'angular2/http';
import {
RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, ROUTER_BINDINGS,
Location, LocationStrategy, HashLocationStrategy
} from 'angular2/router';
import 'rxjs/add/operator/map';
import {enableProdMode} from 'angular2/core';

enableProdMode();
import { Routes, APP_ROUTES } from './routes';

import { DataService } from './core/services/dataService';
import { MembershipService } from './core/services/membershipService';
import { UtilityService } from './core/services/utilityService';
import { User } from './core/domain/user';

@Component({
    selector: 'photogallery-app',
    templateUrl: './app/app.html',
    directives: [ROUTER_DIRECTIVES, CORE_DIRECTIVES]
})
@RouteConfig(APP_ROUTES)
export class AppRoot {
    private routes = Routes;

    constructor(public membershipService: MembershipService, location: Location) {
        this.routes = Routes;
        location.go('/');
    }

    isUserLoggedIn(): boolean {
        return this.membershipService.isUserAuthenticated();
    }

    getUserName(): string {
        if (this.isUserLoggedIn()) {
            var _user = this.membershipService.getLoggedInUser();
            return _user.Username;
        }
        else
            return 'Account';
    }

    logout(): void {
        this.membershipService.logout()
            .subscribe(res => {
                localStorage.removeItem('user');
            },
            error => console.error('Error: ' + error),
            () => { });
    }
}

class AppBaseRequestOptions extends BaseRequestOptions {
    headers: Headers = new Headers({
        'Content-Type': 'application/json'
    })
}

bootstrap(AppRoot, [HTTP_PROVIDERS, ROUTER_PROVIDERS,
    provide(RequestOptions, { useClass: AppBaseRequestOptions }),
    provide(LocationStrategy, { useClass: HashLocationStrategy }),
    DataService, MembershipService, UtilityService])
    .catch(err => console.error(err));

// ROUTER_BINDINGS: DO NOT USE HERE IF YOU WANT TO HAVE HASHLOCATIONSTRATEGY!!

The AppRoot components sets the default route configuration when the SPA starts. Of course those routes comes from the app folder and not from the app/components/account. The component exposes some membership related functions (login, logout, getUsername) which will allow us to display the user’s name and a logout button if authenticated and a login button if not. We created a BaseRequestOption class in order to override the default options used by Http to create and send Requests. We declared that we want the Content-Type header to be equal to application/json when making http requests to our Web API back-end infrastructure. Last but not least we called the bootstrap function to instantiate an Angular application. We explicitly specified that the root component will be the AppRoot. Then we set the request options we created before and a LocationStrategy as well. We have also made our custom services available through our application. The app.html template is the one the will host two important features. The first one is the navigation top-bar and of course the main router-outlet. Add it under wwwroot/app folder.

<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" [routerLink]="['/Home']"><i class="fa fa-home fa-fw"></i>&nbsp;Photo Gallery</a>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav navbar-right">
                <li>
                    <a href="http://wp.me/p3mRWu-11L" target="_blank"><i class="fa fa-info fa-fw"></i>&nbsp;About</a>
                </li>
                <li>
                    <a [routerLink]="[routes.photos.name]"><i class="fa fa-camera fa-fw"></i>&nbsp;Photos</a>
                </li>
                <li>
                <li><a [routerLink]="[routes.albums.name]"><i class="fa fa-picture-o fa-fw"></i>&nbsp;Albums</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-pencil-square-o fa-fw"></i>&nbsp;Blog<b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li>
                            <a href="https://github.com/chsakell" target="_blank"><i class="fa fa-github fa-fw"></i>&nbsp;Github</a>
                        </li>
                        <li>
                            <a href="https://twitter.com/chsakellsblog" target="_blank"><i class="fa fa-twitter fa-fw"></i>&nbsp;Twitter</a>
                        </li>
                        <li>
                            <a href="https://www.facebook.com/chsakells.blog" target="_blank"><i class="fa fa-facebook fa-fw"></i>&nbsp;Facebook</a>
                        </li>
                        <li>
                            <a href="http://chsakell.com/2015/08/23/building-single-page-applications-using-web-api-and-angularjs-free-e-book/" target="_blank"><i class="fa fa-backward fa-fw"></i>&nbsp;Previous version</a>
                        </li>
                    </ul>
                </li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-user fa-fw"></i>&nbsp;{{getUserName()}}<b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li *ngIf="!isUserLoggedIn()">
                            <a [routerLink]="['/Account/Login']"><i class="fa fa-unlock-alt fa-fw"></i>&nbsp;Sign in</a>
                        </li>
                        <li *ngIf="isUserLoggedIn()">
                            <a  [routerLink]="[routes.home.name]" (click)="logout()"><i class="fa fa-power-off fa-fw"></i>&nbsp;Logout</a>
                        </li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>

<div>
    <router-outlet></router-outlet>
</div>

Since the top-bar is html elements of the base host component, it will be always displayed whichever component and route is active. Believe it or not, we have finished building the PhotoGallery cross-platform ASP.NET 5 Single Page Application. At this point you can run the build-spa Gulp task, build and run the application. Let’s view the final result with a .gif:aspnet5-angular2.

Discussion

Single Page Application Scalability

One thing you may wonder at the moment is.. I cannot remember referensing all those components we have coded (e.g. Home, Login, Albums..) in any page, so how is this even working? The answer to this question will show us the way to scale this app in case we wanted to provide multiple views. Let me remind you first that when the compile-typescript task runs all the transpiled files end in the wwwroot/lib/spa folder. Hense, we should wait to find the transpiled result of our root component AppRoot in a wwwroot/lib/spa/app.js file. This is the only file you need to reference in your page that will eventually host this single page application. And which is that page? The Index.cshtml of course..

<photogallery-app>
    <div class="loading">Loading</div>
</photogallery-app>

@section scripts
{
    <script src="~/lib/js/jquery.fancybox.pack.js"></script>
    <script src="~/lib/js/alertify.min.js"></script>
}

@section customScript
{
    System.import('./lib/spa/app.js').catch(console.log.bind(console));
    $(document).ready(function() {
    $('.fancybox').fancybox();
    });
}

All the required components and their javascript-transpiled files will be automatically loaded as required using the universal dynamic module loader SystemJS. Let’s assume now that we need to scale this app and create a new MVC View named Test.cshtml and of course we want to bootstrap a hall new different Single Page Application. This Test.cshtml could use the default _Layout.cshtml page which is a bootstrap template that references only the basic libraries to start coding Angular 2 applications. All you have to do now is create a new root component, for example AppRootTest in a typescript file named apptest.ts under wwwroot/app. This component will probably has its own routes, so you could create them as well. If you see that your application gets too large and you code too many root components, you can place all of them under a new folder named wwwroot/app/bootstrappers. Then part of the Test.cshtml page could look like this.

<photogallery-apptest>
    <div class="loading">Loading</div>
</photogallery-apptest>

@section scripts
{
    <script src="~/lib/js/jquery.fancybox.pack.js"></script>
    <script src="~/lib/js/alertify.min.js"></script>
}

@section customScript
{
    System.import('./lib/spa/apptest.js').catch(console.log.bind(console));
    $(document).ready(function() {
    $('.fancybox').fancybox();
    });
}

As you can see this page will only load the root component AppTest and its related files as well.

ASP.NET 5 architecture

In case this isn’t your first time you visit my blog, you will know that I always try to keep things clean as far as instrastrure concerns. Yet, on this application I kept all data repositories and services inside the web app. I did it for a reason. First of all I excluded the classic c# class libraries. ASP.NET 5 is a cross-platform framework, it isn’t supposed to work directly with those libraries. MAC and linux users have probably no clue what a c# class library is. So an alternative option may would be to use ASP.NET 5 class libraries (packages). Those types of projects are cross-platform compiled and can target multiple frameworks. At the time though this post was written, I found that they weren’t that much stable to use them in this app so I decided to focus on the Web only. I am very sure that time will show us the best way to architect the different components in a ASP.NET 5 application. When this time comes, I will certainly post about it.

Conlusion

Building the PhotoGallery application was a great experience for me and I hope so for you as well. I believe that ASP.NET 5 will help us build more robust, scalable and feature-focused applications. The option where we can only install what we only want to use is simply great. The idea that you have no unnecessary packages installed or unnecessary code running behind the scenes makes you think that you build super lightweighted and fast applications. Same applies for Angular 2 for which I found that coding with it had a lot of fun. Import only what you actually want to use, inject only what actually need to inject, nothing more. Let’s recap at this point the best features we saw on this post.

  1. Create and configure an ASP.NET 5 application to use MVC 6 services.
  2. Setup Entity Framework 7 and dependency injection.
  3. Enable Entity Framework migrations.
  4. Resolve any dependency conficts between different target frameworks.
  5. Setup an ASP.NET 5 application to start coding with Angular 2 and Typescript using NPM, Bower, Gulp.
  6. View in action several Angular 2 features such as Routing, nested routing, Components, HTTP requests.
  7. CRUD operations using Angular 2 and Typescript
  8. Form validation with Angular 2

    The source code of the PhotoGallery app is available on GitHub and in case you haven’t followed coding with me, you will also find detailed instructions to run it over there. You can submit your comments for this post on the comments area.
    aspnet5-angular2-32

    In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

    Facebook Twitter
    .NET Web Application Development by Chris S.
    facebook twitter-small

Building multi-client APIs in ASP.NET

$
0
0

The structure of a modern web application nowadays consists of one or more APIs and one or more different type of clients that consume those APIs. The thing is that despite the fact that those clients may required to support the same business logic, they have entirely different specifications in terms of CPU, memory or even network bandwidth usage. To make things more clearer and denote the problem we are going to solve on this post let’s assume your boss wants you to build an online solution to support the business of the company. The requirement is simple: clients should be able to view their data through an ASP.NET MVC Web application, a mobile oriented website build with AngularJS, some native (Android or IOS) or hybrid Ionic mobile applications and last but not least a desktop application.

Houston we have a problem

After being shocked on your boss’s requirements you gather yourself up and decide that you are going to build a single API and make all the clients consume it. Is this going to work? It depends on how you design it. In case you decide to serve the same data over the wire for all types of clients then boom! As long as your business logic and hence your ViewModels being served grow, the clients with less CPU and memory power such as the mobile applications, will probably start being slower, impacting the user’s experience (not to mention your boss’s temper). If you think about it, a list item in a mobile application usually renders about 5 properties so why even bother to request the entire viewmodel which may reach 25 properties? From my experience, I have seen hybrid applications built with Ionic getting slower or even crash when trying to render 250 items with 10-15 properties for each item. This means that you have to be more careful with your API and find a solution that will not cause those types of issues on your clients. A simple solution that you may come up to is to have different versions of your APIs for each client. This would mean that you would have different Action methods and viewmodels for the same resources. I would recommend you to avoid this approach because this would cause dramatically maintainability and testability issues. Versioning in APIs should be used for supporting new features, not different clients.

The solution

The best solution would be to allow your different clients to request the exact properties for the resources being served. But what do I mean by that? Let’s explain it with an example. Let’s assume that there is a resource uri /api/tracks/1 that returns a single track with the following JSON properties:

{
  "TrackId": 1,
  "AlbumId": 1,
  "Bytes": 11170334,
  "Composer": "Angus Young, Malcolm Young, Brian Johnson",
  "GenreId": 1,
  "MediaTypeId": 1,
  "Milliseconds": 343719,
  "Name": "For Those About To Rock (We Salute You)",
  "UnitPrice": 0.99
}

What we would like to achieve is to allow the client to request for certain properties on that resource by making a request to /api/tracks/1?props=bytes,milliseconds,name:

{
  "Bytes": 11170334,
  "Milliseconds": 343719,
  "Name": "For Those About To Rock (We Salute You)"
}

What we gain with this approach is reduce the amount of data being served over the wire between the server and the client and hence the time for the request to be completed. More over the client’s CPU and memory usage isn’t overwhelmed which means better user experience and application performance. This approach may sounds easy to implement but it isn’t. The algorithm I created is based on parsing and traversing a Newtonsoft.Json.Linq.JToken representation of the viewmodel. We will discuss more on this algorithm when we reach the respective section of the post but for now let’s say that the approach should support nested or navigation properties as well. For example assuming that we have the resource /api/albums/22:

{
  "AlbumId": 22,
  "ArtistName": "Caetano Veloso",
  "Title": "Sozinho Remix Ao Vivo",
  "Track": [
    {
      "TrackId": 223,
      "AlbumId": 22,
      "Bytes": 14462072,
      "Composer": null,
      "GenreId": 7,
      "MediaTypeId": 1,
      "Milliseconds": 436636,
      "Name": "Sozinho (Hitmakers Classic Mix)",
      "UnitPrice": 0.99
    },
    {
      "TrackId": 224,
      "AlbumId": 22,
      "Bytes": 6455134,
      "Composer": null,
      "GenreId": 7,
      "MediaTypeId": 1,
      "Milliseconds": 195004,
      "Name": "Sozinho (Hitmakers Classic Radio Edit)",
      "UnitPrice": 0.99
    }
  ]
}

We need to be able to request certain properties for the nested Track property as well by making a request to /api/albums/22?props=artistname,title,track(trackid;bytes;name).

{
  "ArtistName": "Caetano Veloso",
  "Title": "Sozinho Remix Ao Vivo",
  "Track": [
    {
      "TrackId": 223,
      "Bytes": 14462072,
      "Name": "Sozinho (Hitmakers Classic Mix)"
    },
    {
      "TrackId": 224,
      "Bytes": 6455134,
      "Name": "Sozinho (Hitmakers Classic Radio Edit)"
    }
  ]
}

Awesome right? Let’s start building this multi-client supported API!
multi-client-api-06

ASP.NET Core and Entity Framework Core

The project we are going to build here will be an ASP.NET MVC 6 application which means you need to have ASP.NET Core installed on your machine. In case though you don’t want to follow along that’s OK. The most important part of the post is the method that alternates the API’s response according to the request’s options. Because of the fact that we need a database to demonstrate the multi-client support of our API I decided to use the well known Chinook database which you can download for free here. Create the Chinook database in your SQL Server by running the Chinook_SqlServer_AutoIncrementPKs.sql file you will find in the downloaded .zip file. I found in this post the opportunity to show you how to initialize the entities using the Database First approach with Entity Framework Core. Start by creating a new Web Application named ShapingAPI by selecting the ASP.NET 5 empty template. Alter the project.json as follow and let Visual Studio 2015 restore the packages.

{
    "version": "1.0.0-*",
    "compilationOptions": {
        "emitEntryPoint": true
    },

    "dependencies": {
        "Microsoft.ApplicationInsights.AspNet": "1.0.0-rc1",
        "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
        "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
        "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
        "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
        "Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
        "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
        "Microsoft.Extensions.Logging": "1.0.0-rc1-final",
        "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
        "Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final",
        "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
        "EntityFramework.MicrosoftSqlServer.Design": "7.0.0-rc1-final",
        "EntityFramework.Commands": "7.0.0-rc1-final",
        "AutoMapper.Data": "1.0.0-beta1",
        "Newtonsoft.Json": "7.0.1"
    },

    "commands": {
        "web": "Microsoft.AspNet.Server.Kestrel",
        "ef": "EntityFramework.Commands"
    },

    "frameworks": {
        "dnx451": { },
        "dnxcore50": { }
    },

    "exclude": [
        "wwwroot",
        "node_modules"
    ],
    "publishExclude": [
        "**.user",
        "**.vspscc"
    ]
}

I have highlighted the packages and commands to be used for generating the entities from the Chinook database. Now open a command prompt, navigate at the root of the application where the project.json exists and run the following command:

dnx ef dbcontext scaffold "Server=(localdb)\v11.0;Database=Chinook;Trusted_Connection=True;" EntityFramework.MicrosoftSqlServer --outputDir Entities

This will scaffold the Entities for you and a DbContext class as well.
multi-client-api-01
You shouldn’t alter those entities by yourself. In my case I have commented out the connection string related line in the ChinookContext class.

protected override void OnConfiguring(DbContextOptionsBuilder options)
 {
      //options.UseSqlServer(@"Server=(localdb)\v11.0;Database=Chinook;Trusted_Connection=True;");
 }

.. and add an appsettings.json file under the root of the application as follow:

{
    "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
            "Default": "Verbose",
            "System": "Information",
            "Microsoft": "Information"
        }
    },
    "Data": {
        "ChinookConnection": {
            "ConnectionString": "Server=(localdb)\\v11.0;Database=Chinook;Trusted_Connection=True;MultipleActiveResultSets=true"
        }
    }
}

Alter the connection string to reflect your database environment. Now switch to the Startup.cs file and change it as follow:

public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            // Set up configuration sources.
            var builder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json");

            if (env.IsEnvironment("Development"))
            {
                // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
                builder.AddApplicationInsightsSettings(developerMode: true);
            }

            builder.AddEnvironmentVariables();
            Configuration = builder.Build().ReloadOnChanged("appsettings.json");
        }

        public IConfigurationRoot Configuration { get; set; }

        // This method gets called by the runtime. Use this method to add services to the container
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);

            services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<ChinookContext>(options => options.UseSqlServer(Configuration["Data:ChinookConnection:ConnectionString"]));

            // Repositories
            services.AddScoped<IAlbumRepository, AlbumRepository>();
            services.AddScoped<IArtistRepository, ArtistRepository>();
            services.AddScoped<ICustomerRepository, CustomerRepository>();
            services.AddScoped<IEmployeeRepository, EmployeeRepository>();
            services.AddScoped<IGenreRepository, GenreRepository>();
            services.AddScoped<IInvoiceLineRepository, InvoiceLineRepository>();
            services.AddScoped<IInvoiceRepository, InvoiceRepository>();
            services.AddScoped<IMediaTypeRepository, MediaTypeRepository>();
            services.AddScoped<IPlaylistTrackRepository, PlaylistTrackRepository>();
            services.AddScoped<IPlaylistRepository, PlaylistRepository>();
            services.AddScoped<ITrackRepository, TrackRepository>();

            services.AddMvc()
                .AddJsonOptions(options =>
                {
                    options.SerializerSettings.ReferenceLoopHandling =
                        Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                    options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
                    options.SerializerSettings.ContractResolver =
                        new CamelCasePropertyNamesContractResolver();
                });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseIISPlatformHandler();

            app.UseApplicationInsightsRequestTelemetry();

            app.UseApplicationInsightsExceptionTelemetry();

            app.UseStaticFiles();

            AutoMapperConfiguration.Configure();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                    // Uncomment the following line to add a route for porting Web API 2 controllers.
                    //routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
                });
        }

        // Entry point for the application.
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }

We haven’t created the repositories yet neither the AutoMapperConfiguarion class. Let’s start with the repositories first. We will only add Get related functions since retrieving customized data is the main problem we are trying to solve. Add the the Infrastructure/Data/Repositories folder hierarchy under the root as shown in the picture below.
multi-client-api-02
Add the following classes under the Infrastructure/Data folder.

public interface IRepository<TEntity>
    {
        #region READ
        TEntity Get(Expression<Func<TEntity, bool>> predicate);
        TEntity Get(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includeProperties);
        IQueryable<TEntity> GetAll();
        IQueryable<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties);

        #endregion
    }
public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected DbContext _context;
        protected DbSet<TEntity> _dbSet;

        public Repository(DbContext context)
        {
            _context = context;
            _dbSet = _context.Set<TEntity>();
        }

        #region READ
        public TEntity Get(Expression<Func<TEntity, bool>> predicate)
        {
            return _dbSet.FirstOrDefault(predicate);
        }

        public TEntity Get(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includeProperties)
        {
            IQueryable<TEntity> query = _dbSet;

            if (includeProperties != null)
                foreach (var property in includeProperties)
                {
                    query = query.Include(property);
                }

            return query.FirstOrDefault(predicate);
        }

        public IQueryable<TEntity> GetAll()
        {
            return _dbSet.AsQueryable<TEntity>();
        }

        public IQueryable<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties)
        {
            IQueryable<TEntity> query = _dbSet.AsQueryable<TEntity>();

            if (includeProperties != null)
                foreach (var property in includeProperties)
                {
                    query = query.Include(property);
                }

            return query;
        }

        #endregion
    }

Now add the following files under the Infrastructure/Data/Repositories folder.

namespace ShapingAPI.Infrastructure.Data.Repositories
{
    public interface IAlbumRepository : IRepository<Album> { }

    public interface IArtistRepository : IRepository<Artist>  {
        IEnumerable<Artist> LoadAll();
        Artist Load(int artistId);
    }

    public interface ICustomerRepository : IRepository<Customer> {
        IEnumerable<Customer> LoadAll();
        Customer Load(int customerId);
    }

    public interface IEmployeeRepository : IRepository<Employee> { }

    public interface IGenreRepository : IRepository<Genre> { }

    public interface IInvoiceLineRepository : IRepository<InvoiceLine> { }

    public interface IInvoiceRepository : IRepository<Invoice> {
        IEnumerable<Invoice> LoadAll();
        Invoice Load(int invoiceId);
    }

    public interface IMediaTypeRepository : IRepository<MediaType> { }

    public interface IPlaylistTrackRepository : IRepository<PlaylistTrack> { }

    public interface IPlaylistRepository : IRepository<Playlist> { }

    public interface ITrackRepository : IRepository<Track> { }

}
namespace ShapingAPI.Infrastructure.Data.Repositories
{
    public class AlbumRepository : Repository<Album>, IAlbumRepository
    {
        public AlbumRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class ArtistRepository : Repository<Artist>, IArtistRepository
    {
        public ArtistRepository(ChinookContext context)
            : base(context)
        {

        }

        public IEnumerable<Artist> LoadAll()
        {
            IQueryable<Artist> query = this._dbSet;

            query = query.Include(a => a.Album).ThenInclude(al => al.Track);

            return query.ToList();
        }

        public Artist Load(int artistId)
        {
            IQueryable<Artist> query = this._dbSet;

            query = query.Include(a => a.Album).ThenInclude(al => al.Track);

            return query.FirstOrDefault(a => a.ArtistId == artistId);
        }
    }

    public class CustomerRepository : Repository<Customer>, ICustomerRepository
    {
        public CustomerRepository(ChinookContext context)
            : base(context)
        { }

        public IEnumerable<Customer> LoadAll()
        {
            IQueryable<Customer> query = this._dbSet;

            query = query.Include(c => c.Invoice).ThenInclude(i => i.InvoiceLine);

            return query.ToList();
        }

        public Customer Load(int customerId)
        {
            IQueryable<Customer> query = this._dbSet;

            query = query.Include(c => c.Invoice).ThenInclude(i => i.InvoiceLine);

            return query.FirstOrDefault(c => c.CustomerId == customerId);
        }
    }

    public class EmployeeRepository : Repository<Employee>, IEmployeeRepository
    {
        public EmployeeRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class GenreRepository : Repository<Genre>, IGenreRepository
    {
        public GenreRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class InvoiceLineRepository : Repository<InvoiceLine>, IInvoiceLineRepository
    {
        public InvoiceLineRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class InvoiceRepository : Repository<Invoice>, IInvoiceRepository
    {
        public InvoiceRepository(ChinookContext context)
            : base(context)
        { }

        public IEnumerable<Invoice> LoadAll()
        {
            IQueryable<Invoice> query = this._dbSet;

            query = query.Include(i => i.Customer).ThenInclude(c => c.Invoice);
            query = query.Include(i => i.InvoiceLine);

            return query.ToList();
        }

        public Invoice Load(int invoiceId)
        {
            IQueryable<Invoice> query = this._dbSet;

            query = query.Include(i => i.Customer).ThenInclude(c => c.Invoice);
            query = query.Include(i => i.InvoiceLine);

            return query.FirstOrDefault(i => i.InvoiceId == invoiceId);
        }
    }

    public class MediaTypeRepository : Repository<MediaType>, IMediaTypeRepository
    {
        public MediaTypeRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class PlaylistTrackRepository : Repository<PlaylistTrack>, IPlaylistTrackRepository
    {
        public PlaylistTrackRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class PlaylistRepository : Repository<Playlist>, IPlaylistRepository
    {
        public PlaylistRepository(ChinookContext context)
            : base(context)
        { }
    }

    public class TrackRepository : Repository<Track>, ITrackRepository
    {
        public TrackRepository(ChinookContext context)
            : base(context)
        { }
    }
}

Create a Mappings folder under Infrastructure and add the following classes:

public class AutoMapperConfiguration
    {
        public static void Configure()
        {
            Mapper.Initialize(x =>
            {
                x.AddProfile<DomainToViewModelMappingProfile>();
            });
        }
    }
public class DomainToViewModelMappingProfile : Profile
    {
        protected override void Configure()
        {
            //Mapper.CreateMap<Track, TrackViewModel>();

            //Mapper.CreateMap<Album, AlbumViewModel>()
            //     .ForMember(vm => vm.ArtistName, map => map.MapFrom(a => a.Artist.Name));

            //Mapper.CreateMap<Invoice, InvoiceViewModel>();

            //Mapper.CreateMap<Artist, ArtistViewModel>();

            //Mapper.CreateMap<Customer, CustomerViewModel>()
            //    .ForMember(vm => vm.Address, map => map.MapFrom(c => new AddressViewModel()
            //    {
            //        Address = c.Address,
            //        City = c.City,
            //        Country = c.Country,
            //        PostalCode = c.PostalCode,
            //        State = c.State
            //    }))
            //    .ForMember(vm => vm.Contact, map => map.MapFrom(c => new ContactViewModel()
            //    {
            //        Email = c.Email,
            //        Fax = c.Fax,
            //        Phone = c.Phone
            //    }))
            //    .ForMember(vm => vm.TotalInvoices, map => map.MapFrom(c => c.Invoice.Count()));

            //Mapper.CreateMap<Employee, EmployeeViewModel>();

            //Mapper.CreateMap<Genre, GenreViewModel>()
            //    .ForMember(vm => vm.Tracks, map => map.MapFrom(g => g.Track.Select(t => t.TrackId).ToList()));

            //Mapper.CreateMap<InvoiceLine, InvoiceLineViewModel>();

            //Mapper.CreateMap<MediaType, MediaTypeViewModel>()
            //    .ForMember(vm => vm.Tracks, map => map.MapFrom(m => m.Track.Select(t => t.TrackId).ToList()));

            //Mapper.CreateMap<PlaylistTrack, PlaylistTrackViewModel>();

            //Mapper.CreateMap<Playlist, PlaylistViewModel>();
        }
    }

We left some lines commented out since we haven’t created yet the View Models. We are going to uncomment them one by one later on. At this point you should be able to resolve all dependencies inside the Startup class.

Shaping API responses

It’s high time to serve some data! We will start with the least complex example where we ‘ll try to serve customized responses for the Track entity. Let’s create the respective ViewModel first. Create the TrackViewModel class under a newly created folder named ViewModels at the root of the application.

public class TrackViewModel
    {
        public TrackViewModel() { }

        public int TrackId { get; set; }
        public int? AlbumId { get; set; }
        public int? Bytes { get; set; }
        public string Composer { get; set; }
        public int? GenreId { get; set; }
        public int MediaTypeId { get; set; }
        public int Milliseconds { get; set; }
        public string Name { get; set; }
        public decimal UnitPrice { get; set; }
    }

In case you didn’t notice, we have removed any navigation properties exist in the original entity Track cause this will be our simplest example. From now on, any time we add a ViewModel make sure you uncomment the respective line in the DomainToViewModelMappingProfile class. Before writing the first controller let’s add two important classes first. Create a new folder named Core under Infrastructure and paste the Expressions class.

public class Expressions
    {
        public static Expression<Func<Track, object>>[] LoadTrackNavigations()
        {
            Expression<Func<Track, object>>[] _navigations = {
                    t => t.Album,
                    t => t.Genre,
                    t => t.InvoiceLine,
                    t => t.MediaType
            };

            return _navigations;
        }

        public static Expression<Func<Customer, object>>[] LoadCustomerNavigations()
        {
            Expression<Func<Customer, object>>[] _navigations = {
                         c => c.Invoice,
                         c => c.SupportRep
             };

            return _navigations;
        }

        public static Expression<Func<Album, object>>[] LoadAlbumNavigations()
        {
            Expression<Func<Album, object>>[] _navigations = {
                         a => a.Track,
                         a => a.Artist
             };

            return _navigations;
        }

        public static Expression<Func<Artist, object>>[] LoadArtistNavigations()
        {
            Expression<Func<Artist, object>>[] _navigations = {
                         a => a.Album
             };

            return _navigations;
        }
    }

This class has methods to inform Entity Framework what navigation properties to include when retrieving data from the Chinook database. The next class gave me serious headaches since is the one that contains the FilterProperties method which does all the magic work. The Utils.cs class is kind of large to paste here so just copy it from here. We will explain the FilterProperties behavior later on. Add a new folder named Controllers and create the first controller named TracksController.

[Route("api/[controller]")]
    public class TracksController : Controller
    {
        #region Properties
        private readonly ITrackRepository _trackRepository;
        private List<string> _properties = new List<string>();
        private Expression<Func<Track, object>>[] includeProperties;
        private const int maxSize = 50;
        #endregion

        #region Constructor
        public TracksController(ITrackRepository trackRepository)
        {
            _trackRepository = trackRepository;

            _properties = new List<string>();
            includeProperties = Expressions.LoadTrackNavigations();
        }
        #endregion

        #region Actions
        public ActionResult Get(string props = null, int page = 1, int pageSize = maxSize)
        {
            try
            {
                var _tracks = _trackRepository.GetAll(includeProperties).Skip((page - 1) * pageSize).Take(pageSize);

                var _tracksVM = Mapper.Map<IEnumerable<Track>, IEnumerable<TrackViewModel>>(_tracks);

                string _serializedTracks = JsonConvert.SerializeObject(_tracksVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedTracks);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());

                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }

        [Route("{trackId}")]
        public ActionResult Get(int trackId, string props = null)
        {
            try
            {


                var _track = _trackRepository.Get(t => t.TrackId == trackId, includeProperties);

                if (_track == null)
                {
                    return HttpNotFound();
                }

                var _trackVM = Mapper.Map<Track, TrackViewModel>(_track);

                string _serializedTrack = JsonConvert.SerializeObject(_trackVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedTrack);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());
                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }
        #endregion
    }

OK, let’s explain what we actually do here. Firstly we make sure we include any navigation properties we want EF to include when retrieving Track entities. We have also set some pagination logic here with a maxsize = 50 items (in case isn’t explicit set on the request). After retrieving the data from the database we make the mapping with the respective View model. Later on we serialize the result and create a JToken representation. Finally and only if the user has set a respective set of properties to get using the props key on the query string, we pass this token on the FilterProperties method. The Get(int trackId, string props = null) action method works in the same way. Build and run the application to test it. Let’s retrieve all the tracks in their original form. This is the response when sending an /api/tracks request.

[
  {
    "TrackId": 2,
    "AlbumId": 2,
    "Bytes": 5510424,
    "Composer": null,
    "GenreId": 1,
    "MediaTypeId": 2,
    "Milliseconds": 342562,
    "Name": "Balls to the Wall",
    "UnitPrice": 0.99
  },
  {
    "TrackId": 3,
    "AlbumId": 3,
    "Bytes": 3990994,
    "Composer": "F. Baltes, S. Kaufman, U. Dirkscneider & W. Hoffman",
    "GenreId": 1,
    "MediaTypeId": 2,
    "Milliseconds": 230619,
    "Name": "Fast As a Shark",
    "UnitPrice": 0.99
  },
  {
    "TrackId": 4,
    "AlbumId": 3,
    "Bytes": 4331779,
    "Composer": "F. Baltes, R.A. Smith-Diesel, S. Kaufman, U. Dirkscneider & W. Hoffman",
    "GenreId": 1,
    "MediaTypeId": 2,
    "Milliseconds": 252051,
    "Name": "Restless and Wild",
    "UnitPrice": 0.99
  },
  // code omitted

Now let’s assume your mobile clients needs to retrieve only the trackid, name and unitprice properties. All you have to do is send a request to api/tracks?props=trackid,name,unitprice.

[
  {
    "TrackId": 2,
    "Name": "Balls to the Wall",
    "UnitPrice": 0.99
  },
  {
    "TrackId": 3,
    "Name": "Fast As a Shark",
    "UnitPrice": 0.99
  },
  {
    "TrackId": 4,
    "Name": "Restless and Wild",
    "UnitPrice": 0.99
  },
  {
    "TrackId": 5,
    "Name": "Princess of the Dawn",
    "UnitPrice": 0.99
  },
  // code omitted

The FilterProperties method removed all other properties you didn’t mention in the props key. At this point we can understand that if we want to get a certain set of properties we only need to defined them as a comma separated string value on the props query string key. If we don’t define the props key then all the properties will be returned.

Navigations

Things are getting serious when we deal with entities that contains Navigation properties. We ‘ll start with the AlbumViewModel which contains a collection of TrackViewModel items. Add it in the ViewModels folder and of course uncomment the respective line in the AutoMapperConfiguration class.

public class AlbumViewModel
    {
        public AlbumViewModel()
        {
            Track = new HashSet<TrackViewModel>();
        }

        public int AlbumId { get; set; }
        public string ArtistName { get; set; }
        public string Title { get; set; }
        public virtual ICollection<TrackViewModel> Track { get; set; }
    }

And of course the AlbumsController.

[Route("api/[controller]")]
    public class AlbumsController : Controller
    {
        #region Properties
        private readonly IAlbumRepository _albumRepository;
        private List<string> _properties = new List<string>();
        private Expression<Func<Album, object>>[] includeProperties;
        private const int maxSize = 50;
        #endregion

        #region Constructor
        public AlbumsController(IAlbumRepository albumRepository)
        {
            _albumRepository = albumRepository;

            _properties = new List<string>();
            includeProperties = Expressions.LoadAlbumNavigations();
        }
        #endregion

        #region Actions
        public ActionResult Get(string props = null, int page = 1, int pageSize = maxSize)
        {
            try
            {
                var _albums = _albumRepository.GetAll(includeProperties).Skip((page - 1) * pageSize).Take(pageSize);

                var _albumsVM = Mapper.Map<IEnumerable<Album>, IEnumerable<AlbumViewModel>>(_albums);

                string _serializedAlbums = JsonConvert.SerializeObject(_albumsVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedAlbums);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());

                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }

        [Route("{albumId}")]
        public ActionResult Get(int albumId, string props = null)
        {
            try
            {
                var _album = _albumRepository.Get(t => t.AlbumId == albumId, includeProperties);

                if (_album == null)
                {
                    return HttpNotFound();
                }

                var _albumVM = Mapper.Map<Album, AlbumViewModel>(_album);

                string _serializedAlbum = JsonConvert.SerializeObject(_albumVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedAlbum);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());
                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }
        #endregion
    }

Let’s start by making a request to /api/albums (the first 50 albums) without defining any properties.

[
  {
    "AlbumId": 2,
    "ArtistName": "Accept",
    "Title": "Balls to the Wall",
    "Track": [
      {
        "TrackId": 2,
        "AlbumId": 2,
        "Bytes": 5510424,
        "Composer": null,
        "GenreId": 1,
        "MediaTypeId": 2,
        "Milliseconds": 342562,
        "Name": "Balls to the Wall",
        "UnitPrice": 0.99
      }
    ]
  },
  {
    "AlbumId": 3,
    "ArtistName": "Accept",
    "Title": "Restless and Wild",
    "Track": [
      {
        "TrackId": 3,
        "AlbumId": 3,
        "Bytes": 3990994,
        "Composer": "F. Baltes, S. Kaufman, U. Dirkscneider & W. Hoffman",
        "GenreId": 1,
        "MediaTypeId": 2,
        "Milliseconds": 230619,
        "Name": "Fast As a Shark",
        "UnitPrice": 0.99
      },
      {
        "TrackId": 4,
        "AlbumId": 3,
        "Bytes": 4331779,
        "Composer": "F. Baltes, R.A. Smith-Diesel, S. Kaufman, U. Dirkscneider & W. Hoffman",
        "GenreId": 1,
        "MediaTypeId": 2,
        "Milliseconds": 252051,
        "Name": "Restless and Wild",
        "UnitPrice": 0.99
      },
	  // code omitted

Track property is a collection of TrackViewModel items. Let’s focus on the 3rd album and request only the ArtistName, Title and Track properties requesting /api/albums/3?props=artistname,title,track.

{
  "ArtistName": "Accept",
  "Title": "Restless and Wild",
  "Track": [
    {
      "TrackId": 3,
      "AlbumId": 3,
      "Bytes": 3990994,
      "Composer": "F. Baltes, S. Kaufman, U. Dirkscneider & W. Hoffman",
      "GenreId": 1,
      "MediaTypeId": 2,
      "Milliseconds": 230619,
      "Name": "Fast As a Shark",
      "UnitPrice": 0.99
    },
    {
      "TrackId": 4,
      "AlbumId": 3,
      "Bytes": 4331779,
      "Composer": "F. Baltes, R.A. Smith-Diesel, S. Kaufman, U. Dirkscneider & W. Hoffman",
      "GenreId": 1,
      "MediaTypeId": 2,
      "Milliseconds": 252051,
      "Name": "Restless and Wild",
      "UnitPrice": 0.99
    },
    // code omitted
    }
  ]
}

As we can see the nested TrackViewModel items inside the Track collection were returned with all their properties. What we would like to have though is the ability to cut certain properties for those items as well. So how can we do it? Simply, by encapsulating the nested properties, semicolon separated inside parenthesis. Assuming we only want the trackid and unitprice for each track we would send the request /api/albums/3?props=artistname,title,track(trackid;unitprice).

{
  "ArtistName": "Accept",
  "Title": "Restless and Wild",
  "Track": [
    {
      "TrackId": 3,
      "UnitPrice": 0.99
    },
    {
      "TrackId": 4,
      "UnitPrice": 0.99
    },
    {
      "TrackId": 5,
      "UnitPrice": 0.99
    }
  ]
}

Let’s keep the pace up and procceed with the ArtistViewModel which has a collection of AlbumViewModel items.

public class ArtistViewModel
    {
        public ArtistViewModel()
        {
            Album = new HashSet<AlbumViewModel>();
        }

        public int ArtistId { get; set; }
        public string Name { get; set; }

        public virtual ICollection<AlbumViewModel> Album { get; set; }
    }
[Route("api/[controller]")]
    public class ArtistsController : Controller
    {
        #region Properties
        private readonly IArtistRepository _artistRepository;
        private List<string> _properties = new List<string>();
        private const int maxSize = 50;
        #endregion

        #region Constructor
        public ArtistsController(IArtistRepository artistRepository)
        {
            _artistRepository = artistRepository;

            _properties = new List<string>();
        }
        #endregion

        #region Actions
        public ActionResult Get(string props = null, int page = 1, int pageSize = maxSize)
        {
            try
            {
                var _artists = _artistRepository.LoadAll().Skip((page - 1) * pageSize).Take(pageSize);

                var _artistsVM = Mapper.Map<IEnumerable<Artist>, IEnumerable<ArtistViewModel>>(_artists);

                string _serializedArtists = JsonConvert.SerializeObject(_artistsVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedArtists);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());

                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }

        [Route("{artistId}")]
        public ActionResult Get(int artistId, string props = null)
        {
            try
            {
                var _artist = _artistRepository.Load(artistId);

                if (_artist == null)
                {
                    return HttpNotFound();
                }

                var _artistVM = Mapper.Map<Artist, ArtistViewModel>(_artist);

                string _serializedArtist = JsonConvert.SerializeObject(_artistVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedArtist);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());
                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }
        #endregion
    }

Let’s focus on a specific Artist making a request to /api/artists/6.

{
  "ArtistId": 6,
  "Name": "Antônio Carlos Jobim",
  "Album": [
    {
      "AlbumId": 8,
      "ArtistName": "Antônio Carlos Jobim",
      "Title": "Warner 25 Anos",
      "Track": [
        {
          "TrackId": 63,
          "AlbumId": 8,
          "Bytes": 5990473,
          "Composer": null,
          "GenreId": 2,
          "MediaTypeId": 1,
          "Milliseconds": 185338,
          "Name": "Desafinado",
          "UnitPrice": 0.99
        },
        {
          "TrackId": 64,
          "AlbumId": 8,
          "Bytes": 9348428,
          "Composer": null,
          "GenreId": 2,
          "MediaTypeId": 1,
          "Milliseconds": 285048,
          "Name": "Garota De Ipanema",
          "UnitPrice": 0.99
        },
        // code omitted
      ]
    },
    {
      "AlbumId": 34,
      "ArtistName": "Antônio Carlos Jobim",
      "Title": "Chill: Brazil (Disc 2)",
      "Track": [
        {
          "TrackId": 391,
          "AlbumId": 34,
          "Bytes": 9141343,
          "Composer": "Vários",
          "GenreId": 7,
          "MediaTypeId": 1,
          "Milliseconds": 279536,
          "Name": "Garota De Ipanema",
          "UnitPrice": 0.99
        },
        {
          "TrackId": 392,
          "AlbumId": 34,
          "Bytes": 7143328,
          "Composer": "Vários",
          "GenreId": 7,
          "MediaTypeId": 1,
          "Milliseconds": 213237,
          "Name": "Tim Tim Por Tim Tim",
          "UnitPrice": 0.99
        },
        //code omitted
      ]
    }
  ]
}

This artist has two albums with each album containing a set of tracks. Assuming a view in your mobile application needs to display only the names of those albums you would make a request to /api/artists/6?props=album(title).

{
  "Album": [
    {
      "Title": "Warner 25 Anos"
    },
    {
      "Title": "Chill: Brazil (Disc 2)"
    }
  ]
}

In case you also wanted to display the trackids contained in each album and their unitprice you would make a request to /api/artists/6?props=album(title;track(trackid;unitprice)).

{
  "Name": "Antônio Carlos Jobim",
  "Album": [
    {
      "Title": "Warner 25 Anos",
      "Track": [
        {
          "TrackId": 63,
          "UnitPrice": 0.99
        },
        {
          "TrackId": 64,
          "UnitPrice": 0.99
        },
        // code omitted
      ]
    },
    {
      "Title": "Chill: Brazil (Disc 2)",
      "Track": [
        {
          "TrackId": 391,
          "UnitPrice": 0.99
        },
        {
          "TrackId": 392,
          "UnitPrice": 0.99
        },
        // code omitted
      ]
    }
  ]
}

I left the api/customers resource last cause it’s the most complex. First add the following view models.

public class InvoiceViewModel
    {
        public InvoiceViewModel()
        {
            InvoiceLine = new HashSet<InvoiceLineViewModel>();
        }

        public int InvoiceId { get; set; }
        public string BillingAddress { get; set; }
        public string BillingCity { get; set; }
        public string BillingCountry { get; set; }
        public string BillingPostalCode { get; set; }
        public string BillingState { get; set; }
        public int CustomerId { get; set; }
        public DateTime InvoiceDate { get; set; }
        public decimal Total { get; set; }
        public ICollection<InvoiceLineViewModel> InvoiceLine { get; set; }
    }
public class InvoiceLineViewModel
    {
        public int InvoiceLineId { get; set; }
        public int InvoiceId { get; set; }
        public int Quantity { get; set; }
        public int TrackId { get; set; }
        public decimal UnitPrice { get; set; }
    }
public class AddressViewModel
    {
        public string Address { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
        public string PostalCode { get; set; }
        public string State { get; set; }
    }
public class ContactViewModel
    {
        public string Email { get; set; }
        public string Fax { get; set; }
        public string Phone { get; set; }
    }
public class CustomerViewModel
    {
        public CustomerViewModel() { }

        public int CustomerId { get; set; }
        public string Company { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int? SupportRepId { get; set; }
        public int TotalInvoices { get; set; }
        public ICollection<InvoiceViewModel> Invoice { get; set; }
        public AddressViewModel Address { get; set; }
        public ContactViewModel Contact { get; set; }
    }

Make sure you uncomment their AutoMapper configurations as well. A customer may have many invoices which in turn may contain many invoiceline items. I have also created a new ViewModel to save all the address related information in a navigation property named Address and another one for Contact information. Here’s the controller.

[Route("api/[controller]")]
    public class CustomersController : Controller
    {
        #region Properties
        private readonly ICustomerRepository _customerRepository;
        private List<string> _properties = new List<string>();
        private const int maxSize = 50;
        #endregion

        #region Constructor
        public CustomersController(ICustomerRepository customerRepository)
        {
            _customerRepository = customerRepository;

            _properties = new List<string>();
        }
        #endregion

        #region Actions
        public ActionResult Get(string props = null, int page = 1, int pageSize = maxSize)
        {
            try
            {
                var _customers = _customerRepository.LoadAll().Skip((page - 1) * pageSize).Take(pageSize);

                var _customersVM = Mapper.Map<IEnumerable<Customer>, IEnumerable<CustomerViewModel>>(_customers);

                string _serializedCustomers = JsonConvert.SerializeObject(_customersVM, Formatting.None,
                new JsonSerializerSettings()
                {
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                });

                JToken _jtoken = JToken.Parse(_serializedCustomers);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());

                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }

        [Route("{customerId}")]
        public ActionResult Get(int customerId, string props = null)
        {
            try
            {
                var _customer = _customerRepository.Load(customerId);

                if (_customer == null)
                {
                    return HttpNotFound();
                }

                var _customerVM = Mapper.Map<Customer, CustomerViewModel>(_customer);

                string _serializedCustomer = JsonConvert.SerializeObject(_customerVM, Formatting.None,
                    new JsonSerializerSettings()
                    {
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                    });

                JToken _jtoken = JToken.Parse(_serializedCustomer);
                if (!string.IsNullOrEmpty(props))
                    Utils.FilterProperties(_jtoken, props.ToLower().Split(',').ToList());
                return Ok(_jtoken);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500);
            }
        }
        #endregion
    }

Let’s start with a request to /api/customers/5 that serves all the available data.

{
  "CustomerId": 5,
  "Company": "JetBrains s.r.o.",
  "FirstName": "František",
  "LastName": "Wichterlová",
  "SupportRepId": 4,
  "TotalInvoices": 7,
  "Invoice": [
    {
      "InvoiceId": 77,
      "BillingAddress": "Klanova 9/506",
      "BillingCity": "Prague",
      "BillingCountry": "Czech Republic",
      "BillingPostalCode": "14700",
      "BillingState": null,
      "CustomerId": 5,
      "InvoiceDate": "2009-12-08T00:00:00",
      "Total": 1.98,
      "InvoiceLine": [
        {
          "InvoiceLineId": 417,
          "InvoiceId": 77,
          "Quantity": 1,
          "TrackId": 2551,
          "UnitPrice": 0.99
        },
        {
          "InvoiceLineId": 418,
          "InvoiceId": 77,
          "Quantity": 1,
          "TrackId": 2552,
          "UnitPrice": 0.99
        }
      ]
    },
    {
      "InvoiceId": 100,
      "BillingAddress": "Klanova 9/506",
      "BillingCity": "Prague",
      "BillingCountry": "Czech Republic",
      "BillingPostalCode": "14700",
      "BillingState": null,
      "CustomerId": 5,
      "InvoiceDate": "2010-03-12T00:00:00",
      "Total": 3.96,
      "InvoiceLine": [
        {
          "InvoiceLineId": 535,
          "InvoiceId": 100,
          "Quantity": 1,
          "TrackId": 3254,
          "UnitPrice": 0.99
        },
        // code omitted
      ]
    },
    {
      "InvoiceId": 122,
      "BillingAddress": "Klanova 9/506",
      "BillingCity": "Prague",
      "BillingCountry": "Czech Republic",
      "BillingPostalCode": "14700",
      "BillingState": null,
      "CustomerId": 5,
      "InvoiceDate": "2010-06-14T00:00:00",
      "Total": 5.94,
      "InvoiceLine": [
        {
          "InvoiceLineId": 653,
          "InvoiceId": 122,
          "Quantity": 1,
          "TrackId": 457,
          "UnitPrice": 0.99
        },
        // code omitted
      ]
    },
    // code omitted
  ],
  "Address": {
    "Address": "Klanova 9/506",
    "City": "Prague",
    "Country": "Czech Republic",
    "PostalCode": "14700",
    "State": null
  },
  "Contact": {
    "Email": "frantisekw@jetbrains.com",
    "Fax": "+420 2 4172 5555",
    "Phone": "+420 2 4172 5555"
  }
}

Now let’s request only two properties for each navigation property from top to bottom by making a request to /api/customers/5?props=company,invoice(total;invoiceline(invoiceid;quantity)),address(address;city),contact(email;fax)..

{
  "Company": "JetBrains s.r.o.",
  "Invoice": [
    {
      "Total": 1.98,
      "InvoiceLine": [
        {
          "InvoiceId": 77,
          "Quantity": 1
        },
        {
          "InvoiceId": 77,
          "Quantity": 1
        }
      ]
    },
    {
      "Total": 3.96,
      "InvoiceLine": [
        {
          "InvoiceId": 100,
          "Quantity": 1
        },
        // code omitted
      ]
    },
    {
      "Total": 16.86,
      "InvoiceLine": [
        {
          "InvoiceId": 306,
          "Quantity": 1
        },
        // code omitted
      ]
    }
	// code omitted
  ],
  "Address": {
    "Address": "Klanova 9/506",
    "City": "Prague"
  },
  "Contact": {
    "Email": "frantisekw@jetbrains.com",
    "Fax": "+420 2 4172 5555"
  }
}

Discussion

Let’s talk Performance. Can FilterProperties method increase the time the server processes the request? The answer depends on the amount of data you request. In case you remove the pagination logic and make a request to /api/tracks?props=bytes,composer,milliseconds the server will response in about 30 seconds which is not acceptable. The reason behind this is that the algorithm will try to process 3500 JToken items one by one and will end up to remove 21000 nested JTokens.
multi-client-api-04
One the other hand a request to /api/tracks?props=bytes,composer,milliseconds&pagesize=500 will only last 1-2 seconds maximum as it would take in normal circumstances. So keep in mind to always use pagination with this technique. It’s important to apply the pagination before you pass the original JToken to the FilterProperties method. Since we are talking about perfonmance I would like here to make a proposal. The algorithm and the FilterProperties method can be optimized and support many other features as well. I have tested it against the Chinook database and some others and it seemed to work fine but it can be done even better. If anyone wants to contribute and improve the algorithm be my guest. In order to help you with this I have created some Views in the application which display the responses for various API calls with a pretty and scrollable enabled JSON format. You can also click the link on each panel header to open the request on a separate tab.
multi-client-api-05
The API calls are made through AngularJS controllers. Let’s see part of the tracksCtrl controller.

(function (app) {
    'use strict';

    app.controller('tracksCtrl', tracksCtrl);

    tracksCtrl.$inject = ['$scope', '$http'];

    function tracksCtrl($scope, $http) {
        // api/tracks
        $http.get('api/tracks/').
          success(function (data, status, headers, config) {
              $scope.apiTracks = data;
          }).
          error(function (data, status, headers, config) {
              console.log(data);
          });

        // api/tracks?page=2&pagesize=100
        $http.get('api/tracks?page=2&pagesize=100').
          success(function (data, status, headers, config) {
              $scope.apiTracksPaged = data;
          }).
          error(function (data, status, headers, config) {
              console.log(data);
          });
	// code omitted

The data are bound to the view through some angularJS directives as follow:

 <div class="col-md-6">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title"><a href="api/tracks" target="_blank">api/tracks</a></h3>
                </div>

                <div class="panel-body">
                    <img src="~/images/spinner.gif" class="spinner" ng-show="!apiTracks" />
                    <perfect-scrollbar ng-show="apiTracks" class="api-box" wheel-propagation="true" wheel-speed="10" min-scrollbar-length="20">
                        <pretty-json json='apiTracks' replace-key='replaceKey' replace-value='replaceValue' indent-key='4' indent-value='indentValue'></pretty-json>
                    </perfect-scrollbar>
                </div>
            </div>
        </div>

        <div class="col-md-6">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title"><a href="api/tracks?page=2&pagesize=100" target="_blank">api/tracks?page=2&pagesize=100</a></h3>
                </div>

                <div class="panel-body">
                    <img src="~/images/spinner.gif" class="spinner" ng-show="!apiTracksPaged" />
                    <perfect-scrollbar ng-show="apiTracksPaged" class="api-box" wheel-propagation="true" wheel-speed="10" min-scrollbar-length="20">
                        <pretty-json json='apiTracksPaged' replace-key='replaceKey' replace-value='replaceValue' indent-key='4' indent-value='indentValue'></pretty-json>
                    </perfect-scrollbar>
                </div>
            </div>
        </div>

So in case you want to improve the algorithm and the function (naming, regression, performance etc..) make sure you pass all those tests first and then send me a pull request to examine. Hint: In production if the algorithm crashes Try/Catch block make sure to return the original JToken just in case. In development leave the function as it is in order to understand what went wrong and solve the problem. When you download the project from GitHub make sure to run the following command to download Bower dependencies.

bower install

Does it worth it? Absolutely. Decreasing the amount of data passing through the wire and reducing CPU and memory usage on the clients is the #1 TOP Priority when designing APIs. We managed to keep a single API giving the ability to the clients to request only the parts of the data that really needs to process. Maintainability and testability remained at the same levels as if you were designing the API for a single client.


That’s it we have finally finished! I hope you have enjoyed the post as much as I did creating it. You can download the project we built from my GitHub account as always. The project was built on Visual Studio 2015 and ASP.NET Core but the parts that do all the magic work can be copy-pasted in any other versions you wish.
multi-client-api-10

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small


BrowseDotNET: Manage your .NET Solutions and code snippets

$
0
0

BrowseDotNET is an open source ASP.NET MVC Web Application I created, that can help you manage your computer’s .NET solutions and code samples (programming language indepented) in one place. What does it solve? It’s common sense for developers to have dosens or hundrends of solutions projects in their personal computers with each of them providing a single solution to a specific problem. The thing is, that when the time comes to solve the same problem it’s quite difficult to find the respective solution among dosens and hence you are forced to seek over and over again the same solution on the internet. More over, you might have noticed that there are code snippets which you often use in your applications, but still you always have to get them from the internet.

BrowseDotNET features

BrowseDotNET came to solve these two important problems by providing three main features:
browsedotnet-01
This post’s objective is to show how to host and use BrowseDotNET in your personal computer. First let’s see the technologies, frameworks and techniques used for this application.

  1. ASP.NET MVC 5
  2. Entity Framework Code First development
  3. Code First Migrations
  4. Generic Repository pattern
  5. Dependency Injection using Autofac framework
  6. ASP.NET Identity
  7. Automapper
  8. FluentValidation API

Installation

Download BrowseDotNET source code from my Github account. You will find link at the bottom of this post. Build the application to restore packages. Rebuild. Open BrowseDotNET.Data and BrowseDotNET.Web and change the connection strings in their configuration files to reflect your development environment. Next, Open Package Manager Console and enter the following command while having BrowseDotNET.Data as the default project.

update-database -verbose

browsedotnet-02
That’s all you need to start BrowseDotNET. Fire the application and you will reach the starting page.
browsedotnet-03
First thing you need to do is create an account. You won’t be able to register solutions and code snippets unless you are an authorized user.
browsedotnet-04

Register single .NET Solution

From the top menu, select Register/Solution and fill all the relative fields. You will be asked to give information for solution’s to be registered file path, name and description and of course search term keys. On the right side, you will always have some hints to help you register either a solution or a code snippet.
browsedotnet-05

Open, Edit or Remove a registered solution

By the time you register a .NET solution, you will be able to open it from BrowseDotNET. From the top menu, select Open/Solution and view all your registered solutions. Of course you can filter your solution by typing into the table’s textbox.
browsedotnet-06

Multiple .NET Solutions registration (scan)

We all have more than one solution in our pc so I decided to create a scanning process to automate registration for you. All you have to do is enter a root directory and then check it’s subdirectories you want to be scanned for .net solutions. BrowseDotNET will search in these folders for .sln files and register them automatically. It will use solution’s file name as solution name and description but you can always edit them later. To start, select from the top menu Register/Scan Solutions and enter a valid root directory path.
browsedotnet-07
If the directory path is valid, you will be redirected to check child folders you want to be scanned.
browsedotnet-08
BrowseDotNET will do it’s magic and when finished it will show you registered solutions page.
browsedotnet-09

Code snippet registration

Select Register/Code Snippet from the top menu and fill the relative code snippet’s information. There are two tabs, one for snippet’s information and another to paste the code. You can paste code of any language you wish. One thing I want to point is the field Group. Use the same group name to group many code snippets together. For example you can register a C# code snippet and a jQuery code snippet under the same group. Later, you will be able to view them together by selecting this group. This is why I have used an autocomplete textbox for this field. It will help you find group names you have used before.
browsedotnet-10
As soon as you register the snippet you will be able to view it whenever you want. View all of your snippets by selecting Open/Snippets from the top menu.
browsedotnet-11
On the left menu you can select settings and set the available Solution Types for your solutions and the available programming languages for you Code Snippets.
browsedotnet-12
Type key terms on the top menu and search .NET Solutions or Code snippets by keys. You will be able to open directly the matched solutions and view the source of your snippets. You can type multiple key terms by comma separating them.
browsedotnet-13
Keys are really important to the search engine so try to be carefull when registering solutions or code snippets. For example if you register a .NET solution with a key mvc and use the same key for registering a code snippet, searching with that term will fetch you both results. Instead, if you use the key term mvc snippet for the snippet registration, BrowseDotNET would fetch again both results but the inner search engine will use two different key records from the database while only one record used on the first case.
browsedotnet-17
As soon as you start registering and opening .NET solutions from BrowseDotNET you will find the most recently used solutions on the first page.
browsedotnet-14

Hosting

You could host BrowseDotNET in your local IIS but unfortunately you cannot start a GUI application from IIS (unless you make serious configurations). What you can do is create a simple .bat file with the following command.

"c:\Program Files\IIS Express\iisexpress.exe" /path:"C:\Path_To_BrowseDotNET\BrowseDotNet.Web" /port:20185

The above command will host BrowseDotNET in iisexpress without running Visual Studio.

Source Code

There is a lot of source code to study in BrowseDotNET and I totally recommend you read ASP.NET MVC Solution Architecture – Best Practices and Web API powerful Custom Model Validation with FluentValidation in case you aren’t familiar with those concepts. FluentValidation is used particularly for validating ViewModels. Let’s see the example for validating the FilePath in SolutiongViewModel, the model which is posted back to server when the user registers a solution.

public class SolutionViewModel : IValidatableObject
    {
        public int ID { get; set; }

        [MaxLength(50)]
        public string Author { get; set; }

        [Required]
        [MaxLength(100)]
        [Display(Name = "Application")]
        public string Name { get; set; }

        [Required]
        [MaxLength(250)]
        [Display(Name = "Full Path (.sln)")]
        [RegularExpression(@"^(?:[\w]\:|\\)(\\[a-zA-Z_\-\s0-9\.]+)+\.(sln)$", ErrorMessage = "Invalid file path format.")]
        public string FilePath { get; set; }

        [Required]
        [MaxLength(300)]
        [DataType(DataType.MultilineText)]
        public string Description { get; set; }

        [MaxLength(200)]
        [DataType(DataType.Url)]
        public string Website { get; set; }

        [Required]
        [Display(Name = "Type")]
        public int SolutionTypeID { get; set; }

        [Display(Name = "Type")]
        public string SolutionTypeType { get; set; }

        [Required]
        [MaxLength(200)]
        [DataType(DataType.MultilineText)]
        public string Keys { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new SolutionViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }

And it’s Validator..

public class SolutionViewModelValidator : AbstractValidator<SolutionViewModel>
    {
        public SolutionViewModelValidator()
        {
            RuleFor(s => s.FilePath)
                .Must(IsValidPath)
                .WithMessage("Enter solution's full file path.");
        }

        private bool IsValidPath(string filePath)
        {
            bool _isValidSolutionPath = new bool();

            try
            {

                if (File.Exists(filePath) && Path.GetExtension(filePath).EndsWith(".sln"))
                    _isValidSolutionPath = true;
            }
            catch
            {
                _isValidSolutionPath = false;
            }

            return _isValidSolutionPath;
        }
    }

Data annotations used for SolutionViewModel in conjuction with jQuery Validation will prevent the user post a string that doesn’t represent a file path ending in .sln.
browsedotnet-15
But even if he does enter a file path that ends with .sln, the ModelState.IsValid will return false if the path posted doesn’t exist in user’s pc. That’s the magic of FluentValidation.
browsedotnet-16

Conclusion

BrowseDotNET is certainly a powerfull tool for .NET developers. The more solutions you have on your pc and the more code snippets you want to easily access, the most you will benefit from BrowseDotNET. Download the source code from here. I’ m certain that you ‘ll enjoy working with it. Happy coding!

Update

Some folks asked me to add a Run as admin button cause they needed to open Visual Studio solutions with administrator rights, so I added that to.
browsedotnet-18


ASP.NET Web API Unit Testing

$
0
0

Unit testing can be beneficial to many aspects in software develepment, from the lowest level that is the source code to the highest level and the end user’s experience. Writing automated tests helps finding defects earlier in the development lifecycle process which leads to fewer late nights or weekend work (happier developers). Since defects are resolved before production, less defects reach end users (happier clients). It also increases reliability of source code, since if the base code doesn’t change all tests should always return the same results. Last but not least, anyone that decides to write unit tests is also forced to write testable code which leads to better software development practices.
web-api-unit-testing

Web API Unit Testing

ASP.NET Web API stack has many aspects that firstly must be well understood before writing unit tests against it and that’s what makes it difficult. This post is a full stack Web API Unit testing tutorial which means will show you how to unit test all the layers and components exist in your Web API application. Let’s see what we are gonna see on this post:

I will break the post in two main sections. The first one will be the one where we ‘re gonna structure the application and the second one will be the actual Unit testing. For the first one I will follow the Generic repository pattern which I have already describe in this post. If you feel familiar with those concepts and you just want to read about how the unit testing is done, you can skip this step. Mind though that part of this section will be the Controller registration of a referenced library which has an important role in our Unit testing.

Section One: Structuring the Web API Application

Create a new blank solution named UnitTestingWebAPI and add the following projects:

  1. UnitTestingWebAPI.Domain: Class library (Contains Entity Models)
  2. UnitTestingWebAPI.Data: Class library (Contains Repositories)
  3. UnitTestingWebAPI.Services: Class library (Contains Services)
  4. UnitTestingWebAPI.API.Core: Class library (Contains Web API components such as Controllers, Filters, Message Handlers)
  5. UnitTestingWebAPI.API: Empty ASP.NET Web Application (Web application to host Web API)
  6. UnitTestingWebAPI.Tests: Class library (Contains the Unit Tests)

Switch to UnitTestingWebAPI.Domain and add the following classes:

public class Article
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Contents { get; set; }
        public string Author { get; set; }
        public string URL { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateEdited { get; set; }

        public int BlogID { get; set; }
        public virtual Blog Blog { get; set; }

        public Article()
        {
        }
    }
public class Blog
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string URL { get; set; }
        public string Owner { get; set; }
        public DateTime DateCreated { get; set; }
        public virtual ICollection<Article> Articles { get; set; }

        public Blog()
        {
            Articles = new HashSet<Article>();
        }
    }

Repository Layer

Switch to UnitTestingWebAPI.Data, install Entity Framework from Nuget packages, add a reference to UnitTestingWebAPI.Data and add the following classes (create the respective folder if required):

public class ArticleConfiguration : EntityTypeConfiguration<Article>
    {
        public ArticleConfiguration()
        {
            ToTable("Article");
            Property(a => a.Title).IsRequired().HasMaxLength(100);
            Property(a => a.Contents).IsRequired();
            Property(a => a.Author).IsRequired().HasMaxLength(50);
            Property(a => a.URL).IsRequired().HasMaxLength(200);
            Property(a => a.DateCreated).HasColumnType("datetime2");
            Property(a => a.DateEdited).HasColumnType("datetime2");
        }
    }
public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            ToTable("Blog");
            Property(b => b.Name).IsRequired().HasMaxLength(100);
            Property(b => b.URL).IsRequired().HasMaxLength(200);
            Property(b => b.Owner).IsRequired().HasMaxLength(50);
            Property(b => b.DateCreated).HasColumnType("datetime2");
        }
    }
public class BloggerEntities : DbContext
    {
        public BloggerEntities()
            : base("BloggerEntities")
        {
            Configuration.ProxyCreationEnabled = false;
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Article> Articles { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }
public class BloggerInitializer : DropCreateDatabaseIfModelChanges<BloggerEntities>
    {
        protected override void Seed(BloggerEntities context)
        {
            GetBlogs().ForEach(b => context.Blogs.Add(b));

            context.Commit();
        }

        public static List<Blog> GetBlogs()
        {
            List<Blog> _blogs = new List<Blog>();

            // Add two Blogs
            Blog _chsakellsBlog = new Blog()
            {
                Name = "chsakell's Blog",
                URL = "https://chsakell.com/",
                Owner = "Chris Sakellarios",
                Articles = GetChsakellsArticles()
            };

            Blog _dotNetCodeGeeks = new Blog()
            {
                Name = "DotNETCodeGeeks",
                URL = "dotnetcodegeeks",
                Owner = ".NET Code Geeks",
                Articles = GetDotNETGeeksArticles()
            };

            _blogs.Add(_chsakellsBlog);
            _blogs.Add(_dotNetCodeGeeks);

            return _blogs;
        }

        public static List<Article> GetChsakellsArticles()
        {
            List<Article> _articles = new List<Article>();

            Article _oData = new Article()
            {
                Author = "Chris S.",
                Title = "ASP.NET Web API feat. OData",
                URL = "https://chsakell.com/2015/04/04/asp-net-web-api-feat-odata/",
                Contents = @"OData is an open standard protocol allowing the creation and consumption of queryable
                            and interoperable RESTful APIs. It was initiated by Microsoft and it’s mostly known to
                            .NET Developers from WCF Data Services. There are many other server platforms supporting
                            OData services such as Node.js, PHP, Java and SQL Server Reporting Services. More over,
                            Web API also supports OData and this post will show you how to integrate those two.."
            };

            Article _wcfCustomSecurity= new Article()
            {
                Author = "Chris S.",
                Title = "Secure WCF Services with custom encrypted tokens",
                URL = "https://chsakell.com/2014/12/13/secure-wcf-services-with-custom-encrypted-tokens/",
                Contents = @"Windows Communication Foundation framework comes with a lot of options out of the box,
                            concerning the security logic you will apply to your services. Different bindings can be
                            used for certain kind and levels of security. Even the BasicHttpBinding binding supports
                            some types of security. There are some times though where you cannot or don’t want to use
                            WCF security available options and hence, you need to develop your own authentication logic
                            accoarding to your business needs."
            };

            _articles.Add(_oData);
            _articles.Add(_wcfCustomSecurity);

            return _articles;
        }

        public static List<Article> GetDotNETGeeksArticles()
        {
            List<Article> _articles = new List<Article>();

            Article _angularFeatWebAPI = new Article()
            {
                Author = "Gordon Beeming",
                Title = "AngularJS feat. Web API",
                URL = "http://www.dotnetcodegeeks.com/2015/05/angularjs-feat-web-api.html",
                Contents = @"Developing Web applications using AngularJS and Web API can be quite amuzing. You can pick
                            this architecture in case you have in mind a web application with limitted page refreshes or
                            post backs to the server while each application’s View is based on partial data retrieved from it."
            };

            _articles.Add(_angularFeatWebAPI);

            return _articles;
        }

        public static List<Article> GetAllArticles()
        {
            List<Article> _articles = new List<Article>();
            _articles.AddRange(GetChsakellsArticles());
            _articles.AddRange(GetDotNETGeeksArticles());

            return _articles;
        }
    }
public class Disposable : IDisposable
    {
        private bool isDisposed;

        ~Disposable()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if (!isDisposed && disposing)
            {
                DisposeCore();
            }

            isDisposed = true;
        }

        // Ovveride this to dispose custom objects
        protected virtual void DisposeCore()
        {
        }
    }
public interface IDbFactory : IDisposable
    {
        BloggerEntities Init();
    }
public class DbFactory : Disposable, IDbFactory
    {
        BloggerEntities dbContext;

        public BloggerEntities Init()
        {
            return dbContext ?? (dbContext = new BloggerEntities());
        }

        protected override void DisposeCore()
        {
            if (dbContext != null)
                dbContext.Dispose();
        }
    }
public interface IRepository<T> where T : class
    {
        // Marks an entity as new
        void Add(T entity);
        // Marks an entity as modified
        void Update(T entity);
        // Marks an entity to be removed
        void Delete(T entity);
        void Delete(Expression<Func<T, bool>> where);
        // Get an entity by int id
        T GetById(int id);
        // Get an entity using delegate
        T Get(Expression<Func<T, bool>> where);
        // Gets all entities of type T
        IEnumerable<T> GetAll();
        // Gets entities using delegate
        IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
    }
public abstract class RepositoryBase<T> where T : class
    {
        #region Properties
        private BloggerEntities dataContext;
        private readonly IDbSet<T> dbSet;

        protected IDbFactory DbFactory
        {
            get;
            private set;
        }

        protected BloggerEntities DbContext
        {
            get { return dataContext ?? (dataContext = DbFactory.Init()); }
        }
        #endregion

        protected RepositoryBase(IDbFactory dbFactory)
        {
            DbFactory = dbFactory;
            dbSet = DbContext.Set<T>();
        }

        #region Implementation
        public virtual void Add(T entity)
        {
            dbSet.Add(entity);
        }

        public virtual void Update(T entity)
        {
            dbSet.Attach(entity);
            dataContext.Entry(entity).State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            dbSet.Remove(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = dbSet.Where<T>(where).AsEnumerable();
            foreach (T obj in objects)
                dbSet.Remove(obj);
        }

        public virtual T GetById(int id)
        {
            return dbSet.Find(id);
        }

        public virtual IEnumerable<T> GetAll()
        {
            return dbSet.ToList();
        }

        public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
        {
            return dbSet.Where(where).ToList();
        }

        public T Get(Expression<Func<T, bool>> where)
        {
            return dbSet.Where(where).FirstOrDefault<T>();
        }

        #endregion

    }
public interface IUnitOfWork
    {
        void Commit();
    }
public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbFactory dbFactory;
        private BloggerEntities dbContext;

        public UnitOfWork(IDbFactory dbFactory)
        {
            this.dbFactory = dbFactory;
        }

        public BloggerEntities DbContext
        {
            get { return dbContext ?? (dbContext = dbFactory.Init()); }
        }

        public void Commit()
        {
            DbContext.Commit();
        }
    }
public class ArticleRepository : RepositoryBase<Article>, IArticleRepository
    {
        public ArticleRepository(IDbFactory dbFactory)
            : base(dbFactory) { }

        public Article GetArticleByTitle(string articleTitle)
        {
            var _article = this.DbContext.Articles.Where(b => b.Title == articleTitle).FirstOrDefault();

            return _article;
        }
    }

    public interface IArticleRepository : IRepository<Article>
    {
        Article GetArticleByTitle(string articleTitle);
    }
public class BlogRepository : RepositoryBase<Blog>, IBlogRepository
    {
        public BlogRepository(IDbFactory dbFactory)
            : base(dbFactory) { }

        public Blog GetBlogByName(string blogName)
        {
            var _blog = this.DbContext.Blogs.Where(b => b.Name == blogName).FirstOrDefault();

            return _blog;
        }
    }

    public interface IBlogRepository : IRepository<Blog>
    {
        Blog GetBlogByName(string blogName);
    }

Service layer

Switch to UnitTestingWebAPI.Service project, add references to UnitTestingWebAPI.Domain,UnitTestingWebAPI.Data and add the following two files:

// operations you want to expose
    public interface IArticleService
    {
        IEnumerable<Article> GetArticles(string name = null);
        Article GetArticle(int id);
        Article GetArticle(string name);
        void CreateArticle(Article article);
        void UpdateArticle(Article article);
        void DeleteArticle(Article article);
        void SaveArticle();
    }

    public class ArticleService : IArticleService
    {
        private readonly IArticleRepository articlesRepository;
        private readonly IUnitOfWork unitOfWork;

        public ArticleService(IArticleRepository articlesRepository, IUnitOfWork unitOfWork)
        {
            this.articlesRepository = articlesRepository;
            this.unitOfWork = unitOfWork;
        }

        #region IArticleService Members

        public IEnumerable<Article> GetArticles(string title = null)
        {
            if (string.IsNullOrEmpty(title))
                return articlesRepository.GetAll();
            else
                return articlesRepository.GetAll().Where(c => c.Title.ToLower().Contains(title.ToLower()));
        }

        public Article GetArticle(int id)
        {
            var article = articlesRepository.GetById(id);
            return article;
        }

        public Article GetArticle(string title)
        {
            var article = articlesRepository.GetArticleByTitle(title);
            return article;
        }

        public void CreateArticle(Article article)
        {
            articlesRepository.Add(article);
        }

        public void UpdateArticle(Article article)
        {
            articlesRepository.Update(article);
        }

        public void DeleteArticle(Article article)
        {
            articlesRepository.Delete(article);
        }

        public void SaveArticle()
        {
            unitOfWork.Commit();
        }

        #endregion
    }
// operations you want to expose
    public interface IBlogService
    {
        IEnumerable<Blog> GetBlogs(string name = null);
        Blog GetBlog(int id);
        Blog GetBlog(string name);
        void CreateBlog(Blog blog);
        void UpdateBlog(Blog blog);
        void SaveBlog();
        void DeleteBlog(Blog blog);
    }

    public class BlogService : IBlogService
    {
        private readonly IBlogRepository blogsRepository;
        private readonly IUnitOfWork unitOfWork;

        public BlogService(IBlogRepository blogsRepository, IUnitOfWork unitOfWork)
        {
            this.blogsRepository = blogsRepository;
            this.unitOfWork = unitOfWork;
        }

        #region IBlogService Members

        public IEnumerable<Blog> GetBlogs(string name = null)
        {
            if (string.IsNullOrEmpty(name))
                return blogsRepository.GetAll();
            else
                return blogsRepository.GetAll().Where(c => c.Name == name);
        }

        public Blog GetBlog(int id)
        {
            var blog = blogsRepository.GetById(id);
            return blog;
        }

        public Blog GetBlog(string name)
        {
            var blog = blogsRepository.GetBlogByName(name);
            return blog;
        }

        public void CreateBlog(Blog blog)
        {
            blogsRepository.Add(blog);
        }

        public void UpdateBlog(Blog blog)
        {
            blogsRepository.Update(blog);
        }

        public void DeleteBlog(Blog blog)
        {
            blogsRepository.Delete(blog);
        }

        public void SaveBlog()
        {
            unitOfWork.Commit();
        }

        #endregion
    }

Web API Core Components

Switch to UnitTestingWebAPI.API.Core and add references to UnitTestingWebAPI.API.Domain and UnitTestingWebAPI.API.Service projects. Install the following packages from Nuget Packages:

  1. Entity Framework
  2. Microsoft.AspNet.WebApi.Core
  3. Microsoft.AspNet.WebApi.Client

Add the following Web API Controllers to a Controllers folder:

public class ArticlesController : ApiController
    {
        private IArticleService _articleService;

        public ArticlesController(IArticleService articleService)
        {
            _articleService = articleService;
        }

        // GET: api/Articles
        public IEnumerable<Article> GetArticles()
        {
            return _articleService.GetArticles();
        }

        // GET: api/Articles/5
        [ResponseType(typeof(Article))]
        public IHttpActionResult GetArticle(int id)
        {
            Article article = _articleService.GetArticle(id);
            if (article == null)
            {
                return NotFound();
            }

            return Ok(article);
        }

        // PUT: api/Articles/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutArticle(int id, Article article)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != article.ID)
            {
                return BadRequest();
            }

            _articleService.UpdateArticle(article);

            try
            {
                _articleService.SaveArticle();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ArticleExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Articles
        [ResponseType(typeof(Article))]
        public IHttpActionResult PostArticle(Article article)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            _articleService.CreateArticle(article);

            return CreatedAtRoute("DefaultApi", new { id = article.ID }, article);
        }

        // DELETE: api/Articles/5
        [ResponseType(typeof(Article))]
        public IHttpActionResult DeleteArticle(int id)
        {
            Article article = _articleService.GetArticle(id);
            if (article == null)
            {
                return NotFound();
            }

            _articleService.DeleteArticle(article);

            return Ok(article);
        }

        private bool ArticleExists(int id)
        {
            return _articleService.GetArticle(id) != null;
        }
    }
public class BlogsController : ApiController
    {
        private IBlogService _blogService;

        public BlogsController(IBlogService blogService)
        {
            _blogService = blogService;
        }

        // GET: api/Blogs
        public IEnumerable<Blog> GetBlogs()
        {
            return _blogService.GetBlogs();
        }

        // GET: api/Blogs/5
        [ResponseType(typeof(Blog))]
        public IHttpActionResult GetBlog(int id)
        {
            Blog blog = _blogService.GetBlog(id);
            if (blog == null)
            {
                return NotFound();
            }

            return Ok(blog);
        }

        // PUT: api/Blogs/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutBlog(int id, Blog blog)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != blog.ID)
            {
                return BadRequest();
            }

            _blogService.UpdateBlog(blog);

            try
            {
                _blogService.SaveBlog();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!BlogExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Blogs
        [ResponseType(typeof(Blog))]
        public IHttpActionResult PostBlog(Blog blog)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            _blogService.CreateBlog(blog);

            return CreatedAtRoute("DefaultApi", new { id = blog.ID }, blog);
        }

        // DELETE: api/Blogs/5
        [ResponseType(typeof(Blog))]
        public IHttpActionResult DeleteBlog(int id)
        {
            Blog blog = _blogService.GetBlog(id);
            if (blog == null)
            {
                return NotFound();
            }

            _blogService.DeleteBlog(blog);

            return Ok(blog);
        }

        private bool BlogExists(int id)
        {
            return _blogService.GetBlog(id) != null;
        }
    }

Add the following filter which when applied, it reverses the order of a List of articles:

public class ArticlesReversedFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            var objectContent = actionExecutedContext.Response.Content as ObjectContent;
            if (objectContent != null)
            {
                List<Article> _articles = objectContent.Value as List<Article>;
                if (_articles != null && _articles.Count > 0)
                {
                    _articles.Reverse();
                }
            }
        }
    }

Add the following MediaTypeFormatter which can return a comma serated representation of articles:

public class ArticleFormatter : BufferedMediaTypeFormatter
    {
        public ArticleFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/article"));
        }

        public override bool CanReadType(Type type)
        {
            return false;
        }

        public override bool CanWriteType(Type type)
        {
            //for single article object
            if (type == typeof(Article))
                return true;
            else
            {
                // for multiple article objects
                Type _type = typeof(IEnumerable<Article>);
                return _type.IsAssignableFrom(type);
            }
        }

        public override void WriteToStream(Type type,
                                           object value,
                                           Stream writeStream,
                                           HttpContent content)
        {
            using (StreamWriter writer = new StreamWriter(writeStream))
            {
                var articles = value as IEnumerable<Article>;
                if (articles != null)
                {
                    foreach (var article in articles)
                    {
                        writer.Write(String.Format("[{0},\"{1}\",\"{2}\",\"{3}\",\"{4}\"]",
                                                    article.ID,
                                                    article.Title,
                                                    article.Author,
                                                    article.URL,
                                                    article.Contents));
                    }
                }
                else
                {
                    var _article = value as Article;
                    if (_article == null)
                    {
                        throw new InvalidOperationException("Cannot serialize type");
                    }
                    writer.Write(String.Format("[{0},\"{1}\",\"{2}\",\"{3}\",\"{4}\"]",
                                                    _article.ID,
                                                    _article.Title,
                                                    _article.Author,
                                                    _article.URL,
                                                    _article.Contents));
                }
            }
        }
    }

Add the following two Message Handlers. The first one is responsible to add a custom header in the response and the second one is able to terminate the request if applied:

public class HeaderAppenderHandler : DelegatingHandler
    {
        async protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

            response.Headers.Add("X-WebAPI-Header", "Web API Unit testing in chsakell's blog.");
            return response;
        }
    }
public class EndRequestHandler : DelegatingHandler
    {
        async protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request.RequestUri.AbsoluteUri.Contains("test"))
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent("Unit testing message handlers!")
                };

                var tsc = new TaskCompletionSource<HttpResponseMessage>();
                tsc.SetResult(response);
                return await tsc.Task;
            }
            else
            {
                return await base.SendAsync(request, cancellationToken);
            }
        }
    }


Add the following DefaultAssembliesResolver which will be used for Controller registration from the Web Application project:

public class CustomAssembliesResolver : DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            var baseAssemblies = base.GetAssemblies().ToList();
            var assemblies = new List<Assembly>(baseAssemblies) { typeof(BlogsController).Assembly };
            baseAssemblies.AddRange(assemblies);

            return baseAssemblies.Distinct().ToList();
        }
    }

ASP.NET Web Application

Switch to UnitTestingWebAPI.API web application project and add references to UnitTestingWebAPI.Core,UnitTestingWebAPI.Data and UnitTestingWebAPI.Service. You will also need to install the following Nuget packages:

  1. Entity Framework
  2. Microsoft.AspNet.WebApi.WebHost
  3. Microsoft.AspNet.WebApi.Core
  4. Microsoft.AspNet.WebApi.Client
  5. Microsoft.AspNet.WebApi.Owin
  6. Microsoft.Owin.Host.SystemWeb
  7. Microsoft.Owin
  8. Autofac.WebApi2

Add a Global Configuration file if not exists and set the database initializer:

public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            // Init database
            System.Data.Entity.Database.SetInitializer(new BloggerInitializer());
        }
    }

Also make sure you add a relevant connection string in the Web.config file:

  <connectionStrings>
    <add name="BloggerEntities" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=BloggerDB;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

External Controller Registration

Create an Owin Startup.cs file at the root of the Web application and paste the following code. This code will ensure to use WebApi controllers from the UnitTestingWebAPI.API.Core project (CustomAssembliesResolver) and inject the appropriate repositories and services when required (autofac configuration):

public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            var config = new HttpConfiguration();
            config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
            config.Formatters.Add(new ArticleFormatter());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );


            // Autofac configuration
            var builder = new ContainerBuilder();
            builder.RegisterApiControllers(typeof(BlogsController).Assembly);
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
            builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerRequest();

            //Repositories
            builder.RegisterAssemblyTypes(typeof(BlogRepository).Assembly)
                .Where(t => t.Name.EndsWith("Repository"))
                .AsImplementedInterfaces().InstancePerRequest();
            // Services
            builder.RegisterAssemblyTypes(typeof(ArticleService).Assembly)
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces().InstancePerRequest();

            IContainer container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            appBuilder.UseWebApi(config);
        }
    }

At this point you should be able to fire the Web application and request articles or blogs using the following requests (port may be different in yours):

http://localhost:56032/api/articles
http://localhost:56032/api/blogs

Section Two: Unit Testing

We have completed structuring our application and it’s time to unit test all of our components. Switch to UnitTestingWebAPI.Tests class library and add references to UnitTestingWebAPI.Domain, UnitTestingWebAPI.Data,UnitTestingWebAPI.Service and UnitTestingWebAPI.API.Core. Also make sure you install the following Nuget Packages:

  1. Entity Framework
  2. Microsoft.AspNet.WebApi.Core
  3. Microsoft.AspNet.WebApi.Client
  4. Microsoft.AspNet.WebApi.Owin
  5. Microsoft.AspNet.WebApi.SelfHost
  6. Micoroft.Owin
  7. Owin
  8. Micoroft.Owin.Hosting
  9. Micoroft.Owin.Host.HttpListener
  10. Autofac.WebApi2
  11. NUnit
  12. NUnitTestAdapter

As you see we are going to use NUnit to write our unit tests.

Services Unit Testing

When writing Unit tests, first you need to setup or initiate some variables to be used for the unit tests. With NUnit this is done via a function with an attribute Setup applied on it. This very function will run before any NUnit test is executed. Unit testing the Service layer is the first thing you need to do since all the Controller’s constructors are injected with Services. Hence, you need to emulate repositories and service behavior before starting unit testing WebAPI Core components. In this example we ‘ll see how to emulate the ArticleSevice. This service’s constructor is injected with instances of IArticleRepository and IUnitOfWork so all we have to do is create two “special” instances and inject them.

public ArticleService(IArticleRepository articlesRepository, IUnitOfWork unitOfWork)
        {
            this.articlesRepository = articlesRepository;
            this.unitOfWork = unitOfWork;
        }

I said “special” cause those instance are not going to be real instances that actually can access the database.

Attention

Unit tests must run in memory and shouldn’t access databases. All core functionality must be emulated by using frameworks such as Mock in our case. This way automated tests will be much more faster. The basic purpose of unit tests is more testing component behavior rather than testing real results.

Let’s procceed with testing the ArticleService. Create a file named ServiceTests and for start add the following code:

    [TestFixture]
    public class ServicesTests
    {
        #region Variables
        IArticleService _articleService;
        IArticleRepository _articleRepository;
        IUnitOfWork _unitOfWork;
        List<Article> _randomArticles;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _randomArticles = SetupArticles();

            _articleRepository = SetupArticleRepository();
            _unitOfWork = new Mock<IUnitOfWork>().Object;
            _articleService = new ArticleService(_articleRepository, _unitOfWork);
        }

        public List<Article> SetupArticles()
        {
            int _counter = new int();
            List<Article> _articles = BloggerInitializer.GetAllArticles();

            foreach (Article _article in _articles)
                _article.ID = ++_counter;

            return _articles;
        }

        public IArticleRepository SetupArticleRepository()
        {
            // Init repository
            var repo = new Mock<IArticleRepository>();

            // Setup mocking behavior
            repo.Setup(r => r.GetAll()).Returns(_randomArticles);

            repo.Setup(r => r.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Article>(
                    id => _randomArticles.Find(a => a.ID.Equals(id))));

            repo.Setup(r => r.Add(It.IsAny<Article>()))
                .Callback(new Action<Article>(newArticle =>
                {
                    dynamic maxArticleID = _randomArticles.Last().ID;
                    dynamic nextArticleID = maxArticleID + 1;
                    newArticle.ID = nextArticleID;
                    newArticle.DateCreated = DateTime.Now;
                    _randomArticles.Add(newArticle);
                }));

            repo.Setup(r => r.Update(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                    {
                        var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
                        oldArticle.DateEdited = DateTime.Now;
                        oldArticle = x;
                    }));

            repo.Setup(r => r.Delete(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var _articleToRemove = _randomArticles.Find(a => a.ID == x.ID);

                    if (_articleToRemove != null)
                        _randomArticles.Remove(_articleToRemove);
                }));

            // Return mock implementation
            return repo.Object;
        }

        #endregion
   }
}

In the SetupArticleRepository() function we emulate our _articleRepository behavior, in other words we setup what results are expected from this repository instance when a specific function is called. Then we inject this instance in our _articleService’s constructor and we are ready to go. Let’s say that we want to test that the _articleService.GetArticles() behaves as expected. Add the following NUnit test in the same file:

        [Test]
        public void ServiceShouldReturnAllArticles()
        {
            var articles = _articleService.GetArticles();

            Assert.That(articles, Is.EqualTo(_randomArticles));
        }

Build the Tests project, run the test and make sure it passes. In the same way create the following tests:

        [Test]
        public void ServiceShouldReturnRightArticle()
        {
            var wcfSecurityArticle = _articleService.GetArticle(2);

            Assert.That(wcfSecurityArticle,
                Is.EqualTo(_randomArticles.Find(a => a.Title.Contains("Secure WCF Services"))));
        }

        [Test]
        public void ServiceShouldAddNewArticle()
        {
            var _newArticle = new Article()
            {
                Author = "Chris Sakellarios",
                Contents = "If you are an ASP.NET MVC developer, you will certainly..",
                Title = "URL Rooting in ASP.NET (Web Forms)",
                URL = "https://chsakell.com/2013/12/15/url-rooting-in-asp-net-web-forms/"
            };

            int _maxArticleIDBeforeAdd = _randomArticles.Max(a => a.ID);
            _articleService.CreateArticle(_newArticle);

            Assert.That(_newArticle, Is.EqualTo(_randomArticles.Last()));
            Assert.That(_maxArticleIDBeforeAdd + 1, Is.EqualTo(_randomArticles.Last().ID));
        }

        [Test]
        public void ServiceShouldUpdateArticle()
        {
            var _firstArticle = _randomArticles.First();

            _firstArticle.Title = "OData feat. ASP.NET Web API"; // reversed:-)
            _firstArticle.URL = "http://t.co/fuIbNoc7Zh"; // short link
            _articleService.UpdateArticle(_firstArticle);

            Assert.That(_firstArticle.DateEdited, Is.Not.EqualTo(DateTime.MinValue));
            Assert.That(_firstArticle.URL, Is.EqualTo("http://t.co/fuIbNoc7Zh"));
            Assert.That(_firstArticle.ID, Is.EqualTo(1)); // hasn't changed
        }

        [Test]
        public void ServiceShouldDeleteArticle()
        {
            int maxID = _randomArticles.Max(a => a.ID); // Before removal
            var _lastArticle = _randomArticles.Last();

            // Remove last article
            _articleService.DeleteArticle(_lastArticle);

            Assert.That(maxID, Is.GreaterThan(_randomArticles.Max(a => a.ID))); // Max reduced by 1
        }

Web API Controllers Unit Testing

Now that we are familiar with emulating our services behavior we can procceed with unit testing Web API Controllers. First thing we need to do is Setup the variables to be used through our test, so create a ControllerTests.cs file and paste the following code:

    [TestFixture]
    public class ControllerTests
    {
        #region Variables
        IArticleService _articleService;
        IArticleRepository _articleRepository;
        IUnitOfWork _unitOfWork;
        List<Article> _randomArticles;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _randomArticles = SetupArticles();

            _articleRepository = SetupArticleRepository();
            _unitOfWork = new Mock<IUnitOfWork>().Object;
            _articleService = new ArticleService(_articleRepository, _unitOfWork);
        }

        public List<Article> SetupArticles()
        {
            int _counter = new int();
            List<Article> _articles = BloggerInitializer.GetAllArticles();

            foreach (Article _article in _articles)
                _article.ID = ++_counter;

            return _articles;
        }

        public IArticleRepository SetupArticleRepository()
        {
            // Init repository
            var repo = new Mock<IArticleRepository>();

            // Setup mocking behavior
            repo.Setup(r => r.GetAll()).Returns(_randomArticles);

            repo.Setup(r => r.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Article>(
                    id => _randomArticles.Find(a => a.ID.Equals(id))));

            repo.Setup(r => r.Add(It.IsAny<Article>()))
                .Callback(new Action<Article>(newArticle =>
                {
                    dynamic maxArticleID = _randomArticles.Last().ID;
                    dynamic nextArticleID = maxArticleID + 1;
                    newArticle.ID = nextArticleID;
                    newArticle.DateCreated = DateTime.Now;
                    _randomArticles.Add(newArticle);
                }));

            repo.Setup(r => r.Update(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
                    oldArticle.DateEdited = DateTime.Now;
                    oldArticle.URL = x.URL;
                    oldArticle.Title = x.Title;
                    oldArticle.Contents = x.Contents;
                    oldArticle.BlogID = x.BlogID;
                }));

            repo.Setup(r => r.Delete(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var _articleToRemove = _randomArticles.Find(a => a.ID == x.ID);

                    if (_articleToRemove != null)
                        _randomArticles.Remove(_articleToRemove);
                }));

            // Return mock implementation
            return repo.Object;
        }

        #endregion
   }
}

WebAPI Controller classes are classes just like all others so we can test them respectively. Let’s see if the _articlesController.GetArticles() does return all articles available:

        [Test]
        public void ControlerShouldReturnAllArticles()
        {
            var _articlesController = new ArticlesController(_articleService);

            var result = _articlesController.GetArticles();

            CollectionAssert.AreEqual(result, _randomArticles);
        }

The most important line here is the highlighted one where the _articleService instance injection will ensure the service’s behavior.
In the same way we ensure that the last article is returned when invoking _articlesController.GetArticle(3) since we setup only 3 articles.

        [Test]
        public void ControlerShouldReturnLastArticle()
        {
            var _articlesController = new ArticlesController(_articleService);

            var result = _articlesController.GetArticle(3) as OkNegotiatedContentResult<Article>;

            Assert.IsNotNull(result);
            Assert.AreEqual(result.Content.Title, _randomArticles.Last().Title);
        }

Let’s test that an invalid Update operation must fail and return a BadRequestResult. Recall the Update operation setup on the _articleRepository:

repo.Setup(r => r.Update(It.IsAny<Article>()))
                .Callback(new Action<Article>(x =>
                {
                    var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
                    oldArticle.DateEdited = DateTime.Now;
                    oldArticle.URL = x.URL;
                    oldArticle.Title = x.Title;
                    oldArticle.Contents = x.Contents;
                    oldArticle.BlogID = x.BlogID;
                }));

So If we pass an non existing article this update should fail:

        [Test]
        public void ControlerShouldPutReturnBadRequestResult()
        {
            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Put,
                    RequestUri = new Uri("http://localhost/api/articles/-1")
                }
            };

            var badresult = _articlesController.PutArticle(-1, new Article() { Title = "Unknown Article" });
            Assert.That(badresult, Is.TypeOf<BadRequestResult>());
        }

Complete the Controller Unit testing by adding the following three tests which tests that updating first article succeeds, post new article succeeds and post new article fails respectivelly.

        [Test]
        public void ControlerShouldPutUpdateFirstArticle()
        {
            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Put,
                    RequestUri = new Uri("http://localhost/api/articles/1")
                }
            };

            IHttpActionResult updateResult = _articlesController.PutArticle(1, new Article()
            {
                ID = 1,
                Title = "ASP.NET Web API feat. OData",
                URL = "http://t.co/fuIbNoc7Zh",
                Contents = @"OData is an open standard protocol.."
            }) as IHttpActionResult;

            Assert.That(updateResult, Is.TypeOf<StatusCodeResult>());

            StatusCodeResult statusCodeResult = updateResult as StatusCodeResult;

            Assert.That(statusCodeResult.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));

            Assert.That(_randomArticles.First().URL, Is.EqualTo("http://t.co/fuIbNoc7Zh"));
        }

        [Test]
        public void ControlerShouldPostNewArticle()
        {
            var article = new Article
            {
                Title = "Web API Unit Testing",
                URL = "https://chsakell.com/web-api-unit-testing",
                Author = "Chris Sakellarios",
                DateCreated = DateTime.Now,
                Contents = "Unit testing Web API.."
            };

            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Post,
                    RequestUri = new Uri("http://localhost/api/articles")
                }
            };

            _articlesController.Configuration.MapHttpAttributeRoutes();
            _articlesController.Configuration.EnsureInitialized();
            _articlesController.RequestContext.RouteData = new HttpRouteData(
            new HttpRoute(), new HttpRouteValueDictionary { { "_articlesController", "Articles" } });
            var result = _articlesController.PostArticle(article) as CreatedAtRouteNegotiatedContentResult<Article>;

            Assert.That(result.RouteName, Is.EqualTo("DefaultApi"));
            Assert.That(result.Content.ID, Is.EqualTo(result.RouteValues["id"]));
            Assert.That(result.Content.ID, Is.EqualTo(_randomArticles.Max(a => a.ID)));
        }

        [Test]
        public void ControlerShouldNotPostNewArticle()
        {
            var article = new Article
            {
                Title = "Web API Unit Testing",
                URL = "https://chsakell.com/web-api-unit-testing",
                Author = "Chris Sakellarios",
                DateCreated = DateTime.Now,
                Contents = null
            };

            var _articlesController = new ArticlesController(_articleService)
            {
                Configuration = new HttpConfiguration(),
                Request = new HttpRequestMessage
                {
                    Method = HttpMethod.Post,
                    RequestUri = new Uri("http://localhost/api/articles")
                }
            };

            _articlesController.Configuration.MapHttpAttributeRoutes();
            _articlesController.Configuration.EnsureInitialized();
            _articlesController.RequestContext.RouteData = new HttpRouteData(
            new HttpRoute(), new HttpRouteValueDictionary { { "Controller", "Articles" } });
            _articlesController.ModelState.AddModelError("Contents", "Contents is required field");

            var result = _articlesController.PostArticle(article) as InvalidModelStateResult;

            Assert.That(result.ModelState.Count, Is.EqualTo(1));
            Assert.That(result.ModelState.IsValid, Is.EqualTo(false));
        }

Take a good look the highlighted lines and see that we can unit test several aspects of our requests, such as CodeStatus returned or routing properties.

Message Handlers Unit Testing

You can test Message Handlers by creating an instance of HttpMessageInvoker, passing the Message Handler instance you want to test and invoke the SendAsync function. Create a MessageHandlerTests.cs file and paste the Setup code first:

    [TestFixture]
    public class MessageHandlerTests
    {
        #region Variables
        private EndRequestHandler _endRequestHandler;
        private HeaderAppenderHandler _headerAppenderHandler;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            // Direct MessageHandler test
            _endRequestHandler = new EndRequestHandler();
            _headerAppenderHandler = new HeaderAppenderHandler()
            {
                InnerHandler = _endRequestHandler
            };
        }
        #endregion
   }
}

We setup the HeaderAppenderHandler’s inner handler another Handler that will terminate the request. Recall that the EndRequestHandler will end the request only if the Uri contains a test literal. Let’s write the test now:

        [Test]
        public async void ShouldAppendCustomHeader()
        {
            var invoker = new HttpMessageInvoker(_headerAppenderHandler);
            var result = await invoker.SendAsync(new HttpRequestMessage(HttpMethod.Get,
                new Uri("http://localhost/api/test/")), CancellationToken.None);

            Assert.That(result.Headers.Contains("X-WebAPI-Header"), Is.True);
            Assert.That(result.Content.ReadAsStringAsync().Result,
                Is.EqualTo("Unit testing message handlers!"));
        }

Now let’s pick up tha pace a little bit and make things quite more interesting. Let’s say you want to make an integration test that is you want to test the actual behavior of your Message Handler when a request is dispatched to a controller’s action. This would require to host the Web API and then run the unit test but is this possible here? Of course it, and this is the beauty when you create a highly loosely coupled application. All you have to do is Self host the web api and setup the appropriate configurations. In our case we are gonna host the web api and also setup Moq instances to be injected for Repositories and Services. Add the following Startup.cs file in the UnitTestingWebAPI.Tests project:

public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            var config = new HttpConfiguration();
            config.MessageHandlers.Add(new HeaderAppenderHandler());
            config.MessageHandlers.Add(new EndRequestHandler());
            config.Filters.Add(new ArticlesReversedFilter());
            config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            config.MapHttpAttributeRoutes();

            // Autofac configuration
            var builder = new ContainerBuilder();
            builder.RegisterApiControllers(typeof(ArticlesController).Assembly);

            // Unit of Work
            var _unitOfWork = new Mock<IUnitOfWork>();
            builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();

            //Repositories
            var _articlesRepository = new Mock<IArticleRepository>();
            _articlesRepository.Setup(x => x.GetAll()).Returns(
                    BloggerInitializer.GetAllArticles()
                );
            builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();

            var _blogsRepository = new Mock<IBlogRepository>();
            _blogsRepository.Setup(x => x.GetAll()).Returns(
                BloggerInitializer.GetBlogs
                );
            builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();

            // Services
            builder.RegisterAssemblyTypes(typeof(ArticleService).Assembly)
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces().InstancePerRequest();

            builder.RegisterInstance(new ArticleService(_articlesRepository.Object, _unitOfWork.Object));
            builder.RegisterInstance(new BlogService(_blogsRepository.Object, _unitOfWork.Object));

            IContainer container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            appBuilder.UseWebApi(config);
        }
    }

Notice that it’s quite similar to the Startup class we wrote for the Web Application project, except that fake repositories and services are used. Now let’s return and write this integration test:

        [Test]
        public void ShouldCallToControllerActionAppendCustomHeader()
        {
            //Arrange
            var address = "http://localhost:9000/";

            using (WebApp.Start<Startup>(address))
            {
                HttpClient _client = new HttpClient();
                var response = _client.GetAsync(address + "api/articles").Result;

                Assert.That(response.Headers.Contains("X-WebAPI-Header"), Is.True);

                var _returnedArticles = response.Content.ReadAsAsync<List<Article>>().Result;
                Assert.That(_returnedArticles.Count, Is.EqualTo( BloggerInitializer.GetAllArticles().Count));
            }
        }

Since the request doesn’t contain a test literal, it will reach the controller’s action and also bring the results. Notice also that the custom header has also been appended.

Action Filters Unit Testing

Recall that we had created an ArticlesReversedFilter that when applied it reverses the order of the articles that should be returned. We can either direct unit test this filter or run an integration one. We will see how to do both of them. To direct test an action filter you need to run it’s OnActionExecuted function by passing a instance of HttpActionExecutedContext as a parameter as follow:

        [Test]
        public void ShouldSortArticlesByTitle()
        {
            var filter = new ArticlesReversedFilter();
            var executedContext = new HttpActionExecutedContext(new HttpActionContext
            {
                Response = new HttpResponseMessage(),
            }, null);

            executedContext.Response.Content = new ObjectContent<List<Article>>(new List<Article>(_articles), new JsonMediaTypeFormatter());

            filter.OnActionExecuted(executedContext);

            var _returnedArticles = executedContext.Response.Content.ReadAsAsync<List<Article>>().Result;

            Assert.That(_returnedArticles.First(), Is.EqualTo(_articles.Last()));
        }

To run an integration test you need to self host the Web API and make the appropriate request. Mind that the filter must be registered in the Startup configuration class.

        [Test]
        public void ShouldCallToControllerActionReverseArticles()
        {
            //Arrange
            var address = "http://localhost:9000/";

            using (WebApp.Start<Startup>(address))
            {
                HttpClient _client = new HttpClient();
                var response = _client.GetAsync(address + "api/articles").Result;

                var _returnedArticles = response.Content.ReadAsAsync<List<Article>>().Result;

                Assert.That(_returnedArticles.First().Title, Is.EqualTo(BloggerInitializer.GetAllArticles().Last().Title));
            }
        }

Media Type formatters Unit Testing

You have created some custom Media Type formatters and you want to test their behavior. Recall the ArticleFormatter we created in the UnitTestingWebAPI.API.Core project and it’s able to return a comma separated string representation of articles. It can only write Article instances, not read ones or understand other type of classes. You need to set the Accept request header to application/article in order to apply the formatter. Let’s see the Setup configuration of our tests:

    [TestFixture]
    public class MediaTypeFormatterTests
    {
        #region Variables
        Blog _blog;
        Article _article;
        ArticleFormatter _formatter;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _blog = BloggerInitializer.GetBlogs().First();
            _article = BloggerInitializer.GetChsakellsArticles().First();
            _formatter = new ArticleFormatter();
        }
        #endregion
   }
}

You can test a MediaTypeFormatter by creating an instance of ObjectContent, passing the object to check if can be formatted by the respective formatter, and the formatter itself. If the formatter cannot read or write the passed object an exception will be thrown, otherwise not. For example let’s ensure that the ArticleFormatter cannot understand Blog instances:

        [Test]
        public void FormatterShouldThrowExceptionWhenUnsupportedType()
        {
            Assert.Throws<InvalidOperationException>(() => new ObjectContent<Blog>(_blog, _formatter));
        }

On the other hand it must work fine with parsing Article objects:

        [Test]
        public void FormatterShouldNotThrowExceptionWhenArticle()
        {
            Assert.DoesNotThrow(() => new ObjectContent<Article>(_article, _formatter));
        }

And here are some other tests you can run against your custom Media type formatters:

[Test]
        public void FormatterShouldHeaderBeSetCorrectly()
        {
            var content = new ObjectContent<Article>(_article, new ArticleFormatter());

            Assert.That(content.Headers.ContentType.MediaType, Is.EqualTo("application/article"));
        }

        [Test]
        public async void FormatterShouldBeAbleToDeserializeArticle()
        {
            var content = new ObjectContent<Article>(_article, _formatter);

            var deserializedItem = await content.ReadAsAsync<Article>(new[] { _formatter });

            Assert.That(_article, Is.SameAs(deserializedItem));
        }

        [Test]
        public void FormatterShouldNotBeAbleToWriteUnsupportedType()
        {
            var canWriteBlog = _formatter.CanWriteType(typeof(Blog));
            Assert.That(canWriteBlog, Is.False);
        }

        [Test]
        public void FormatterShouldBeAbleToWriteArticle()
        {
            var canWriteArticle = _formatter.CanWriteType(typeof(Article));
            Assert.That(canWriteArticle, Is.True);
        }

Routing Unit Testing

You want to test your routing configuration without hosting Web API. For this you ‘ll need a helper class that is able to return the Controller type or the controller’s action from an instance of a HttpControllerContext. Before this you have to create an HttpConfiguration with your routing configuration setup in it. Let’s see first the helper class:

public class ControllerActionSelector
    {
        #region Variables
        HttpConfiguration config;
        HttpRequestMessage request;
        IHttpRouteData routeData;
        IHttpControllerSelector controllerSelector;
        HttpControllerContext controllerContext;
        #endregion

        #region Constructor
        public ControllerActionSelector(HttpConfiguration conf, HttpRequestMessage req)
        {
            config = conf;
            request = req;
            routeData = config.Routes.GetRouteData(request);
            request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
            controllerSelector = new DefaultHttpControllerSelector(config);
            controllerContext = new HttpControllerContext(config, routeData, request);
        }
        #endregion

        #region Methods
        public string GetActionName()
        {
            if (controllerContext.ControllerDescriptor == null)
                GetControllerType();

            var actionSelector = new ApiControllerActionSelector();
            var descriptor = actionSelector.SelectAction(controllerContext);

            return descriptor.ActionName;
        }

        public Type GetControllerType()
        {
            var descriptor = controllerSelector.SelectController(request);
            controllerContext.ControllerDescriptor = descriptor;
            return descriptor.ControllerType;
        }
        #endregion
    }

And now the RouteTests Setup configuration:

    [TestFixture]
    public class RouteTests
    {
        #region Variables
        HttpConfiguration _config;
        #endregion

        #region Setup
        [SetUp]
        public void Setup()
        {
            _config = new HttpConfiguration();
            _config.Routes.MapHttpRoute(name: "DefaultWebAPI", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });
        }
        #endregion

        #region Helper methods
        public static string GetMethodName<T, U>(Expression<Func<T, U>> expression)
        {
            var method = expression.Body as MethodCallExpression;
            if (method != null)
                return method.Method.Name;

            throw new ArgumentException("Expression is wrong");
        }
        #endregion

Let’s see that a request to api/articles/5 invokes the ArticlesController.GetArticle(int id) function:

        [Test]
        public void RouteShouldControllerGetArticleIsInvoked()
        {
            var request = new HttpRequestMessage(HttpMethod.Get, "http://www.chsakell.com/api/articles/5");

            var _actionSelector = new ControllerActionSelector(_config, request);

            Assert.That(typeof(ArticlesController), Is.EqualTo(_actionSelector.GetControllerType()));
            Assert.That(GetMethodName((ArticlesController c) => c.GetArticle(5)),
                Is.EqualTo(_actionSelector.GetActionName()));
        }

We used some reflection to get controller’s action name. In the same way we can test that the post action is invoked:

        [Test]
        public void RouteShouldPostArticleActionIsInvoked()
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "http://www.chsakell.com/api/articles/");

            var _actionSelector = new ControllerActionSelector(_config, request);

            Assert.That(GetMethodName((ArticlesController c) =>
                c.PostArticle(new Article())), Is.EqualTo(_actionSelector.GetActionName()));
        }

You will probably want to test that an invalid route is not working:

        [Test]
        public void RouteShouldInvalidRouteThrowException()
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "http://www.chsakell.com/api/InvalidController/");

            var _actionSelector = new ControllerActionSelector(_config, request);

            Assert.Throws<HttpResponseException>(() => _actionSelector.GetActionName());
        }

Conclusion

We have seen many aspects of Unit Testing in Web API stack such as mocking the Service layer, unit testing Controllers, Message Handlers, Filters, Custom Media type Formatters and the routing configuration. Try to always writing unit tests for your application and you will never regret it. The most unit tests you write the more benefits you will get. For example a simple change in your repository may brake many aspects in your application. If the appropriate tests have been written, then in the first run you should see all broken parts of your application immediately. I hope you liked the post as much I did. You can download the source code for this project here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Web API File Uploading (Desktop and Web client)

$
0
0

File uploading is one of the most common tasks all developers have to deal with, at some point of their application development cycle. For example, Web applications (such as social networks) usually require users to upload a profile picture during registration or in the case of desktop ones, users may required to upload private documents.
This post will show you how to upload multipart/form-data files using Web API with both Web and Desktop clients. More particular here’s what we gonna see:

  • Web API File Upload Controller: Create an action for Uploading multipart-form data
  • Web client: Create an AngularJS web client to upload multiple files at once using the Angular File Upload module
  • Desktop client: Create an Windows Form client to upload multiple files using HttpClient

Let’s start:

The Controller

Create a solution named WebApiFileUpload and add a new empty Web Application named WebApiFileUpload.API selecting both the Web API and MVC checkboxes. Add a Web API Controller named FileUploadController. Before showing controller’s code let’s see what this controller requires:

  • A FileUpload result: Custom result to return to clients
  • A custom MultipartFormDataStreamProvider: The provider which will actually capture and save the uploaded file
  • Allow only Mime-Multipart content to be uploaded:For this our best option is to create a Filter and apply it to the POST method

Create a Infrastructure folder inside the API project and add the following three classes that implement the above required behaviors.

public class FileUploadResult
    {
        public string LocalFilePath { get; set; }
        public string FileName { get; set; }
        public long FileLength { get; set; }
    }
public class UploadMultipartFormProvider : MultipartFormDataStreamProvider
    {
        public UploadMultipartFormProvider(string rootPath) : base(rootPath) { }

        public override string GetLocalFileName(HttpContentHeaders headers)
        {
            if (headers != null &&
                headers.ContentDisposition != null)
            {
                return headers
                    .ContentDisposition
                    .FileName.TrimEnd('"').TrimStart('"');
            }

            return base.GetLocalFileName(headers);
        }
    }
public class MimeMultipart : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (!actionContext.Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(
                    new HttpResponseMessage(
                        HttpStatusCode.UnsupportedMediaType)
                );
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {

        }
    }

And now the controller:

public class FileUploadController : ApiController
    {
        [MimeMultipart]
        public async Task<FileUploadResult> Post()
        {
            var uploadPath = HttpContext.Current.Server.MapPath("~/Uploads");

            var multipartFormDataStreamProvider = new UploadMultipartFormProvider(uploadPath);

            // Read the MIME multipart asynchronously
            await Request.Content.ReadAsMultipartAsync(multipartFormDataStreamProvider);

            string _localFileName = multipartFormDataStreamProvider
                .FileData.Select(multiPartData => multiPartData.LocalFileName).FirstOrDefault();

            // Create response
            return new FileUploadResult
            {
                LocalFilePath = _localFileName,

                FileName = Path.GetFileName(_localFileName),

                FileLength = new FileInfo(_localFileName).Length
            };
        }
    }

Make sure you create a folder named Uploads inside the project cause this is where the uploaded files will be saved. The uploaded result will contain the file uploaded local path in the server, file’s name and length in bytes. You can add any other properties you want.

AngularJS Web client

It’s time to create a web client using AngularJS that will be able to upload multiple files using the controller we have just created. As we have mentioned we are going to use the AngularJS File Upload module which can be found here. Create a Scripts folder inside the API project and you need to add the following javascript files:

  1. angular.js
  2. angular-file-upload.js
  3. angular-route.js
  4. jquery-1.8.2.js (you can use any other version if you want)

Also add the bootstrap.css inside a Styles folder. Add a HomeController MVC Controller and add an Index() action if not exists. Create the respective View for this action and paste the following code:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <link href="~/Styles/bootstrap.css" rel="stylesheet" />
</head>
<body ng-app="angularUploadApp">
    <div class="container">
        <div class="jumbotron">
            <p>Web API File uploading using AngularJS client. Read more at <a href="https://chsakell.com/2015/06/07/web-api-file-uploading-desktop-and-web-client" target="_blank">chsakell.com/</a>.</p>
        </div>
        <div ng-include="'app/templates/fileUpload.html'"></div>

    </div>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script src="Scripts/angular.js"></script>
    <script src="Scripts/angular-file-upload.js"></script>
    <script src="Scripts/angular-route.js"></script>
    <script src="app/app.js"></script>
    <script src="app/controllers/fileUploadCtrl.js"></script>

</body>
</html>

We haven’t created either the angularJS fileUpload.html or the app.js and fileUploadCtrl.js yet, so let’s add them. You have to create the following folders first:

  1. app
  2. app/controllers
  3. app/templates
'use strict';

angular.module('angularUploadApp', [
    'ngRoute',
    'angularFileUpload'
])

.config(function ($routeProvider) {
    $routeProvider
    .when('/', {
        templateUrl: 'app/templates/fileUpload.html',
        controller: 'fileUploadCtrl'
    })
    .otherwise({
        redirectTo: '/'
    });
});
'use strict';

angular.module('angularUploadApp')
    .controller('fileUploadCtrl', function ($scope, $http, $timeout, $upload) {
        $scope.upload = [];
        $scope.UploadedFiles = [];

        $scope.startUploading = function ($files) {
            //$files: an array of files selected
            for (var i = 0; i < $files.length; i++) {
                var $file = $files[i];
                (function (index) {
                    $scope.upload[index] = $upload.upload({
                        url: "./api/fileupload", // webapi url
                        method: "POST",
                        file: $file
                    }).progress(function (evt) {
                    }).success(function (data, status, headers, config) {
                        // file is uploaded successfully
                        $scope.UploadedFiles.push({ FileName: data.FileName, FilePath: data.LocalFilePath, FileLength : data.FileLength });
                    }).error(function (data, status, headers, config) {
                        console.log(data);
                    });
                })(i);
            }
        }
    });
<div class="row" ng-controller="fileUploadCtrl">
    <div class="col-xs-3">
        <div>
            <input type="file" ng-file-select="startUploading($files)" multiple>
        </div>
    </div>
    <div class="col-xs-9">
        <div class="panel panel-default" ng-repeat="uploadedFile in UploadedFiles track by $index">
            <div class="panel-heading">
                <strong>{{uploadedFile.FileName}}</strong>
            </div>
            <div class="panel-body">
                <div class=" media">
                    <a class="pull-left" href="#">
                        <img class="media-object" width="100" ng-src="../uploads/{{uploadedFile.FileName}}" />
                    </a>
                    <div class="media-body">
                        <div class="lead" style="font-size:14px;color: crimson;width:500px;word-wrap:break-word">{{uploadedFile.FilePath}}</div>
                    </div>
                </div>
            </div>
            <div class="panel-footer">
                File total bytes: <span style="color:black">{{uploadedFile.FileLength}}</span>
            </div>
        </div>
    </div>
</div>

Build and fire your application. Select multiple files to upload and ensure that all worked as expected.
webapi-file-upload-01

Desktop Client

For the Windows Form client you need to add a new Windows Form project named WebApiFileUpload.DesktopClient and add reference to the WebApiFileUpload.API project (just to access the FileUploadResult class). You also need to install the Microsoft.AspNet.WebApi.Client through the Nuget Packages. I have created a simple Form that allows the user to select multiple files and upload them through the API Controller we created. Let’s see the main method that does that.

try
                {
                    HttpClient httpClient = new HttpClient();
                    // Read the files
                    foreach (String file in openFileDialog1.FileNames)
                    {
                        var fileStream = File.Open(file, FileMode.Open);
                        var fileInfo = new FileInfo(file);
                        FileUploadResult uploadResult = null;
                        bool _fileUploaded = false;

                        var content = new MultipartFormDataContent();
                        content.Add(new StreamContent(fileStream), "\"file\"", string.Format("\"{0}\"", fileInfo.Name)
                        );

                        Task taskUpload = httpClient.PostAsync(uploadServiceBaseAddress, content).ContinueWith(task =>
                        {
                            if (task.Status == TaskStatus.RanToCompletion)
                            {
                                var response = task.Result;

                                if (response.IsSuccessStatusCode)
                                {
                                    uploadResult = response.Content.ReadAsAsync<FileUploadResult>().Result;
                                    if (uploadResult != null)
                                        _fileUploaded = true;

                                    // Read other header values if you want..
                                    foreach (var header in response.Content.Headers)
                                    {
                                        Debug.WriteLine("{0}: {1}", header.Key, string.Join(",", header.Value));
                                    }
                                }
                                else
                                {
                                    Debug.WriteLine("Status Code: {0} - {1}", response.StatusCode, response.ReasonPhrase);
                                    Debug.WriteLine("Response Body: {0}", response.Content.ReadAsStringAsync().Result);
                                }
                            }

                            fileStream.Dispose();
                        });

                        taskUpload.Wait();
                        if (_fileUploaded)
                            AddMessage(uploadResult.FileName + " with length " + uploadResult.FileLength
                                            + " has been uploaded at " + uploadResult.LocalFilePath);
                    }

                    httpClient.Dispose();
                }
                catch (Exception ex)
                {
                    AddMessage(ex.Message);
                }

Of course you need to run the API Project first before trying to upload files using the Desktop client.
webapi-file-upload-02
That’s it, we are done! We saw how to upload multipart-form data files using Web API from both Web and Desktop clients. I hope you have enjoyed the post. You can download the project we built from here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Dependency injection in WCF

$
0
0

Dependency injection is a software design pattern that implements inversion of control for resolving dependencies and is highly recommended for building scalable, testable and maintainable applications. In this very blog we have seen many times this pattern, mostly in ASP.NET MVC and ASP.NET Web API related posts where for example dependencies (data repositories or middle services) were injected into MVC constructors. We haven’t seen this pattern though in applications using the Windows Communication Framework. I decided to write this post cause I believe there many interesting things to cover when it comes to “marry” Dependency Injection and WCF. I will divide this post in the following three main areas:

Solution architecture

We are going to build a Service Oriented Application based on Windows Communication Framework. The architecture is mostly based on the decision that we are going to de-couple client and business entities in our application. Many developers believe that the only way to create WCF clients is to generate them using Visual Studio built in functionality. Of course this method works like a charm but the truth is that it strongly binds both sides of the wire: Proxies will fault if any either client’s data contract or service data contract change. And here is where the power of System.Runtime.Serialization comes into the play supporting Data Contract versioning through the IExtensibleDataObject interface. This interface has one property ExtensionData of type ExtensionDataObject which makes data contract changes non-breakable in both sides. That said we will create both business and client entities in such a way that implement the IExtensibleDataObject interface. WCF Service will serve business entities but proxies (clients) will be able to serialize using their client ones. Business and client entities will share some common properties but nothing stops us to add more or remove some others in either of them. The serialization will work properly. More over you ‘ll see that client entities may have any other functionality you wish such as custom validators which a Web client may need for it’s UI. Data repositories will use business entities mapped to the database using Entity Framework. For this example we ‘ll use Entity Framework in an hybrid way that is design the database in SQL Server and manually add later the respective properties in our business entities. Let’s take a look at the architecture of our application.
wcf-dependency-injection-03

Let’s start building our SOA application. Create an empty solution named WCF.DependencyInjection and add the following solution folders with their respective projects:

  1. Business
    • Business.Entities: Class library (Contains Service side Entities)
    • Business.Services: Class library (Contains WCF Service contracts and implementations)
  2. Client
    • Client.Contracts: Class library (Contains the relevant Client side WCF Contracts)
    • Client.Entities: Class library (Contains client side Entities)
    • Client.Proxies: Class library (Contains WCF client proxies)
  3. Core
    • Core.Common: Class library (Contains Common classes – extensions for our solution)
  4. Hosts
    • WCF.DependencyInjection.Web: Empty ASP.NET Web application (Hosts WCF Services)
  5. Data Access
    • Data.Core: Class library (Contains Entity Framework configurations, Data repositories etc..)
  6. Tests
    • Test.Proxies: Class library (Contains NUnit tests for our solution)

Business.Entities

Switch to Business.Entities and create the following two Entities which will be used as Data Contracts at the business side of the wire.

 [DataContract]
    public class Blog : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public string Owner { get; set; }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }
[DataContract]
    public class Article : IExtensibleDataObject
    {
        [DataMember]
        public int ID { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Contents { get; set; }
        [DataMember]
        public string Author { get; set; }
        [DataMember]
        public string URL { get; set; }
        [DataMember]
        public int BlogID { get; set; }

        [DataMember]
        public int ContentLength
        {
            get
            {
                return Contents.Length;
            }
            set { }
        }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }

As you can see both of our entities implement the IExtensibleDataObject interface in order to support Round-Tripping (Forward-Compatible Data Contracts). The same will apply in Client entities too. You need to add reference to System.Runtime.Serialization assembly. In fact, go and add this reference to all of our projects.

Data.Core

We need to create the data access repositories before move to WCF Services so switch to Data.Core project, add reference to Business.Entities and install Entity Framework through Nuget Packages. At this point you will allow me not to paste all the required code for this layer since we have seen the same code implementing generic data repositories in many other posts of this blog. Instead, I will show you only the most important parts that needs to be explained for this solution. More over, I will provide you links for all not shown classes from my github account. Of course once again, all the source code for this post is available (you ‘ll find a link at the bottom of this post).
wcf-dependency-injection-02
Let’s see Entity Framework configurations for our entities.

public class BlogConfiguration : EntityTypeConfiguration<Blog>
    {
        public BlogConfiguration()
        {
            HasKey(b => b.ID);
            Property(b => b.Name).IsRequired().HasMaxLength(100);
            Property(b => b.URL).IsRequired().HasMaxLength(200);
            Property(b => b.Owner).IsRequired().HasMaxLength(50);

            Ignore(b => b.ExtensionData);
        }
    }
public class ArticleConfiguration : EntityTypeConfiguration<Article>
    {
        public ArticleConfiguration()
        {
            HasKey(a => a.ID);
            Property(a => a.Title).IsRequired().HasMaxLength(100);
            Property(a => a.Contents).IsRequired();
            Property(a => a.Author).IsRequired().HasMaxLength(50);
            Property(a => a.URL).IsRequired().HasMaxLength(200);

            Ignore(a => a.ExtensionData);
            Ignore(a => a.ContentLength);
        }
    }

It’s obvious that I just wanted to point that ExtensionData will not be mapped to a database table column. Now let’s see the DbContext class that we need to access table records through Entity Framework:

public class DIContext : DbContext
    {
        public DIContext()
            : base("DIContext")
        {
            Database.SetInitializer<DIContext>(null);
        }

        public DbSet<Blog> BlogSet { get; set; }
        public DbSet<Article> ArticleSet { get; set; }

        public virtual void Commit()
        {
            base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }

We turned off the database initializer and we told Entity Framework not to use the default naming convention when accessing database. Tables will have the same name as our entity classes. Here are all the other required classes:

  1. Data.Core.Infrastructure
  2. Data.Core.Repositories

Database

Let’s see WCFDI database we need for our example. Run the following script to create the database and insert some records as well.

USE [master]
GO
/****** Object:  Database [WCFDI] ******/
CREATE DATABASE [WCFDI]
GO
USE [WCFDI]
GO

-- Create Blog Table
CREATE TABLE [dbo].[Blog](
	[ID] [int] NOT NULL,
	[Name] [nvarchar](100) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[Owner] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Blog] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


-- Crate Article Table
CREATE TABLE [dbo].[Article](
	[ID] [int] NOT NULL,
	[Title] [nvarchar](100) NOT NULL,
	[Contents] [nvarchar](max) NOT NULL,
	[Author] [nvarchar](50) NOT NULL,
	[URL] [nvarchar](200) NOT NULL,
	[BlogID] [int] NOT NULL,
 CONSTRAINT [PK_Article] PRIMARY KEY CLUSTERED
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
-- Create FK
GO
ALTER TABLE [dbo].[Article]  WITH CHECK ADD  CONSTRAINT [FK_Article_Blog] FOREIGN KEY([BlogID])
REFERENCES [dbo].[Blog] ([ID])
GO
ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_Article_Blog]
GO

-- Add some records
INSERT INTO dbo.Blog
        ( ID, Name, URL, Owner )
VALUES  ( 1, -- ID - int
          N'chsakells blog', -- Name - nvarchar(100)
          N'http://chsakell.com', -- URL - nvarchar(200)
          N'Chris Sakellarios'  -- Owner - nvarchar(50)
          )

INSERT INTO dbo.Article
        ( ID ,
          Title ,
          Contents ,
          Author ,
          URL ,
          BlogID
        )
VALUES  ( 1 , -- ID - int
          N'WCF Dependency Injection' , -- Title - nvarchar(100)
          N'Dependency injection is a software design pattern that implements..' , -- Contents - nvarchar(max)
          N'Christos Sakellarios' , -- Author - nvarchar(50)
          N'https://chsakell.com/2015/07/03/dependency-injection-in-wcf/' , -- URL - nvarchar(200)
          1  -- BlogID - int
        )

Business.Services

Now that we have done with the Data access layer we can proceed implement the core WCF Services that are going to be actually hosted (from Web application or Self hosts in our NUnit Tests). Add references to Data.Core, Business.Entities projects and System.ServiceModel assembly as well. Create a folder named Contracts inside the Business.Services project and add the following Service Contracts

    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }


At the root of this project create Services Implementations:

public class BlogService : IBlogService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IBlogRepository _blogRepository;

        public BlogService(IUnitOfWork unitOfWork, IBlogRepository blogRepository)
        {
            _unitOfWork = unitOfWork;
            _blogRepository = blogRepository;
        }
        public void Add(Blog blog)
        {
            _blogRepository.Add(blog);
            _unitOfWork.Commit();
        }

        public void Update(Blog blog)
        {
            _blogRepository.Update(blog);
            _unitOfWork.Commit();
        }

        public void Delete(Blog blog)
        {
            _blogRepository.Delete(blog);
            _unitOfWork.Commit();
        }

        public Blog GetById(int id)
        {
            return _blogRepository.GetById(id);
        }

        public Blog[] GetAll()
        {
            return _blogRepository.GetAll().ToArray();
        }
    }
public class ArticleService : IArticleService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IArticleRepository _articleRepository;

        public ArticleService(IUnitOfWork unitOfWork, IArticleRepository articleRepository)
        {
            _unitOfWork = unitOfWork;
            _articleRepository = articleRepository;
        }
        public void Add(Article article)
        {
            _articleRepository.Add(article);
            _unitOfWork.Commit();
        }

        public void Update(Article article)
        {
            _articleRepository.Update(article);
            _unitOfWork.Commit();
        }

        public void Delete(Article article)
        {
            _articleRepository.Delete(article);
            _unitOfWork.Commit();
        }

        public Article GetById(int id)
        {
            return _articleRepository.GetById(id);
        }

        public Article[] GetAll()
        {
            return _articleRepository.GetAll().ToArray();
        }
    }

Here we can see for the first time how Data repositories are going to be injected to WCF Services. For this to be completed we ‘ll have to register service’s implementations using an Inversion of Control container. We ‘ll do that later, both in Web host and self hosts using Autofac.

Core.Common

It’s time to create the client side of our SOA but before doing this, let’s create a base class for our client entities that will allow them to provide custom validation using the Fluent Validation. As we have already mentioned, client entities can have much more functionality than the Business Entities, functionality that may be required from client applications, web or desktop ones. For our example, we assume that clients needs to know if an entity is Valid or not based on custom validation rules for the specific client entity. In Core.Common install Fluent Validation through Nuget Packages and create the following class:

public class Validatable
    {
        protected IValidator _validator = null;
        protected IEnumerable<ValidationFailure> _validityErrors = null;

        protected virtual IValidator GetValidator()
        {
            return null;
        }

        public IEnumerable<ValidationFailure> ValidityErrors
        {
            get { return _validityErrors; }
            set { }
        }

        public void Validate()
        {
            if (_validator != null)
            {
                ValidationResult results = _validator.Validate(this);
                _validityErrors = results.Errors;
            }
        }

        public virtual bool IsValid
        {
            get
            {
                if (_validityErrors != null && _validityErrors.Count() > 0)
                    return false;
                else
                    return true;
            }
        }
    }

All that our Client Entities need to have is an override for the GetValidator() virtual method.

Client.Entities

Make sure you also install Fluent Validation package through Nuget Packages and create the following two files.

namespace Client.Entities
{
    public class Blog : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _name;
        string _url;
        string _owner;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Name
        {
            get
            {
                return _name;
            }
            set { _name = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public string Owner
        {
            get
            {
                return _owner;
            }
            set { _owner = value; }
        }

        #endregion

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new BlogValidator();
        }
        #endregion
    }

    // Validator Class
    class BlogValidator : AbstractValidator<Blog>
    {
        public BlogValidator()
        {
            RuleFor(b => b.Name).NotEmpty();
            RuleFor(b => b.Owner).NotEmpty();
            RuleFor(b => b.ID).GreaterThan(0);
        }
    }
}
namespace Client.Entities
{
    public class Article : Validatable, IExtensibleDataObject
    {
        #region Variables
        int _id;
        string _title;
        string _contents;
        string _author;
        string _url;
        int _blogID;
        #endregion

        #region Properties
        public int ID
        {
            get
            {
                return _id;
            }
            set { _id = value; }
        }
        public string Title
        {
            get
            {
                return _title;
            }
            set { _title = value; }
        }
        public string Contents
        {
            get
            {
                return _contents;
            }
            set { _contents = value; }
        }
        public string Author
        {
            get
            {
                return _author;
            }
            set { _author = value; }
        }
        public string URL
        {
            get
            {
                return _url;
            }
            set { _url = value; }
        }
        public int BlogID
        {
            get
            {
                return _blogID;
            }
            set { _blogID = value; }
        }
        #endregion

        #region IExtensibleDataObject

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion

        #region Validation
        protected override IValidator GetValidator()
        {
            return new ArticleValidator();
        }
        #endregion
    }

    // Validator Class
    class ArticleValidator : AbstractValidator<Article>
    {
        public ArticleValidator()
        {
            RuleFor(a => a.Author).NotEmpty();
            RuleFor(a => a.BlogID).GreaterThan(0);
            RuleFor(a => a.Contents).NotEmpty();
        }
    }
}

I believe it’s getting clearer now what we are trying to achieve. Those two classes will be used for serializing WCF call results. Clients won’t be tightly bound to WCF Business side entities. The next thing to do, is to create the relevant Client side Service Contracts that use Client Entities.

Client.Contracts

Switch to Client.Contracts and add references to Client.Entities and Core.Common projects. You also need to add reference to System.ServiceModel assembly. Create the following two contracts:

namespace Client.Contracts
{
    [ServiceContract]
    public interface IBlogService
    {
        [OperationContract]
        void Add(Blog blog);

        [OperationContract]
        void Update(Blog blog);

        [OperationContract]
        void Delete(Blog blog);

        [OperationContract]
        Blog GetById(int id);

        Blog[] GetAll();
    }
}
namespace Client.Contracts
{
    [ServiceContract]
    public interface IArticleService
    {
        [OperationContract]
        void Add(Article article);

        [OperationContract]
        void Update(Article article);

        [OperationContract]
        void Delete(Article article);

        [OperationContract]
        Article GetById(int id);

        [OperationContract]
        Article[] GetAll();
    }
}

Seems familiar? Of course it does, it’s the same contracts we added in the business side of our SOA except that uses Client Entities.

Client.Proxies

We have created the client service contracts but we haven’t provided yet implementations for those interfaces. Guess what, the classes that will implement those contracts are the WCF Client proxies. Switch to Client.Proxies and add references to Client.Entities, Core.Common, Client.Contracts projects and System.ServiceModel assembly. Now provide the following implementations:

public class BlogClient : ClientBase<IBlogService>, IBlogService
    {
        #region IBlogService implementation
        public void Add(Blog blog)
        {
            Channel.Add(blog);
        }

        public void Update(Blog blog)
        {
            Channel.Update(blog);
        }

        public void Delete(Blog blog)
        {
            Channel.Delete(blog);
        }

        public Blog GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Blog[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }
public class ArticleClient : ClientBase<IArticleService>, IArticleService
    {
        #region IArticleService Members
        public void Add(Article article)
        {
            Channel.Add(article);
        }

        public void Update(Article article)
        {
            Channel.Update(article);
        }

        public void Delete(Article article)
        {
            Channel.Delete(article);
        }

        public Article GetById(int id)
        {
            return Channel.GetById(id);
        }

        public Article[] GetAll()
        {
            return Channel.GetAll();
        }
        #endregion

        public void CleanUp()
        {
            try
            {
                if (base.State != CommunicationState.Faulted)
                    base.Close();
                else
                    base.Abort();
            }
            catch (Exception ex)
            {
                base.Abort();
            }
        }
    }

Business Entities – Client Entities Deserialization

So far so good, but yet we haven’t completed all the missing parts relating to Deserializing Client entities to Business Entities and vise versa. Take a look at the Article entity in both sides, Client and Business. First of all notice that Business Article has DataContract and DataMember attributes to inform what parts of the class will be used for serialization. There isn’t such thing in the client side entity which means that all public properties will be used for serialization. I made that choice in order not to pollute the class since I may add even more functionality later. More over, If you are familiar with WCF Services, you will be aware that client and service data contracts must have the same namespace otherwise the deserialization will fail. At this point our client and business entities belong to different namespaces so it won’t work. We could add a namespace attribute value in the business entities like this:

[DataContract(Namespace="http://www.chsakell.com/WCF_DI")]

But this would require to insert the same attributes in the client side which is something I ‘d like to avoid. To solve this, there is a trick you can do, and register the common namespace for those entities. Switch to the AssemblyInfo.cs file in both of the projects and register there the namespace you want to be used for serialization-deserialization during WCF calls, by adding the following entries.

[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Business.Entities")]
[assembly: ContractNamespace("http://www.chsakell.com/WCF_DI",
                              ClrNamespace = "Client.Entities")]

WCF Web Host

Switch to WCF.DependencyInjection.Web Web application to host our WCF Services. You need to add references to Business.Services, Data.Core projects and System.ServiceModel assembly. You also need to install Entity Framework and Autofac.Integration.Wcf packages from Nuget Packages as well.
Add two WCF Service files named BlogService.svc and ArticleService. Make sure you remove the *.svc.cs file created for each service and just leave the .svc file. Alter those files as follow:

<%@ ServiceHost
    Language="C#"
    Debug="true"
    Service="Business.Services.Contracts.IBlogService, Business.Services"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>
<%@ ServiceHost
    Language="C#"
    Debug="true"
    Service="Business.Services.Contracts.IArticleService, Business.Services"
    Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

It’s time to register our services and implementations using Autofac in order for Dependency Injection to work. Create the following Bootstrapper class:

public static class Bootstrapper
    {
        /// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services

            builder.RegisterType<BlogService>().As<IBlogService>();
            builder.RegisterType<ArticleService>().As<IArticleService>();

            // register repositories and UnitOfWork
            builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
            builder.RegisterType<DbFactory>().As<IDbFactory>();
            builder.RegisterType<ArticleRepository>().As<IArticleRepository>();
            builder.RegisterType<BlogRepository>().As<IBlogRepository>();

            // build container
            return builder.Build();
        }
    }

You need to register this container in Application start event so create a Global.asax file (if not exists) and paste the following code that uses Autofac.Integration.Wcf.

protected void Application_Start(object sender, EventArgs e)
        {
            IContainer container = Bootstrapper.BuildContainer();
            AutofacHostFactory.Container = container;
        }

Don’t forget to add a connection string in Web.config file that points to WCFDI database we created before, to be used by Entity Framework.

  <connectionStrings>
    <add name="DIContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=WCFDI;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

At this point you should be able to use dependency injection concerning injecting data repositories to your WCF services.

Test.Proxies

We have done a lot of work structuring our SOA application and now it’s time to test all of our components. We need to test that data repositories are indeed injected and of course view how to configure proxy dependency injection. Switch to Test.Proxies project and for start add references to all of the other projects except the web application. Now install the following Nuget packages:

  1. Autofac WCF Integration
  2. Moq (We ‘ll mock the data repositories)
  3. NUnit
  4. NUnit Test Adapter

Also add reference to System.ServiceModel assembly.
Before creating the relevant Bootstrapper to be used by WCF self hosts, let’s create a class that needs WCF proxies to make WCF calls. Those proxies will have to be injected by Autofac container rather than be manually created.

public class ClientInjectionClass : Disposable
    {
        public Client.Contracts.IBlogService _blogProxy;
        public Client.Contracts.IArticleService _articleProxy;
        public ClientInjectionClass(Client.Contracts.IArticleService articleServiceProxy,
            Client.Contracts.IBlogService blogServiceProxy)
        {
            this._blogProxy = blogServiceProxy;
            this._articleProxy = articleServiceProxy;
        }

        #region IDisposable
        protected override void DisposeCore()
        {
            base.DisposeCore();
            try
            {
                (_blogProxy as Client.Proxies.BlogClient).CleanUp();

                (_articleProxy as Client.Proxies.ArticleClient).CleanUp();
            }
            catch
            {
                _blogProxy = null;
                _articleProxy = null;
            }
        }
        #endregion

        #region Methods

        public Client.Entities.Article[] GetArticles()
        {
            return _articleProxy.GetAll();
        }

        public Client.Entities.Blog GetBlogById(int id)
        {
            return _blogProxy.GetById(id);
        }

        #endregion
    }

Let’s view the Bootstrapper which will mock data repositories, register WCF Services, Client contracts with their respective implementations.

public static class Bootstrapper
    {/// <summary>
        /// Configures and builds Autofac IOC container.
        /// </summary>
        /// <returns></returns>
        public static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();

            // register services
            builder.RegisterType<BlogService>().As<Business.Services.Contracts.IBlogService>();
            builder.RegisterType<ArticleService>().As<Business.Services.Contracts.IArticleService>();

            // register proxies
            builder.Register(c => new ChannelFactory<Client.Contracts.IArticleService>("BasicHttpBinding_IArticleService"))
                .InstancePerLifetimeScope();
            builder.Register(c => new ChannelFactory<Client.Contracts.IBlogService>("BasicHttpBinding_IBlogService"))
                .InstancePerLifetimeScope();

            builder.RegisterType<ArticleClient>().As<Client.Contracts.IArticleService>().UseWcfSafeRelease();
            builder.RegisterType<BlogClient>().As<Client.Contracts.IBlogService>().UseWcfSafeRelease();

            // Unit of Work
            var _unitOfWork = new Mock<IUnitOfWork>();
            builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();
            // DbFactory
            var _dbFactory = new Mock<IDbFactory>();
            builder.RegisterInstance(_dbFactory.Object).As<IDbFactory>();

            //Repositories
            var _articlesRepository = new Mock<IArticleRepository>();
            _articlesRepository.Setup(x => x.GetAll()).Returns(new List<Business.Entities.Article>()
                {
                    new Business.Entities.Article() {
                        ID = 1,
                        Author = "Chris Sakellarios",
                        BlogID = 1,
                        Contents = "Dependency injection is a software design pattern that implements..",
                        Title = "WCF Dependency Injection",
                        URL =" https://chsakell.com/2015/07/03/dependency-injection-in-wcf/"}
                });
            builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();

            var _blogsRepository = new Mock<IBlogRepository>();
            _blogsRepository.Setup(x => x.GetById(It.IsAny<int>()))
                .Returns(new Func<int, Business.Entities.Blog>(
                    id => new Business.Entities.Blog() {
                        ID = id,
                        Name = "chsakell's blog",
                        Owner = "Chris Sakellarios",
                        URL = "https://chsakell.com/"
                    }));
            builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();

            builder.RegisterType<ClientInjectionClass>();

            // build container
            return builder.Build();
        }
    }

I have highlighted the parts that we register client side service contracts to used for WCF proxy injection because when this is done, clients will search for the respective Endpoint configurations in the configuration file. To make this work, add an App.config file in the Test.Proxies project and paste the following code.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IArticleService" />
        <binding name="BasicHttpBinding_IBlogService" />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost:18850/ArticleService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IArticleService"
          contract="Client.Contracts.IArticleService" name="BasicHttpBinding_IArticleService" />
      <endpoint address="http://localhost:18850/BlogService.svc"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBlogService"
          contract="Client.Contracts.IBlogService" name="BasicHttpBinding_IBlogService" />
    </client>
  </system.serviceModel>
</configuration>

Create a ProxiesTests.cs to write our NUnit Tests. First of all we need to Setup the self host and the IoC Container before each of our test runs. At the end of the test we can close the host. for those two we ‘ll use NUnit’s Setup and TearDown attributes.

    [TestFixture]
    public class ProxiesTests
    {
        IContainer container = null;
        ServiceHost svcArticleHost = null;
        ServiceHost svcBlogHost = null;
        Uri svcArticleServiceURI = new Uri("http://localhost:18850/ArticleService.svc");
        Uri svcBlogServiceURI = new Uri("http://localhost:18850/BlogService.svc");


        [SetUp]
        public void Setup()
        {
            try
            {
                container = Bootstrapper.BuildContainer();

                svcArticleHost = new ServiceHost(typeof(ArticleService), svcArticleServiceURI);
                svcBlogHost = new ServiceHost(typeof(BlogService), svcBlogServiceURI);

                svcArticleHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IArticleService>(container);
                svcBlogHost.AddDependencyInjectionBehavior<Business.Services.Contracts.IBlogService>(container);

                svcArticleHost.Open();
                svcBlogHost.Open();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }

        [TearDown]
        public void TearDown()
        {
            try
            {
                if (svcArticleHost != null && svcArticleHost.State != CommunicationState.Closed)
                    svcArticleHost.Close();

                if (svcBlogHost != null && svcBlogHost.State != CommunicationState.Closed)
                    svcBlogHost.Close();
            }
            catch (Exception ex)
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
            finally
            {
                svcArticleHost = null;
                svcBlogHost = null;
            }
        }
}

Before running tests, make sure you run Visual Studio as administrator for self host to work. Let’s write a test to check that self host works as expected:

        [Test]
        public void test_self_host_connection()
        {
            Assert.That(svcArticleHost.State, Is.EqualTo(CommunicationState.Opened));
            Assert.That(svcBlogHost.State, Is.EqualTo(CommunicationState.Opened));
        }

Test that ArticleClient and BlogClient proxies are injected properly when resolving Client.Contracts.IArticleService and Client.Contracts.IBlogService client service contracts:

        [Test]
        public void test_article_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IArticleService proxy
                = container.Resolve<Client.Contracts.IArticleService>();

                Assert.IsTrue(proxy is ArticleClient);
            }
        }

        [Test]
        public void test_blog_proxy_is_injected()
        {
            using (var lifetime = container.BeginLifetimeScope())
            {
                Client.Contracts.IBlogService proxy
                = container.Resolve<Client.Contracts.IBlogService>();

                Assert.IsTrue(proxy is BlogClient);
            }
        }

Test proxy state expected behavior:

        [Test]
        public void test_article_proxy_state()
        {
            Client.Contracts.IArticleService proxy;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                CommunicationState state = (proxy as ArticleClient).State;
                Assert.That(state, Is.EqualTo(CommunicationState.Created));

                // Open connection
                (proxy as ArticleClient).Open();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Opened));
                // Close connection
                (proxy as ArticleClient).Close();
                Assert.That((proxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            }
        }

Test data repositories injection in WCF Services:

        [Test]
        public void test_article_proxy_getall()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            // Close connection
            if ((proxy as ArticleClient).State == CommunicationState.Opened)
                (proxy as ClientBase<Client.Contracts.IArticleService>).Close();
        }

Test that proxies are injected and cleaned properly in ClientInjectionClass.

        [Test]
        public void test_constructor_injected_proxy()
        {
            ClientInjectionClass _testClass = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                using (_testClass = new ClientInjectionClass(container.Resolve<Client.Contracts.IArticleService>(),
                   container.Resolve<Client.Contracts.IBlogService>()))
                {
                    {
                        Client.Entities.Article[] _articles = _testClass.GetArticles();
                        Client.Entities.Blog _blog = _testClass.GetBlogById(1);

                        Assert.That(_articles.Count(), Is.EqualTo(1));
                        Assert.That(_blog, Is.Not.Null);
                        Assert.That(_blog.IsValid, Is.EqualTo(true));
                    }
                }
            }

            Assert.That((_testClass._articleProxy as ArticleClient).State, Is.EqualTo(CommunicationState.Closed));
            Assert.That((_testClass._blogProxy as BlogClient).State, Is.EqualTo(CommunicationState.Closed));
        }

In case you have noticed, I added an extra property named ContentLength at the Business Article entity, a property that doesn’t exist in the client Article entity. Still serialization – deserialization works like a charm due to the IExtensibleDataObject interface the both implement. Let’s write a test to view that Article client holds this property in it’s ExtensionData property. To read this property we need a method that uses reflection and you can view it from here.

        [Test]
        public void test_article_extension_data_not_empty()
        {
            Client.Contracts.IArticleService proxy;
            Client.Entities.Article[] articles = null;

            using (var lifetime = container.BeginLifetimeScope())
            {
                proxy = container.Resolve<Client.Contracts.IArticleService>();

                articles = proxy.GetAll();
            }

            Assert.That(articles.Count(), Is.EqualTo(1));

            var contentLength = Extensions.GetExtensionDataMemberValue(articles.First(), "ContentLength");

            Assert.That(articles.First().Contents.Length, Is.EqualTo(Int32.Parse(contentLength.ToString())));
        }

Discussion

We have seen many interesting concepts in this article, from WCF Dependency Injection integration and client proxy injection to de-coupled and extensible Data Contracts. There is one thing I would like to discuss here and it has to do about dependency injection in constructors. Days ago, a fellow asked me what if a class required ten or even twenty repositories? Do I have to pollute my constructor that much? The answer is not necessary. There is another more abstract and generic pattern that you can use where you create a generic Factory capable of resolving custom IDataRepository interfaces. In other words, you can create an interface like this:

public interface IDataRepositoryFactory
    {
        T GetCustomDataRepository<T>() where T : IDataRepository;
    }

And then implement it as follow:

public class DataRepositoryFactory : IDataRepositoryFactory
    {
        T IDataRepositoryFactory.GetCustomDataRepository<T>()
        {
            return Container.Resole<T>();
        }
    }

Then all you have to do is register this high level interface using an IoC and use this in your constructors instead of custom ones. When you need a custom repository, for example an IArticleRepository you get it like this:

private IArticleRepository _articleRepository;
private IBlogRepository _blogRepository;
private IDataRepositoryFactory _repositoryFactory;

// constructor
public ArticleService(IDataRepositoryFactory repositoryFactory)
        {
            _repositoryFactory= repositoryFactory;
        }

// method that requires IArticleRepository
public Article[] GetAll()
{
  _articleRepository = _repositoryFactory.GetCustomDataRepository<IArticleRepository>();
  return _articleRepository.GetAll();
}
// method that requires IBlogRepository
public Blog[] GetAll()
{
  _blogRepository= _repositoryFactory.GetCustomDataRepository<IBlogRepository>();
  return _blogRepository.GetAll();
}

That’s it, we ‘re done! I hope you enjoyed reading this post as much as I did writing it. You can download the source code for this post here.

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

Building Single Page Applications using Web API and angularJS (Free e-book)

$
0
0

Single Page Applications are getting more and more attractive nowadays for two basic reasons. Website users have always preferred a fluid user experience than one with page reloads and the incredible growth of several JavaScript frameworks such as angularJS. This growth in conjunction with all the powerful server side frameworks makes Single Page Application development a piece of cake. This post is the online version of the free e-book and describes step by step how to build a production-level SPA using ASP.NET Web API 2 and angularJS. You have two choices. Either grab a coffee and keep going on with this version or simply download the e-book and enjoy it whenever and wherever you want.
spa-webapi-angular-28
There are a lot of stuff to build in this application so I will break this post in the following sections:

  • What we are going to build: Describe the purpose and the requirements of our SPA
  • What we are going to use: The list of technologies and all server and front-end side libraries and frameworks
  • SPA architecture: The entire design of our SPA application from the lowest to the highest level
  • Domain Entities and Data repositories: Build the required Domain Entities and Data repositories using the generic repository pattern
  • Membership: Create a custom authentication mechanism which will be used for Basic Authentication through Web API
  • Single Page Application: Start building the core SPA components step by step
  • Discussion: We ‘ll discuss the choices we made in the development process and everything you need to know to scale up the SPA

Ready to go? Let’s start!

What we are going to build

We are going to build a Singe Page Application to support the requirements of a Video Rental store that is a store that customers visit, pick and rent DVDs. Days later they come back and return what they have borrowed. This Web application is supposed to be used only by the rental store’s employees and that’s a requirement that will affect mostly the front-end application’s architecture. Let’s see the requirements along with their respective screenshots:

Requirement 1: Home page

  1. Latest DVD movies released added to the system must be displayed
  2. For each DVD, relevant information and functionality must be available, such as display availability, watch YouTube trailer and its rating
  3. On the right side of the page, genre statistics are being displayed
  4. This page should be accessible to unauthenticated users

spa-webapi-angular-01

Requirement 2: Customers

  1. There will be 2 pages related to customers. One to view and edit them and another for registration
  2. Both of the pages must be accessible only to authenticated users
  3. The page where all customers are being displayed should use pagination for faster results. A search textbox must be able to filter the already displayed customers and start a new server side search as well
  4. Customer information should be editable in the same view through a modal popup window

spa-webapi-angular-02
spa-webapi-angular-03
spa-webapi-angular-04

Requirement 3: Movies

  1. All movies must be displayed with their relevant information (availability, trailer etc..)
  2. Pagination must be used for faster results, and user can either filter the already displayed movies or search for new ones
  3. Clicking on a DVD image must show the movie’s Details view where user can either edit the movie or rent it to a specific customer if available. This view is accessible only to authenticated users
  4. When employee decides to rent a specific DVD to a customer through the Rent view, it should be able to search customers through an auto-complete textbox
  5. The details view displays inside a panel, rental-history information for this movie, that is the dates rentals and returnings occurred. From this panel user can search a specific rental and mark it as returned
  6. Authenticated employees should be able to add a new entry to the system. They should be able to upload a relevant image for the movie as well

spa-webapi-angular-05
spa-webapi-angular-06
spa-webapi-angular-07
spa-webapi-angular-08
spa-webapi-angular-09

Requirement 4: Movie Rental History

  1. There should be a specific view for authenticated users where rental history is being displayed for all system’s movies. History is based on total rentals per date and it’s being displayed through a line chart

spa-webapi-angular-10

Requirement 5: Accounts

  1. There should be views for employees to either login or register to system. For start employees are being registered as Administrator

spa-webapi-angular-11

General requirements

  1. All views should be displayed smoothly even to mobile devices. For this bootstrap and collapsible components will be used (sidebar, topbar)

spa-webapi-angular-12

What we are going to use

We have all the requirements, now we need to decide the technologies we are going to use in order to build our SPA application.

Server Side

  1. ASP.NET Web API for serving data to Web clients (browsers)
  2. Entity Framework as Object-relational Mapper for accessing data (SQL Server)
  3. Autofac for Inversion of Control Container and resolving dependencies
  4. Automapper for mapping Domain entities to ViewModels
  5. FluentValidation for validating ViewModels in Web API Controllers

Front Side

  1. AngularJS as the core JavaScript framework
  2. Bootstrap 3 as the CSS framework for creating a fluent and mobile compatible interface
  3. 3rd party libraries

SPA architecture

We have seen both application’s requirements and the technologies we are going to use, now it’s time to design a decoupled, testable and scalable solution. There are two different designs we need to provide here. The first one has to do with the entire project’s solution structure and how is this divided in independent components. The second one has to do with the SPA structure itself, that is how angularJS folders and files will be organized.

Application Design – 1

  1. At the lowest level we have the Database. We ‘ll use Entity Framework Code First but this doesn’t prevent us to design the database directly from the SQL Server. In fact that’s what I did, I created all the tables and relationships in my SQL Server and then added the respective model Entities. That way I didn’t have to work with Code First Migrations and have more control on my database and entities. Though, I have to note that when development processes finished, I enabled code first migrations and added a seed method to initialize some data, just to help you kick of the project. We ‘ll see more about this in the installation section.
  2. The next level are the domain Entities. These are the classes that will map our database tables. One point I want to make here is that when I started design my entities, none of them had virtual references or collections for lazy loading. Those virtual properties were added during the development and the needs of the application.
  3. Entity Framework configurations, DbContext and Generic Repositories are the next level. Here we ‘ll configure EF and we ‘ll create the base classes and repositories to access database data
  4. Service layer is what comes next. For this application there will be only one service the membership service. This means that data repositories are going to be injected directly to our Web API Controllers. I ‘ve made that decision because there will be no complex functionality as far the data accessing. If you wish though you can use this layer to add a middle layer for data accessing too.
  5. Last but not least is the Web application that will contain the Web API Controllers and the SPA itself. This project will start as an Empty ASP.NET Web Application with both Web API and MVC references included.

Let’s take a look at the Database design.
spa-webapi-angular-13
Notice that for each Movie can exist multiple stock items that may be available or not. Think this as there are many DVDs of the same movie. The movie itself will be categorized as available or not depending on if there is any available stock item or not. Each customer rents a stock item and when he/she does, this stock item becomes unavailable until he/she returns it back to store. The tables used to accomplish this functionality are Customer, Rental, Stock. You can also see that there are 3 membership tables, User, UserRole and Role which are quite self explanatory. Upon them we ‘ll build the custom membership mechanism. I have also created an Error table just to show you how to avoid polluting you code with Try, Catch blocks and have a centralized logging point in your application.
spa-webapi-angular-14

Application Design – 2 (angular components)

  1. Folders are organized by Feature in our SPA. This means that you ‘ll see folders such as Customers, Movies and Rental
  2. Each of those folders may have angularJS controllers, directives or templates
  3. There is a folder Modules for hosting reusable components-modules. Those modules use common directives or services from the respective common folders
  4. 3rd party libraries are inside a folder Vendors. Here I want to point something important. You should (if not already yet) start using Bower for installing-downloading web dependencies, packages etc.. After you download required packages through Bower, you can then either include them in your project or simple simply reference them from their downloaded folder. In this application though, you will find all the required vendors inside this folder and just for reference, I will provide you with the bower installation commands for most of those packages.

spa-webapi-angular-15

Domain Entities and Data repositories

Time to start building our Single Page Application. Create a new empty solution named HomeCinema and add new class library project named HomeCinema.Entities. We ‘ll create those first. All of our entities will implement an IEntityBase interface which means that will have an ID property mapping to their primary key in the database. Add the following interface:

public interface IEntityBase
    {
        int ID { get; set; }
    }

Each movie belongs to a specific Genre (Comedy, Drama, Action, etc..). If we want to be able to retrieve all movies through a Genre instance, then we need to add a virtual collection of Movies property.

public class Genre : IEntityBase
    {
        public Genre()
        {
            Movies = new List<Movie>();
        }
        public int ID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Movie> Movies { get; set; }
    }

The most important Entity of our application is the Movie. A movie holds information such as title, director, release date, trailer URL (Youtube) or rating. As we have already mentioned, for each movie there are several stock items and hence for this entity we need to add a collection of Stock.

public class Movie : IEntityBase
    {
        public Movie()
        {
            Stocks = new List<Stock>();
        }
        public int ID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Image { get; set; }
        public int GenreId { get; set; }
        public virtual Genre Genre { get; set; }
        public string Director { get; set; }
        public string Writer { get; set; }
        public string Producer { get; set; }
        public DateTime ReleaseDate { get; set; }
        public byte Rating { get; set; }
        public string TrailerURI { get; set; }
        public virtual ICollection<Stock> Stocks { get; set; }
    }

Each stock actually describes a DVD by itself. It has a reference to a specific movie and a unique key (code) that uniquely identifies it. For example, when there are three available DVDs for a specific movie then 3 unique codes identify those DVDs. The employee will choose among those codes which could probably be written on the DVD to rent a specific movie to a customer. Since a movie rental is directly connected to a stock item, Stock entity may have a collection of Rental items that is all rentals for this stock item.

public class Stock : IEntityBase
    {
        public Stock()
        {
            Rentals = new List<Rental>();
        }
        public int ID { get; set; }
        public int MovieId { get; set; }
        public virtual Movie Movie { get; set; }
        public Guid UniqueKey { get; set; }
        public bool IsAvailable { get; set; }
        public virtual ICollection<Rental> Rentals { get; set; }
    }

The customer Entity is self explanatory.

public class Customer : IEntityBase
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string IdentityCard { get; set; }
        public Guid UniqueKey { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Mobile { get; set; }
        public DateTime RegistrationDate { get; set; }
    }

The Rental entity which finally describes a DVD rental for a specific customer holds information about the customer, the stock item he/she picked (DVD and its code), the rentals date, its status (Borrowed or Returned and the date the customer returned it.

public class Rental : IEntityBase
    {
        public int ID { get; set; }
        public int CustomerId { get; set; }
        public int StockId { get; set; }
        public virtual Stock Stock { get; set; }
        public DateTime RentalDate { get; set; }
        public Nullable<DateTime> ReturnedDate { get; set; }
        public string Status { get; set; }
    }

Now let’s see all Entities related to Membership. The first one is the Role that describes logged in user’s role. For our application there will be only the Admin role (employees) but we will discuss later the scalability options we have in case we want customers to use the application as well. Let me remind you that we are going to use Basic Authentication for Web API Controllers and many controllers and their actions will have an Authorize attribute and a list of roles authorized to access their resources.

public class Role : IEntityBase
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

User entity holds basic information for the user and most important the salt and the encrypted by this salt, password.

public class User : IEntityBase
    {
        public User()
        {
            UserRoles = new List<UserRole>();
        }
        public int ID { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public string HashedPassword { get; set; }
        public string Salt { get; set; }
        public bool IsLocked { get; set; }
        public DateTime DateCreated { get; set; }

        public virtual ICollection<UserRole> UserRoles { get; set; }
    }

A user may have more than one roles so we have a UserRole Entity as well.

public class UserRole :IEntityBase
    {
        public int ID { get; set; }
        public int UserId { get; set; }
        public int RoleId { get; set; }
        public virtual Role Role { get; set; }
    }

One last entity I have added is the Error. It is always good to log your application’s errors and we ‘ll use a specific repository to do this. I decided to add error logging functionality in order to show you a nice trick that will prevent you from polluting you controllers with Try Catch blocks all over the place. We ‘ll see it in action when we reach Web API Controllers.

public class Error : IEntityBase
    {
        public int ID { get; set; }
        public string Message { get; set; }
        public string StackTrace { get; set; }
        public DateTime DateCreated { get; set; }
    }

Data Repositories

Add a new class library project named HomeCinema.Data and add reference to HomeCinema.Entities project. Make sure you also install Entity Framework through Nuget Packages. For start we will create EF Configurations for our Entities. Add a new folder named Configurations and add the following configuration to declare the primary key for our Entities:

public class EntityBaseConfiguration<T> : EntityTypeConfiguration<T> where T : class, IEntityBase
    {
        public EntityBaseConfiguration()
        {
            HasKey(e => e.ID);
        }
    }

Entity Framework either way assumes that a property named “ID” is a primary key but this is a nice way to declare it in case you give this property different name. Following are one by one all other configurations. I will highlight the important lines (if any) to notice for each of these.

public class GenreConfiguration : EntityBaseConfiguration<Genre>
    {
        public GenreConfiguration()
        {
            Property(g => g.Name).IsRequired().HasMaxLength(50);
        }
    }
public MovieConfiguration()
        {
            Property(m => m.Title).IsRequired().HasMaxLength(100);
            Property(m => m.GenreId).IsRequired();
            Property(m => m.Director).IsRequired().HasMaxLength(100);
            Property(m => m.Writer).IsRequired().HasMaxLength(50);
            Property(m => m.Producer).IsRequired().HasMaxLength(50);
            Property(m => m.Writer).HasMaxLength(50);
            Property(m => m.Producer).HasMaxLength(50);
            Property(m => m.Rating).IsRequired();
            Property(m => m.Description).IsRequired().HasMaxLength(2000);
            Property(m => m.TrailerURI).HasMaxLength(200);
            HasMany(m => m.Stocks).WithRequired().HasForeignKey(s => s.MovieId);
        }
    }
public class StockConfiguration : EntityBaseConfiguration<Stock>
    {
        public StockConfiguration()
        {
            Property(s => s.MovieId).IsRequired();
            Property(s => s.UniqueKey).IsRequired();
            Property(s => s.IsAvailable).IsRequired();
            HasMany(s => s.Rentals).WithRequired(r=> r.Stock).HasForeignKey(r => r.StockId);
        }
    }
public class CustomerConfiguration : EntityBaseConfiguration<Customer>
    {
        public CustomerConfiguration()
        {
            Property(u => u.FirstName).IsRequired().HasMaxLength(100);
            Property(u => u.LastName).IsRequired().HasMaxLength(100);
            Property(u => u.IdentityCard).IsRequired().HasMaxLength(50);
            Property(u => u.UniqueKey).IsRequired();
            Property(c => c.Mobile).HasMaxLength(10);
            Property(c => c.Email).IsRequired().HasMaxLength(200);
            Property(c => c.DateOfBirth).IsRequired();
        }
    }
public class RentalConfiguration : EntityBaseConfiguration<Rental>
    {
        public RentalConfiguration()
        {
            Property(r => r.CustomerId).IsRequired();
            Property(r => r.StockId).IsRequired();
            Property(r => r.Status).IsRequired().HasMaxLength(10);
            Property(r => r.ReturnedDate).IsOptional();
        }
    }
public class RoleConfiguration : EntityBaseConfiguration<Role>
    {
        public RoleConfiguration()
        {
            Property(ur => ur.Name).IsRequired().HasMaxLength(50);
        }
    }
public class UserRoleConfiguration : EntityBaseConfiguration<UserRole>
    {
        public UserRoleConfiguration()
        {
            Property(ur => ur.UserId).IsRequired();
            Property(ur => ur.RoleId).IsRequired();
        }
    }
public class UserConfiguration : EntityBaseConfiguration<User>
    {
        public UserConfiguration()
        {
            Property(u => u.Username).IsRequired().HasMaxLength(100);
            Property(u => u.Email).IsRequired().HasMaxLength(200);
            Property(u => u.HashedPassword).IsRequired().HasMaxLength(200);
            Property(u => u.Salt).IsRequired().HasMaxLength(200);
            Property(u => u.IsLocked).IsRequired();
            Property(u => u.DateCreated);
        }
    }

Those configurations will affect how the database tables will be created. Add a new class named HomeCinemaContext at the root of the project. This class will inherit from DbContext and will be the main class for accessing data from the database.

 public class HomeCinemaContext : DbContext
    {
        public HomeCinemaContext()
            : base("HomeCinema")
        {
            Database.SetInitializer<HomeCinemaContext>(null);
        }

        #region Entity Sets
        public IDbSet<User> UserSet { get; set; }
        public IDbSet<Role> RoleSet { get; set; }
        public IDbSet<UserRole> UserRoleSet { get; set; }
        public IDbSet<Customer> CustomerSet { get; set; }
        public IDbSet<Movie> MovieSet { get; set; }
        public IDbSet<Genre> GenreSet { get; set; }
        public IDbSet<Stock> StockSet { get; set; }
        public IDbSet<Rental> RentalSet { get; set; }
        public IDbSet<Error> ErrorSet { get; set; }
        #endregion

        public virtual void Commit()
        {
            base.SaveChanges();
        }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new UserConfiguration());
            modelBuilder.Configurations.Add(new UserRoleConfiguration());
            modelBuilder.Configurations.Add(new RoleConfiguration());
            modelBuilder.Configurations.Add(new CustomerConfiguration());
            modelBuilder.Configurations.Add(new MovieConfiguration());
            modelBuilder.Configurations.Add(new GenreConfiguration());
            modelBuilder.Configurations.Add(new StockConfiguration());
            modelBuilder.Configurations.Add(new RentalConfiguration());
        }
    }

Notice that I ‘ve made a decision to name all of the Entity Sets with a Set prefix. Also I turned of the default pluralization convention that Entity Framework uses when creating the tables in database. This will result in tables having the same name as the Entity. You need to add an App.config file if not exists and create the following connection string:

  <connectionStrings>
    <add name="HomeCinema" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=HomeCinema;Integrated Security=SSPI; MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />
  </connectionStrings>

You can alter the server if you wish to match your development environment.
Let us proceed with the UnitOfWork pattern implementation. Add a folder named Infrastructure and paste the following classes and interfaces:

public class Disposable : IDisposable
    {
        private bool isDisposed;

        ~Disposable()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if (!isDisposed && disposing)
            {
                DisposeCore();
            }

            isDisposed = true;
        }

        // Ovveride this to dispose custom objects
        protected virtual void DisposeCore()
        {
        }
    }
public interface IDbFactory : IDisposable
    {
        HomeCinemaContext Init();
    }
public class DbFactory : Disposable, IDbFactory
    {
        HomeCinemaContext dbContext;

        public HomeCinemaContext Init()
        {
            return dbContext ?? (dbContext = new HomeCinemaContext());
        }

        protected override void DisposeCore()
        {
            if (dbContext != null)
                dbContext.Dispose();
        }
    }
public interface IUnitOfWork
    {
        void Commit();
    }
public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbFactory dbFactory;
        private HomeCinemaContext dbContext;

        public UnitOfWork(IDbFactory dbFactory)
        {
            this.dbFactory = dbFactory;
        }

        public HomeCinemaContext DbContext
        {
            get { return dbContext ?? (dbContext = dbFactory.Init()); }
        }

        public void Commit()
        {
            DbContext.Commit();
        }
    }

Time for the Generic Repository Pattern. We have seen this pattern many times in this blog but this time I will make a slight change. One of the blog’s readers asked me if he had to create a specific repository class that implements the generic repository T each time a need for a new type of repository is needed. Reader’s question was really good if you think that you may have hundred of Entities in a large scale application. The answer is NO and we will see it on action in this project where will try to inject repositories of type T as needed. Create a folder named Repositories and add the following interface with its implementation class:

public interface IEntityBaseRepository<T> where T : class, IEntityBase, new()
    {
        IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
        IQueryable<T> All { get; }
        IQueryable<T> GetAll();
        T GetSingle(int id);
        IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Edit(T entity);
    }
public class EntityBaseRepository<T> : IEntityBaseRepository<T>
            where T : class, IEntityBase, new()
    {

        private HomeCinemaContext dataContext;

        #region Properties
        protected IDbFactory DbFactory
        {
            get;
            private set;
        }

        protected HomeCinemaContext DbContext
        {
            get { return dataContext ?? (dataContext = DbFactory.Init()); }
        }
        public EntityBaseRepository(IDbFactory dbFactory)
        {
            DbFactory = dbFactory;
        }
        #endregion
        public virtual IQueryable<T> GetAll()
        {
            return DbContext.Set<T>();
        }
        public virtual IQueryable<T> All
        {
            get
            {
                return GetAll();
            }
        }
        public virtual IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = DbContext.Set<T>();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;
        }
        public T GetSingle(int id)
        {
            return GetAll().FirstOrDefault(x => x.ID == id);
        }
        public virtual IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
        {
            return DbContext.Set<T>().Where(predicate);
        }

        public virtual void Add(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry<T>(entity);
            DbContext.Set<T>().Add(entity);
        }
        public virtual void Edit(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Modified;
        }
        public virtual void Delete(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry<T>(entity);
            dbEntityEntry.State = EntityState.Deleted;
        }
    }

Before leaving this project and proceed with the Services one, there is only one thing remained to do. As I said when I was developing this application I was designing the database on my SQL Server and adding the respective Entities at the same time (yeap, you can do this as well..). No migrations where enabled. Yet, I thought that I should enable them in order to help you kick of the project and create the database automatically. For this you should do the same thing if you follow along with me. Open Package Manager Console, make sure you have selected the HomeCinema.Data project and type the following command:

enable-migrations

This will add a Configuration class inside a Migrations folder. This Configuration class has a seed method that is invoked when you create the database. The seed method I ‘ve written is a little bit large for pasting it here so please find it here. What I did is add data for Genres, Movies, Roles, Customers and Stocks. For customers I used a Nuget Package named MockData so make sure you install it too. More over I added a user with username chsakell and password homecinema. You can use them in order to login to our SPA. Otherwise you can just register a new user and sign in with those credentials. If you want to create the database right now, run the following commands from the Package Manager Console:

add-migration "initial_migration"
update-database -verbose

We ‘ll come up again to HomeCinema.Data project later to add some extension methods.

Membership

There is one middle layer between the Web application and the Data repositories and that’s the Service layer. In this application though, we will use this layer only for the membership’s requirements leaving all data repositories being injected as they are directly to API Controllers. Add a new class library project named HomeCinema.Services and make sure you add references to both of the other projects, HomeCinema.Data and HomeCinema.Entities. First, we ‘ll create a simple Encryption service to create salts and encrypted passwords and then we ‘ll use this service to implement a custom membership mechanism. Add a folder named Abstract and create the following interfaces.

public interface IEncryptionService
    {
        string CreateSalt();
        string EncryptPassword(string password, string salt);
    }
public interface IMembershipService
    {
        MembershipContext ValidateUser(string username, string password);
        User CreateUser(string username, string email, string password, int[] roles);
        User GetUser(int userId);
        List<Role> GetUserRoles(string username);
    }

At the root of this project add the EncryptionService implementation. It is a simple password encryption based on a salt and the SHA256 algorithm from System.Security.Cryptography namespace. Of course you can always use your own implementation algorithm.

public class EncryptionService : IEncryptionService
    {
        public string CreateSalt()
        {
            var data = new byte[0x10];
            using (var cryptoServiceProvider = new RNGCryptoServiceProvider())
            {
                cryptoServiceProvider.GetBytes(data);
                return Convert.ToBase64String(data);
            }
        }

        public string EncryptPassword(string password, string salt)
        {
            using (var sha256 = SHA256.Create())
            {
                var saltedPassword = string.Format("{0}{1}", salt, password);
                byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
                return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
            }
        }
    }

Let’s move to the Membership Service now. For start let’s see the base components of this class. Add the following class at the root of the project as well.

public class MembershipService : IMembershipService
    {
        #region Variables
        private readonly IEntityBaseRepository<User> _userRepository;
        private readonly IEntityBaseRepository<Role> _roleRepository;
        private readonly IEntityBaseRepository<UserRole> _userRoleRepository;
        private readonly IEncryptionService _encryptionService;
        private readonly IUnitOfWork _unitOfWork;
        #endregion

        public MembershipService(IEntityBaseRepository<User> userRepository, IEntityBaseRepository<Role> roleRepository,
        IEntityBaseRepository<UserRole> userRoleRepository, IEncryptionService encryptionService, IUnitOfWork unitOfWork)
        {
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _userRoleRepository = userRoleRepository;
            _encryptionService = encryptionService;
            _unitOfWork = unitOfWork;
        }
    }

Here we can see for the first time the way the generic repositories are going to be injected through the Autofac Inversion of Control Container. Before moving to the core implementation of this service we will have to add a User extension method in HomeCinema.Data and some helper methods in the previous class. Switch to HomeCinema.Data and add a new folder named Extensions. We will use this folder for adding Data repository extensions based on the Entity Set. Add the following User Entity extension method which retrieves a User instance based on its username.

public static class UserExtensions
    {
        public static User GetSingleByUsername(this IEntityBaseRepository<User> userRepository, string username)
        {
            return userRepository.GetAll().FirstOrDefault(x => x.Username == username);
        }
    }

Switch again to MembershipService class and add the following helper private methods.

#region Helper methods
        private void addUserToRole(User user, int roleId)
        {
            var role = _roleRepository.GetSingle(roleId);
            if (role == null)
                throw new ApplicationException("Role doesn't exist.");

            var userRole = new UserRole()
            {
                RoleId = role.ID,
                UserId = user.ID
            };
            _userRoleRepository.Add(userRole);
        }

        private bool isPasswordValid(User user, string password)
        {
            return string.Equals(_encryptionService.EncryptPassword(password, user.Salt), user.HashedPassword);
        }

        private bool isUserValid(User user, string password)
        {
            if (isPasswordValid(user, password))
            {
                return !user.IsLocked;
            }

            return false;
        }
        #endregion

Let’s view the CreateUser implementation method. The method checks if username already in use and if not creates the user.

public User CreateUser(string username, string email, string password, int[] roles)
        {
            var existingUser = _userRepository.GetSingleByUsername(username);

            if (existingUser != null)
            {
                throw new Exception("Username is already in use");
            }

            var passwordSalt = _encryptionService.CreateSalt();

            var user = new User()
            {
                Username = username,
                Salt = passwordSalt,
                Email = email,
                IsLocked = false,
                HashedPassword = _encryptionService.EncryptPassword(password, passwordSalt),
                DateCreated = DateTime.Now
            };

            _userRepository.Add(user);

            _unitOfWork.Commit();

            if (roles != null || roles.Length > 0)
            {
                foreach (var role in roles)
                {
                    addUserToRole(user, role);
                }
            }

            _unitOfWork.Commit();

            return user;
        }

The GetUser and GetUserRoles implementations are quite simple.

public User GetUser(int userId)
        {
            return _userRepository.GetSingle(userId);
        }
public List<Role> GetUserRoles(string username)
        {
            List<Role> _result = new List<Role>();

            var existingUser = _userRepository.GetSingleByUsername(username);

            if (existingUser != null)
            {
                foreach (var userRole in existingUser.UserRoles)
                {
                    _result.Add(userRole.Role);
                }
            }

            return _result.Distinct().ToList();
        }

I left the ValidateUser implementation last because is a little more complex than the others. You will have noticed from its interface that this service make use of a class named MembershipContext. This custom class is the one that will hold the IPrincipal object when authenticating users. When a valid user passes his/her credentials the service method will create an instance of GenericIdentity for user’s username. Then it will set the IPrincipal property using the GenericIdentity created and user’s roles. User roles information will be used to authorize API Controller’s actions based on logged in user’s roles. Let’s see the MembershipContext class and then the ValidateUser implementation.

public class MembershipContext
    {
        public IPrincipal Principal { get; set; }
        public User User { get; set; }
        public bool IsValid()
        {
            return Principal != null;
        }
    }
public MembershipContext ValidateUser(string username, string password)
        {
            var membershipCtx = new MembershipContext();

            var user = _userRepository.GetSingleByUsername(username);
            if (user != null && isUserValid(user, password))
            {
                var userRoles = GetUserRoles(user.Username);
                membershipCtx.User = user;

                var identity = new GenericIdentity(user.Username);
                membershipCtx.Principal = new GenericPrincipal(
                    identity,
                    userRoles.Select(x => x.Name).ToArray());
            }

            return membershipCtx;
        }

Check that MembershipContext class may hold additional information for the User. Add anything else you wish according to your needs.

Single Page Application

We are done building the core components for the HomeCinema Single Page Application, now it’s time to create the Web Application that will make use all of the previous parts we created. Add a new Empty Web Application project named HomeCinema.Web and make sure to check both the Web API and MVC check buttons. Add references to all the previous projects and install the following Nuget Packages:

Nuget Packages

  1. Entity Framework
  2. Autofac ASP.NET Web API 2.2 Integration
  3. Automapper
  4. FluentValidation

First thing we need to do is create any configurations we want to apply to our application. We ‘ll start with the Autofac Inversion of Control Container to work along with Web API framework. Next we will configure the bundling and last but not least, we will add the MessageHandler to configure the Basic Authentication. Add the following class inside the App_Start project’s folder.

public class AutofacWebapiConfig
    {
        public static IContainer Container;
        public static void Initialize(HttpConfiguration config)
        {
            Initialize(config, RegisterServices(new ContainerBuilder()));
        }

        public static void Initialize(HttpConfiguration config, IContainer container)
        {
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        }

        private static IContainer RegisterServices(ContainerBuilder builder)
        {
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // EF HomeCinemaContext
            builder.RegisterType<HomeCinemaContext>()
                   .As<DbContext>()
                   .InstancePerRequest();

            builder.RegisterType<DbFactory>()
                .As<IDbFactory>()
                .InstancePerRequest();

            builder.RegisterType<UnitOfWork>()
                .As<IUnitOfWork>()
                .InstancePerRequest();

            builder.RegisterGeneric(typeof(EntityBaseRepository<>))
                   .As(typeof(IEntityBaseRepository<>))
                   .InstancePerRequest();

            // Services
            builder.RegisterType<EncryptionService>()
                .As<IEncryptionService>()
                .InstancePerRequest();

            builder.RegisterType<MembershipService>()
                .As<IMembershipService>()
                .InstancePerRequest();

            Container = builder.Build();

            return Container;
        }
    }

This will make sure dependencies will be injected in constructors as expected. Since we are in the App_Start folder let’s configure the Bundling in our application. Add the following class in App_Start folder as well.

public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                "~/Scripts/Vendors/modernizr.js"));

            bundles.Add(new ScriptBundle("~/bundles/vendors").Include(
                "~/Scripts/Vendors/jquery.js",
                "~/Scripts/Vendors/bootstrap.js",
                "~/Scripts/Vendors/toastr.js",
                "~/Scripts/Vendors/jquery.raty.js",
                "~/Scripts/Vendors/respond.src.js",
                "~/Scripts/Vendors/angular.js",
                "~/Scripts/Vendors/angular-route.js",
                "~/Scripts/Vendors/angular-cookies.js",
                "~/Scripts/Vendors/angular-validator.js",
                "~/Scripts/Vendors/angular-base64.js",
                "~/Scripts/Vendors/angular-file-upload.js",
                "~/Scripts/Vendors/angucomplete-alt.min.js",
                "~/Scripts/Vendors/ui-bootstrap-tpls-0.13.1.js",
                "~/Scripts/Vendors/underscore.js",
                "~/Scripts/Vendors/raphael.js",
                "~/Scripts/Vendors/morris.js",
                "~/Scripts/Vendors/jquery.fancybox.js",
                "~/Scripts/Vendors/jquery.fancybox-media.js",
                "~/Scripts/Vendors/loading-bar.js"
                ));

            bundles.Add(new ScriptBundle("~/bundles/spa").Include(
                "~/Scripts/spa/modules/common.core.js",
                "~/Scripts/spa/modules/common.ui.js",
                "~/Scripts/spa/app.js",
                "~/Scripts/spa/services/apiService.js",
                "~/Scripts/spa/services/notificationService.js",
                "~/Scripts/spa/services/membershipService.js",
                "~/Scripts/spa/services/fileUploadService.js",
                "~/Scripts/spa/layout/topBar.directive.js",
                "~/Scripts/spa/layout/sideBar.directive.js",
                "~/Scripts/spa/layout/customPager.directive.js",
                "~/Scripts/spa/directives/rating.directive.js",
                "~/Scripts/spa/directives/availableMovie.directive.js",
                "~/Scripts/spa/account/loginCtrl.js",
                "~/Scripts/spa/account/registerCtrl.js",
                "~/Scripts/spa/home/rootCtrl.js",
                "~/Scripts/spa/home/indexCtrl.js",
                "~/Scripts/spa/customers/customersCtrl.js",
                "~/Scripts/spa/customers/customersRegCtrl.js",
                "~/Scripts/spa/customers/customerEditCtrl.js",
                "~/Scripts/spa/movies/moviesCtrl.js",
                "~/Scripts/spa/movies/movieAddCtrl.js",
                "~/Scripts/spa/movies/movieDetailsCtrl.js",
                "~/Scripts/spa/movies/movieEditCtrl.js",
                "~/Scripts/spa/controllers/rentalCtrl.js",
                "~/Scripts/spa/rental/rentMovieCtrl.js",
                "~/Scripts/spa/rental/rentStatsCtrl.js"
                ));

            bundles.Add(new StyleBundle("~/Content/css").Include(
                "~/content/css/site.css",
                "~/content/css/bootstrap.css",
                "~/content/css/bootstrap-theme.css",
                 "~/content/css/font-awesome.css",
                "~/content/css/morris.css",
                "~/content/css/toastr.css",
                "~/content/css/jquery.fancybox.css",
                "~/content/css/loading-bar.css"));

            BundleTable.EnableOptimizations = false;
        }
    }

You may wonder where the heck will I find all these files? Do not worry about that, i will provide you links for all the static JavaScript libraries inside the ~/bundles/vendors bundle and the CSS stylesheets in the ~/Content/css one as well. All JavaScript files in the ~/bundles/spa bundle are the angularJS components we are going to build. Don’t forget that as always the source code for this application will be available at the end of this post. Add the Bootstrapper class in the App_Start folder. We will call its Run method from the Global.asax ApplicationStart method. For now let the Automapper’s part commented out and we will un-comment it when the time comes.

public class Bootstrapper
    {
        public static void Run()
        {
            // Configure Autofac
            AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
            //Configure AutoMapper
            //AutoMapperConfiguration.Configure();
        }
    }

Change Global.asax.cs (add it if not exists) as follow:

public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            var config = GlobalConfiguration.Configuration;

            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(config);
            Bootstrapper.Run();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            GlobalConfiguration.Configuration.EnsureInitialized();
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

Not all pages will be accessible to unauthenticated users as opposed from application requirements and for this reason we are going to use Basic Authentication. This will be done through a Message Handler whose job is to search for an Authorization header in the request. Create a folder named Infrastructure at the root of the web application project and add a sub-folder named MessageHandlers. Create the following handler.

public class HomeCinemaAuthHandler : DelegatingHandler
    {
        IEnumerable<string> authHeaderValues = null;
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try
            {
                request.Headers.TryGetValues("Authorization",out authHeaderValues);
                if(authHeaderValues == null)
                    return base.SendAsync(request, cancellationToken); // cross fingers

                var tokens = authHeaderValues.FirstOrDefault();
                tokens = tokens.Replace("Basic","").Trim();
                if (!string.IsNullOrEmpty(tokens))
                {
                    byte[] data = Convert.FromBase64String(tokens);
                    string decodedString = Encoding.UTF8.GetString(data);
                    string[] tokensValues = decodedString.Split(':');
                    var membershipService = request.GetMembershipService();

                    var membershipCtx = membershipService.ValidateUser(tokensValues[0], tokensValues[1]);
                    if (membershipCtx.User != null)
                    {
                        IPrincipal principal = membershipCtx.Principal;
                        Thread.CurrentPrincipal = principal;
                        HttpContext.Current.User = principal;
                    }
                    else // Unauthorized access - wrong crededentials
                    {
                        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                        var tsc = new TaskCompletionSource<HttpResponseMessage>();
                        tsc.SetResult(response);
                        return tsc.Task;
                    }
                }
                else
                {
                    var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                    var tsc = new TaskCompletionSource<HttpResponseMessage>();
                    tsc.SetResult(response);
                    return tsc.Task;
                }
                return base.SendAsync(request, cancellationToken);
            }
            catch
            {
                var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                var tsc = new TaskCompletionSource<HttpResponseMessage>();
                tsc.SetResult(response);
                return tsc.Task;
            }
        }
    }

The GetMembershipService() is an HttpRequestMessage extension which I will provide right away. Notice that we ‘ve made some decisions in this handler. When a request dispatches, the handler searches for an Authorization header. If it doesn’t find one then we cross our fingers and let the next level decide if the request is accessible or not. This means that if a Web API Controller’s action has the AllowAnonymous attribute the request doesn’t have to hold an Authorization header. One the other hand if the action did have an Authorize attribute then an Unauthorized response message would be returned in case of empty Authorization header. If Authorization header is present, the membership service decodes the based64 encoded credentials and checks their validity. User and role information is saved in the HttpContext.Current.User object.
spa-webapi-angular-16
As far as the HttpRequestMessage extension add a folder named Extensions inside the Infrastructure folder and create the following class.

public static class RequestMessageExtensions
    {
        internal static IMembershipService GetMembershipService(this HttpRequestMessage request)
        {
            return request.GetService<IMembershipService>();
        }

        private static TService GetService<TService>(this HttpRequestMessage request)
        {
            IDependencyScope dependencyScope = request.GetDependencyScope();
            TService service = (TService)dependencyScope.GetService(typeof(TService));

            return service;
        }
    }

Now switch to the WebApiConfig.cs inside the App_Start folder and register the authentication message handler.

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            config.MessageHandlers.Add(new HomeCinemaAuthHandler());

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

Don’t forget to add the same connection string you added in the HomeCinema.Data App.config file, in the Web.config configuration file.

Static CSS files and images

We configured previously the bundling but at this point you don’t have all the necessary files and their respective folders in your application. Add a Content folder at the root of the web application and create the following sub-folders in it.

CSS – Fonts – Images

  1. Content/css: Bootstrap, Font-awesome, fancy-box, morris, toastr, homecinema relative css files
  2. Content/fonts: Bootstrap and font-awesome required fonts
  3. Content/images
  4. Content/images/movies: Hold’s application’s movies
  5. Content/images/raty: Raty.js required images

You can find and download all these static files from here.

Vendors – 3rd party libraries

Create a Scripts folder in application’s root and a two sub-folders, Scripts/spa and Scripts/vendors. Each of those libraries solves a specific requirement and it was carefully picked. Before providing you some basic information for most of them, let me point out something important.

Always pick 3rd libraries carefully. When searching for a specific component you may find several implementations for it out on the internet. One thing to care about is do not end up with a bazooka while trying to kill a mosquito. For example, in this application we need modal popups. If you search on the internet you will find various implementations but most of these are quite complex. All we need is a modal window so for this I decided that the $modal service of angularJS UI-bootstrap is more than enough to do the job. Another thing that you should do is always check for any opened issues for the 3rd library. Those libraries are usually hosted on Github and there is an Issues section that you can view. Check if any performance or memory leaks issue exists before decide to attach any library to your project.

3rd party libraries

  1. toastr.js: A non-blocking notification library
  2. jquery.raty.js: A star rating plug-in
  3. angular-validator.js: A light weighted validation directive
  4. angular-base64.js: Base64 encode-decode library
  5. angular-file-upload.js: A file-uploading library
  6. angucomplete-alt.min.js: Auto-complete search directive
  7. ui-bootstrap-tpls-0.13.1.js: Native AngularJS (Angular) directives for Bootstrap
  8. morris.js: Charts library
  9. jquery.fancybox.js: Tool for displaying images

These are the most important libraries we will be using but you may find some other files too. Download all those files from here. As I mentioned, generally you should use Bower and Grunt or Gulp for resolving web dependencies so let me provide you some installation commands for the above libraries:

bower install angucomplete-alt --save
bower install angular-base64
bower install angular-file-upload
bower install tg-angular-validator
bower install bootstrap
bower install raty
bower install angular-loading-bar
bower install angular-bootstrap

The ng-view

Now that we are done configurating the Single Page Application it’s time to create its initial page, the page that will be used as the parent of all different views and templates in our application. Inside the Controllers folder, create an MVC Controller named HomeController as follow. Right click inside its Index method and create a new view with the respective name.

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

Alter the Views/Home/Index.cshtml file as follow:

@{
    Layout = null;
}

<!DOCTYPE html>

<html ng-app="homeCinema">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home Cinema</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body ng-controller="rootCtrl">
    <top-bar></top-bar>
    <div class="row-offcanvas row-offcanvas-left">
        <side-bar></side-bar>
        <div id="main">
            <div class="col-md-12">
                <p class="visible-xs">
                    <button type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas"><i class="glyphicon glyphicon-chevron-left"></i></button>
                </p>
                <div class="page {{ pageClass }}" ng-view></div>
            </div>
        </div>
    </div><!--/row-offcanvas -->
    @Scripts.Render("~/bundles/vendors")
    @Scripts.Render("~/bundles/spa")

</body>
</html>

Let’s explain one by one the highlighted lines, from top to bottom. The main module in our spa will be named homeCinema. This module will have two other modules as dependencies, the Common.core and the Common.UI which we will create later. Next we render the CSS bundles we created before. There will be one root-parent controller in the application named rootCtrl. The top-bar and side-bar elements are custom angularJS directives what will build for the top and side bar collapsible menus respectively. We use a page-class $scope variable to change the style in a rendered template. This is a nice trick to render different styles in ng-view templates. Last but not least, we render the vendors and spa JavaScript bundles.

From what we have till now, it’s obvious that first we should create the homeCinema module, the rootCtrl controller then the two custom directives for this initial page to be rendered. Instead of doing this, we ‘ll take a step back and prepare some basic angularJS components, the Common.core and Common.ui modules. Add a Modules folder in the Scripts/spa folder and create the following two files.

(function () {
    'use strict';

    angular.module('common.ui', ['ui.bootstrap', 'chieffancypants.loadingBar']);

})();
(function () {
	'use strict';

	angular.module('common.core', ['ngRoute', 'ngCookies', 'base64', 'angularFileUpload', 'angularValidator', 'angucomplete-alt']);

})();

Let Common.ui module be a UI related reusable component through our SPA application and Common.core a core functional one. We could just inject all those dependencies directly to the homeCinema module but that would require to do the same in case we wanted to scale the application as we ‘ll discuss later. Pay some attention the way we created the modules. We will be using this pattern a lot for not polluting the global JavaScript’s namespace. At the root of the spa folder add the following app.js file and define the core homeCinema module and its routes.

(function () {
    'use strict';

    angular.module('homeCinema', ['common.core', 'common.ui'])
        .config(config);

    config.$inject = ['$routeProvider'];
    function config($routeProvider) {
        $routeProvider
            .when("/", {
                templateUrl: "scripts/spa/home/index.html",
                controller: "indexCtrl"
            })
            .when("/login", {
                templateUrl: "scripts/spa/account/login.html",
                controller: "loginCtrl"
            })
            .when("/register", {
                templateUrl: "scripts/spa/account/register.html",
                controller: "registerCtrl"
            })
            .when("/customers", {
                templateUrl: "scripts/spa/customers/customers.html",
                controller: "customersCtrl"
            })
            .when("/customers/register", {
                templateUrl: "scripts/spa/customers/register.html",
                controller: "customersRegCtrl"
            })
            .when("/movies", {
                templateUrl: "scripts/spa/movies/movies.html",
                controller: "moviesCtrl"
            })
            .when("/movies/add", {
                templateUrl: "scripts/spa/movies/add.html",
                controller: "movieAddCtrl"
            })
            .when("/movies/:id", {
                templateUrl: "scripts/spa/movies/details.html",
                controller: "movieDetailsCtrl"
            })
            .when("/movies/edit/:id", {
                templateUrl: "scripts/spa/movies/edit.html",
                controller: "movieEditCtrl"
            })
            .when("/rental", {
                templateUrl: "scripts/spa/rental/rental.html",
                controller: "rentStatsCtrl"
            }).otherwise({ redirectTo: "/" });
    }

})();

Once again take a look at the explicit service injection using the angularJS property annotation $inject which allows the minifiers to rename the function parameters and still be able to inject the right services. You probably don’t have all the referenced files in the previous script (except if you have downloaded the source code) but that’s OK. We ‘ll create all of those one by one. It is common practice to place any angularJS components related to application’s layout in a layout folder so go ahead and create this folder. For the side-bar element directive we used we need two files, one for the directive definition and another for its template. Add the following two files to the layout folder you created.

(function(app) {
    'use strict';

    app.directive('sideBar', sideBar);

    function sideBar() {
        return {
            restrict: 'E',
            replace: true,
            templateUrl: '/scripts/spa/layout/sideBar.html'
        }
    }

})(angular.module('common.ui'));
<div id="sidebar" class="sidebar-offcanvas">
    <div class="col-md-12">
        <h3></h3>
        <ul class="nav nav-pills nav-stacked">
            <li class="nav-divider"></li>
            <li class=""><a ng-href="#/">Home<i class="fa fa-home fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/customers/">Customers<i class="fa fa-users fa-fw pull-right"></i></a></li>
            <li><a ng-href="#/customers/register">Register customer<i class="fa fa-user-plus fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/movies/">Movies<i class="fa fa-film fa-fw pull-right"></i></a></li>
            <li><a ng-href="#/movies/add">Add movie<i class="fa fa-plus-circle fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/rental/">Rental history<i class="fa fa-leanpub fa-fw pull-right"></i></a></li>
            <li class="nav-divider"></li>
            <li><a ng-href="#/login" ng-if="!userData.isUserLoggedIn">Login<i class="fa fa-sign-in fa-fw pull-right"></i></a></li>
            <li><button type="button" class="btn btn-danger btn-sm" ng-click="logout();" ng-if="userData.isUserLoggedIn">Logout<i class="fa fa-sign-out fa-fw pull-right"></i></a></li>
        </ul>
    </div>
</div>

This directive will create a collapsible side-bar such as the following when applied.
spa-webapi-angular-17
Let’s see the top-bar directive and its template as well.

(function(app) {
    'use strict';

    app.directive('topBar', topBar);

    function topBar() {
        return {
            restrict: 'E',
            replace: true,
            templateUrl: '/scripts/spa/layout/topBar.html'
        }
    }

})(angular.module('common.ui'));
<div class="navbar navbar-default navbar-fixed-top">
    <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand active" href="#/">Home Cinema</a>
    </div>
    <div class="collapse navbar-collapse">
        <ul class="nav navbar-nav">
            <li><a href="#about">About<i class="fa fa-info-circle fa-fw pull-right"></i></a></li>
        </ul>
        <ul class="nav navbar-nav navbar-right" ng-if="userData.isUserLoggedIn">
            <li class="userinfo"><a href="#/">{{username}}<i class="fa fa-user fa-fw"></i></a></li>
        </ul>
    </div>
</div>

You may have noticed I highlighted two lines in the side-bar template’s code. Those lines are responsible to show or hide the login and log-off buttons respectively depending if the user is logged in or not. The required functionality will be place at the rootCtrl Controller inside a home folder.

(function (app) {
    'use strict';

    app.controller('rootCtrl', rootCtrl);

    function rootCtrl($scope) {

        $scope.userData = {};

        $scope.userData.displayUserInfo = displayUserInfo;
        $scope.logout = logout;


        function displayUserInfo() {

        }

        function logout() {

        }
    }

})(angular.module('homeCinema'));

We will update its contents as soon as we create the membership service. All of our views require to fetch data from the server and for that a specific apiService will be used through our application. This service will also be able to display some kind of notifications to the user so let’s build a notificationService as well. Create a services folder under the spa and add the following angularJS factory services.

(function (app) {
    'use strict';

    app.factory('notificationService', notificationService);

    function notificationService() {

        toastr.options = {
            "debug": false,
            "positionClass": "toast-top-right",
            "onclick": null,
            "fadeIn": 300,
            "fadeOut": 1000,
            "timeOut": 3000,
            "extendedTimeOut": 1000
        };

        var service = {
            displaySuccess: displaySuccess,
            displayError: displayError,
            displayWarning: displayWarning,
            displayInfo: displayInfo
        };

        return service;

        function displaySuccess(message) {
            toastr.success(message);
        }

        function displayError(error) {
            if (Array.isArray(error)) {
                error.forEach(function (err) {
                    toastr.error(err);
                });
            } else {
                toastr.error(error);
            }
        }

        function displayWarning(message) {
            toastr.warning(message);
        }

        function displayInfo(message) {
            toastr.info(message);
        }

    }

})(angular.module('common.core'));

The notificationService is based on the toastr.js notification library. It displays different type (style class) of notifications depending on the method invoked, that is success, error, warning and info.

(function (app) {
    'use strict';

    app.factory('apiService', apiService);

    apiService.$inject = ['$http', '$location', 'notificationService','$rootScope'];

    function apiService($http, $location, notificationService, $rootScope) {
        var service = {
            get: get,
            post: post
        };

        function get(url, config, success, failure) {
            return $http.get(url, config)
                    .then(function (result) {
                        success(result);
                    }, function (error) {
                        if (error.status == '401') {
                            notificationService.displayError('Authentication required.');
                            $rootScope.previousState = $location.path();
                            $location.path('/login');
                        }
                        else if (failure != null) {
                            failure(error);
                        }
                    });
        }

        function post(url, data, success, failure) {
            return $http.post(url, data)
                    .then(function (result) {
                        success(result);
                    }, function (error) {
                        if (error.status == '401') {
                            notificationService.displayError('Authentication required.');
                            $rootScope.previousState = $location.path();
                            $location.path('/login');
                        }
                        else if (failure != null) {
                            failure(error);
                        }
                    });
        }

        return service;
    }

})(angular.module('common.core'));

The apiService is quite straight forward. It defines a factory with two basic methods, GET and POST. Both of these methods can handle 401 errors by redirecting the user at the login view and saving the previous state so that after a successful login, the user gets back where he/she was. They also accept a required success callback to invoke and an optional failure one in case of a failed request. Our spa application is pretty much ready to fetch and post data from the server so this is the right time to write the first Web API controller.

Web API

We ‘ll try to apply some basic rules for all of Web API Controllers in our application. The first one is that all of them will inherit from a base class named ApiControllerBase. The basic responsibility of this class will be handling the Error logging functionality. That’s the only class where instances of IEntityBaseRepository<Error> will be injected with the centralized Try, Catch point we talked about at the start of this post. This class of course will inherit from the ApiController. Create a folder named core inside the Infrastructure and create the base class for our controllers.

 public class ApiControllerBase : ApiController
    {
        protected readonly IEntityBaseRepository<Error> _errorsRepository;
        protected readonly IUnitOfWork _unitOfWork;

        public ApiControllerBase(IEntityBaseRepository<Error> errorsRepository, IUnitOfWork unitOfWork)
        {
            _errorsRepository = errorsRepository;
            _unitOfWork = unitOfWork;
        }

        protected HttpResponseMessage CreateHttpResponse(HttpRequestMessage request, Func<HttpResponseMessage> function)
        {
            HttpResponseMessage response = null;

            try
            {
                response = function.Invoke();
            }
            catch (DbUpdateException ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.BadRequest, ex.InnerException.Message);
            }
            catch (Exception ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }

            return response;
        }

        private void LogError(Exception ex)
        {
            try
            {
                Error _error = new Error()
                {
                    Message = ex.Message,
                    StackTrace = ex.StackTrace,
                    DateCreated = DateTime.Now
                };

                _errorsRepository.Add(_error);
                _unitOfWork.Commit();
            }
            catch { }
        }
    }

You will surprised how powerful the CreateHttpResponse function can be when we reach the discussion section. Notice that this method can handle a DbUpdateException exception as well. You can omit this type if you want and write more custom methods such as this. Each controller’s action will start by calling this base method.

If you recall, the home’s page displays the latest movies released plus some genre statistics on the right. Let’s start from the latest movies. First thing we need to do is create a ViewModel for Movie entities. For each type of Entity we ‘ll create the respective ViewModel for the client. All ViewModels will have the relative validation rules based on the FluentValidation Nuget package. Add the MovieViewModel and GenreViewModel classes inside the Models folder.

    [Bind(Exclude = "Image")]
    public class MovieViewModel : IValidatableObject
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Image { get; set; }
        public string  Genre { get; set; }
        public int GenreId { get; set; }
        public string Director { get; set; }
        public string Writer { get; set; }
        public string Producer { get; set; }
        public DateTime ReleaseDate { get; set; }
        public byte Rating { get; set; }
        public string TrailerURI { get; set; }
        public bool IsAvailable { get; set; }
        public int NumberOfStocks { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new MovieViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }

We excluded the Image property from MovieViewModel binding cause we will be using a specific FileUpload action to upload images.

public class GenreViewModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int NumberOfMovies { get; set; }
    }

Create a Validators folder inside the Infrastructure and the MovieViewModelValidator.

public class MovieViewModelValidator : AbstractValidator<MovieViewModel>
    {
        public MovieViewModelValidator()
        {
            RuleFor(movie => movie.GenreId).GreaterThan(0)
                .WithMessage("Select a Genre");

            RuleFor(movie => movie.Director).NotEmpty().Length(1,100)
                .WithMessage("Select a Director");

            RuleFor(movie => movie.Writer).NotEmpty().Length(1,50)
                .WithMessage("Select a writer");

            RuleFor(movie => movie.Producer).NotEmpty().Length(1, 50)
                .WithMessage("Select a producer");

            RuleFor(movie => movie.Description).NotEmpty()
                .WithMessage("Select a description");

            RuleFor(movie => movie.Rating).InclusiveBetween((byte)0, (byte)5)
                .WithMessage("Rating must be less than or equal to 5");

            RuleFor(movie => movie.TrailerURI).NotEmpty().Must(ValidTrailerURI)
                .WithMessage("Only Youtube Trailers are supported");
        }

        private bool ValidTrailerURI(string trailerURI)
        {
            return (!string.IsNullOrEmpty(trailerURI) && trailerURI.ToLower().StartsWith("https://www.youtube.com/watch?"));
        }
    }

Now that we have our first ViewModels and its a validator setup, we can configure the Automapper mappings as well. Add a Mappings folder inside the Infrastructure and create the following DomainToViewModelMappingProfile Profile class.

public class DomainToViewModelMappingProfile : Profile
    {
        public override string ProfileName
        {
            get { return "DomainToViewModelMappings"; }
        }

        protected override void Configure()
        {
            Mapper.CreateMap<Movie, MovieViewModel>()
                .ForMember(vm => vm.Genre, map => map.MapFrom(m => m.Genre.Name))
                .ForMember(vm => vm.GenreId, map => map.MapFrom(m => m.Genre.ID))
                .ForMember(vm => vm.IsAvailable, map => map.MapFrom(m => m.Stocks.Any(s => s.IsAvailable)))
                .ForMember(vm => vm.NumberOfStocks, map => map.MapFrom(m => m.Stocks.Count))
                .ForMember(vm => vm.Image, map => map.MapFrom(m => string.IsNullOrEmpty(m.Image) == true ? "unknown.jpg" : m.Image));

            Mapper.CreateMap<Genre, GenreViewModel>()
                .ForMember(vm => vm.NumberOfMovies, map => map.MapFrom(g => g.Movies.Count()));
        }
    }

Notice how we set if a Movie (ViewModel) is available or not by checking if any of its stocks is available. Add Automapper’s configuration class and make sure to comment out the respective line in the Bootstrapper class.

public class AutoMapperConfiguration
    {
        public static void Configure()
        {
            Mapper.Initialize(x =>
            {
                x.AddProfile<DomainToViewModelMappingProfile>();
            });
        }
    }
public static void Run()
        {
            // Configure Autofac
            AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
            //Configure AutoMapper
            AutoMapperConfiguration.Configure();
        }

We have done so much preparation and now it is time to implement and view all application requirements in practice. We will start with the Home page.

The Home page

The home page displays information about the latest DVD movie released plus some Genre statistics. For the first feature will start from creating the required Web API controller, MoviesController. Add this class inside the Controllers folder.

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/movies")]
    public class MoviesController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Movie> _moviesRepository;

        public MoviesController(IEntityBaseRepository<Movie> moviesRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _moviesRepository = moviesRepository;
        }

        [AllowAnonymous]
        [Route("latest")]
        public HttpResponseMessage Get(HttpRequestMessage request)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                var movies = _moviesRepository.GetAll().OrderByDescending(m => m.ReleaseDate).Take(6).ToList();

                IEnumerable<MovieViewModel> moviesVM = Mapper.Map<IEnumerable<Movie>, IEnumerable<MovieViewModel>>(movies);

                response = request.CreateResponse<IEnumerable<MovieViewModel>>(HttpStatusCode.OK, moviesVM);

                return response;
            });
        }
    }

Let’s explain the highlighted lines from top to bottom. All actions for this Controller required the user not only to be authenticated but also belong to Admin role, except if AllowAnonymous attribute is applied. All requests to this controller will start with a prefix of api/movies. The error handling as already explained is handled from the base class ApiControllerBase and its method CreateHttpResponse. Here we can see for the first time how this method is actually called. Add the GenresController as well.

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/genres")]
    public class GenresController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Genre> _genresRepository;

        public GenresController(IEntityBaseRepository<Genre> genresRepository,
             IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _genresRepository = genresRepository;
        }

        [AllowAnonymous]
        public HttpResponseMessage Get(HttpRequestMessage request)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                var genres = _genresRepository.GetAll().ToList();

                IEnumerable<GenreViewModel> genresVM = Mapper.Map<IEnumerable<Genre>, IEnumerable<GenreViewModel>>(genres);

                response = request.CreateResponse<IEnumerable<GenreViewModel>>(HttpStatusCode.OK, genresVM);

                return response;
            });
        }
    }

We prepared the server side part, let’s move on to its JavaScript one now. If you recall we ‘ll follow a structure by feature in our spa, so we will place the two required files for the home page inside the spa/home folder. We need two files, one template and the respective controller. Let’s see the template first.

<hr />
<div class="row">
    <div class="col-md-8">
        <div class="panel panel-primary" id="panelLatestMovies">
            <div class="panel-heading">
                <h3 class="panel-title">Latest Movies Released</h3>
            </div>

            <div class="panel-body">
                <div ng-if="loadingMovies">
                    <div class="col-xs-4"></div>
                    <div class="col-xs-4">
                        <i class="fa fa-refresh fa-5x fa-spin"></i> <label class="label label-primary">Loading movies...</label>
                    </div>
                    <div class="col-xs-4"></div>
                </div>
                <div class="col-xs-12 col-sm-6 movie" ng-repeat="movie in latestMovies">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <strong>{{movie.Title}} </strong>
                        </div>
                        <div class="panel-body">
                            <div class="media">
                                <a class="pull-left" href="#">
                                    <a class="fancybox pull-left" rel="gallery1" ng-href="../../Content/images/movies/{{movie.Image}}" title="{{movie.Description | limitTo:200}}">
                                        <img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
                                    </a>

                                </a>
                                <div class="media-body">
                                    <available-movie is-available="{{movie.IsAvailable}}"></available-movie>
                                    <div><small>{{movie.Description | limitTo: 70}}...</small></div>
                                    <label class="label label-info">{{movie.Genre}}</label><br />
                                </div>
                                <br />
                            </div>
                        </div>
                        <div class="panel-footer">
                            <span component-rating="{{movie.Rating}}"></span>
                            <a class="fancybox-media pull-right" ng-href="{{movie.TrailerURI}}">Trailer<i class="fa fa-video-camera fa-fw"></i></a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="panel panel-success" id="panelMovieGenres">
            <div class="panel-heading">
                <h3 class="panel-title">Movies Genres</h3>
            </div>

            <div class="panel-body">
                <div ng-if="loadingGenres">
                    <div class="col-xs-4"></div>
                    <div class="col-xs-4"><i class="fa fa-refresh fa-5x fa-spin"></i> <label class="label label-primary">Loading Genres..</label></div>
                    <div class="col-xs-4"></div>
                </div>
                <div id="genres-bar"></div>
            </div>
            <div class="panel-footer">
                <p class="text-center"><em>Wanna add a new Movie? Head over to the add movie form.</em></p>
                <p class="text-center"><a ng-href="#/movies/add" class="btn btn-default">Add new Movie</a></p>
            </div>
        </div>
    </div>
</div>

I made a convention that the first view rendered for each template will be named index.html. This means that you will see later the movies/index.html, rental/index.html etc.. We use ng-if angularJS directive to display a loader (spinner if you prefer) till server side data retrieved from the server. Let’s see now the controller that binds the data to the template, the indexCtrl. Add the following file to the home folder as well.

(function (app) {
    'use strict';

    app.controller('indexCtrl', indexCtrl);

    indexCtrl.$inject = ['$scope', 'apiService', 'notificationService'];

    function indexCtrl($scope, apiService, notificationService) {
        $scope.pageClass = 'page-home';
        $scope.loadingMovies = true;
        $scope.loadingGenres = true;
        $scope.isReadOnly = true;

        $scope.latestMovies = [];
        $scope.loadData = loadData;

        function loadData() {
            apiService.get('/api/movies/latest', null,
                        moviesLoadCompleted,
                        moviesLoadFailed);

            apiService.get("/api/genres/", null,
                genresLoadCompleted,
                genresLoadFailed);
        }

        function moviesLoadCompleted(result) {
            $scope.latestMovies = result.data;
            $scope.loadingMovies = false;
        }

        function genresLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function moviesLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function genresLoadCompleted(result) {
            var genres = result.data;
            Morris.Bar({
                element: "genres-bar",
                data: genres,
                xkey: "Name",
                ykeys: ["NumberOfMovies"],
                labels: ["Number Of Movies"],
                barRatio: 0.4,
                xLabelAngle: 55,
                hideHover: "auto",
                resize: 'true'
            });

            $scope.loadingGenres = false;
        }

        loadData();
    }

})(angular.module('homeCinema'));

The loadData() function requests movie and genre data from the respective Web API controllers we previously created. For the movie data only thing needed to do is bind the requested data to a $scope.latestMovies variable. For the genres data thought, we used genres retrieved data and a specific div element, genres-bar to create a Morris bar.

Movie Directives

In case you noticed, the index.html template has two custom directives. One to render if the movie is available and another one to display its rating through the raty.js library. Those two directives will be used over and over again through our application so let’s take a look at them.

The first one is responsible to render a label element that may be red or green depending if the a movie is available or not. Since those directives can be used all over the application we ‘ll place their components inside a directives folder, so go ahead and add this folder under the spa. Create a availableMovie.html file which will be the template for the new directive.

<label ng-class="getAvailableClass()">{{getAvailability()}}</label>

Now add the directive definition, availableMovie.directive.js

(function (app) {
	'use strict';

	app.directive('availableMovie', availableMovie);

	function availableMovie() {
		return {
			restrict: 'E',
			templateUrl: "/Scripts/spa/directives/availableMovie.html",
			link: function ($scope, $element, $attrs) {
				$scope.getAvailableClass = function () {
					if ($attrs.isAvailable === 'true')
						return 'label label-success'
					else
						return 'label label-danger'
				};
				$scope.getAvailability = function () {
					if ($attrs.isAvailable === 'true')
						return 'Available!'
					else
						return 'Not Available'
				};
			}
		}
	}

})(angular.module('common.ui'));

spa-webapi-angular-18
The component-rating which displays a star based rating element, is slightly different in terms of restriction, since it is used as an element, not as an attribute. I named it component-rating cause you may want to use it to rate entities other than movies. When you want to render the rating directive all you have to do is create the following element.

<span component-rating="{{movie.Rating}}"></span>

The movie.Rating will hold the rating value. Let’s see the directive’s definition. Place the following file in the directives folder as well.

(function(app) {
    'use strict';

    app.directive('componentRating', componentRating);

    function componentRating() {
        return {
            restrict: 'A',
            link: function ($scope, $element, $attrs) {
                $element.raty({
                    score: $attrs.componentRating,
                    halfShow: false,
                    readOnly: $scope.isReadOnly,
                    noRatedMsg: "Not rated yet!",
                    starHalf: "../Content/images/raty/star-half.png",
                    starOff: "../Content/images/raty/star-off.png",
                    starOn: "../Content/images/raty/star-on.png",
                    hints: ["Poor", "Average", "Good", "Very Good", "Excellent"],
                    click: function (score, event) {
                        //Set the model value
                        $scope.movie.Rating = score;
                        $scope.$apply();
                    }
                });
            }
        }
    }

})(angular.module('common.ui'));

One important thing the directive needs to know is if the rating element will be editable or not and that is configured through the readOnly: $scope.isReadOnly definition. For the home/index.html we want the rating to be read-only so the controller has the following declaration:

$scope.isReadOnly = true;

Any other controller that requires to edit movie’s rating value will set this value to false.

Account

One of the most important parts in every application is how users are getting authenticated in order to access authorized resources. We certainly built a custom membership schema and add an Basic Authentication message handler in Web API, but we haven’t yet created neither the required Web API AccountController or the relative angularJS component. Let’s start with the server side first and view the AccountController.

    [Authorize(Roles="Admin")]
    [RoutePrefix("api/Account")]
    public class AccountController : ApiControllerBase
    {
        private readonly IMembershipService _membershipService;

        public AccountController(IMembershipService membershipService,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _membershipService = membershipService;
        }

        [AllowAnonymous]
        [Route("authenticate")]
        [HttpPost]
        public HttpResponseMessage Login(HttpRequestMessage request, LoginViewModel user)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (ModelState.IsValid)
                {
                    MembershipContext _userContext = _membershipService.ValidateUser(user.Username, user.Password);

                    if (_userContext.User != null)
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = true });
                    }
                    else
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = false });
                    }
                }
                else
                    response = request.CreateResponse(HttpStatusCode.OK, new { success = false });

                return response;
            });
        }

        [AllowAnonymous]
        [Route("register")]
        [HttpPost]
        public HttpResponseMessage Register(HttpRequestMessage request, RegistrationViewModel user)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateResponse(HttpStatusCode.BadRequest, new { success = false });
                }
                else
                {
                    Entities.User _user = _membershipService.CreateUser(user.Username, user.Email, user.Password, new int[] { 1 });

                    if (_user != null)
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = true });
                    }
                    else
                    {
                        response = request.CreateResponse(HttpStatusCode.OK, new { success = false });
                    }
                }

                return response;
            });
        }
    }

User send a POST request to api/account/authenticate with their credentials (we ‘ll view the LoginViewModel soon) and the controller validates the user though the MemebershipService. If user’s credentials are valid then the returned MembershipContext will contain the relative user’s User entity. The registration process works pretty much the same. This time the user posts a request to api/account/register (we ‘ll view the RegistrationViewModel later) and if the ModelState is valid then the user is created through the MemebershipService’s CreateUser method. Let’s see now both the LoginViewModel and the RegistrationViewModel with their respective validators. Add the ViewModel classes in the Models folder and a AccountViewModelValidators.cs file inside the Infrastructure/Validators folder to hold both their validators.

public class LoginViewModel : IValidatableObject
    {
        public string Username { get; set; }
        public string Password { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new LoginViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }
public class RegistrationViewModel : IValidatableObject
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new RegistrationViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }
public class RegistrationViewModelValidator : AbstractValidator<RegistrationViewModel>
    {
        public RegistrationViewModelValidator()
        {
            RuleFor(r => r.Email).NotEmpty().EmailAddress()
                .WithMessage("Invalid email address");

            RuleFor(r => r.Username).NotEmpty()
                .WithMessage("Invalid username");

            RuleFor(r => r.Password).NotEmpty()
                .WithMessage("Invalid password");
        }
    }

    public class LoginViewModelValidator : AbstractValidator<LoginViewModel>
    {
        public LoginViewModelValidator()
        {
            RuleFor(r => r.Username).NotEmpty()
                .WithMessage("Invalid username");

            RuleFor(r => r.Password).NotEmpty()
                .WithMessage("Invalid password");
        }
    }

In the Front-End side now, we need to build a MembershipService to handle the following:

MembershipService factory

  1. Authenticate user through the Login view
  2. Register a user through the Register view
  3. Save user’s credentials after successful login or registration in a session cookie ($cookieStore)
  4. Remove credentials when user log-off from application
  5. Checks if user is logged in or not through the relative $cookieStore repository value

This factory service is mostly depending in the $cookieStore service (ngCookies module) and in a 3rd party module named ‘$base64’ able to encode – decode strings in base64 format. Logged in user’s credentials are saved in a $rootScope variable and added as Authorization header in a each http request. Add the membershipService.js file inside the spa/services folder.

(function (app) {
    'use strict';

    app.factory('membershipService', membershipService);

    membershipService.$inject = ['apiService', 'notificationService','$http', '$base64', '$cookieStore', '$rootScope'];

    function membershipService(apiService, notificationService, $http, $base64, $cookieStore, $rootScope) {

        var service = {
            login: login,
            register: register,
            saveCredentials: saveCredentials,
            removeCredentials: removeCredentials,
            isUserLoggedIn: isUserLoggedIn
        }

        function login(user, completed) {
            apiService.post('/api/account/authenticate', user,
            completed,
            loginFailed);
        }

        function register(user, completed) {
            apiService.post('/api/account/register', user,
            completed,
            registrationFailed);
        }

        function saveCredentials(user) {
            var membershipData = $base64.encode(user.username + ':' + user.password);

            $rootScope.repository = {
                loggedUser: {
                    username: user.username,
                    authdata: membershipData
                }
            };

            $http.defaults.headers.common['Authorization'] = 'Basic ' + membershipData;
            $cookieStore.put('repository', $rootScope.repository);
        }

        function removeCredentials() {
            $rootScope.repository = {};
            $cookieStore.remove('repository');
            $http.defaults.headers.common.Authorization = '';
        };

        function loginFailed(response) {
            notificationService.displayError(response.data);
        }

        function registrationFailed(response) {

            notificationService.displayError('Registration failed. Try again.');
        }

        function isUserLoggedIn() {
            return $rootScope.repository.loggedUser != null;
        }

        return service;
    }



})(angular.module('common.core'));

Now that we built this service we are able to handle page refreshes as well. Go ahead and add the run configuration for the main module homeCiname inside the app.js file.

(function () {
    'use strict';

    angular.module('homeCinema', ['common.core', 'common.ui'])
        .config(config)
        .run(run);
    // routeProvider code ommited

    run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];

    function run($rootScope, $location, $cookieStore, $http) {
        // handle page refreshes
        $rootScope.repository = $cookieStore.get('repository') || {};
        if ($rootScope.repository.loggedUser) {
            $http.defaults.headers.common['Authorization'] = $rootScope.repository.loggedUser.authdata;
        }

        $(document).ready(function () {
            $(".fancybox").fancybox({
                openEffect: 'none',
                closeEffect: 'none'
            });

            $('.fancybox-media').fancybox({
                openEffect: 'none',
                closeEffect: 'none',
                helpers: {
                    media: {}
                }
            });

            $('[data-toggle=offcanvas]').click(function () {
                $('.row-offcanvas').toggleClass('active');
            });
        });
    }

})();

I found the opportunity to add same fancy-box related initialization code as well. Now that we have the membership functionality configured both for the server and the front-end side,
let’s proceed to the login and register views with their controllers. Those angularJS components will be placed inside an account folder in the spa. Here is the login.html template:

<div class="row">
    <form class="form-signin" role="form" novalidate angular-validator name="userLoginForm" angular-validator-submit="login()">
        <h3 class="form-signin-heading">Sign in</h3>
        <div class="form-group">
            <label for="inputUsername" class="sr-only">Username</label>
            <input type="text" id="inputUsername" name="inputUsername" class="form-control" ng-model="user.username" placeholder="Username"
                   validate-on="blur" required required-message="'Username is required'">
        </div>
        <div class="form-group">
            <label for="inputPassword" class="sr-only">Password</label>
            <input type="password" id="inputPassword" name="inputPassword" class="form-control" ng-model="user.password" placeholder="Password"
                   validate-on="blur" required required-message="'Password is required'">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
        <div class="pull-right">
            <a ng-href="#/register" class="control-label">Register</a>
        </div>
    </form>
</div>

Here you can see (highlighted lines) for the first time a new library we will be using for validating form controls, the angularValidator.
spa-webapi-angular-19
And now the loginCtrl controller.

(function (app) {
    'use strict';

    app.controller('loginCtrl', loginCtrl);

    loginCtrl.$inject = ['$scope', 'membershipService', 'notificationService','$rootScope', '$location'];

    function loginCtrl($scope, membershipService, notificationService, $rootScope, $location) {
        $scope.pageClass = 'page-login';
        $scope.login = login;
        $scope.user = {};

        function login() {
            membershipService.login($scope.user, loginCompleted)
        }

        function loginCompleted(result) {
            if (result.data.success) {
                membershipService.saveCredentials($scope.user);
                notificationService.displaySuccess('Hello ' + $scope.user.username);
                $scope.userData.displayUserInfo();
                if ($rootScope.previousState)
                    $location.path($rootScope.previousState);
                else
                    $location.path('/');
            }
            else {
                notificationService.displayError('Login failed. Try again.');
            }
        }
    }

})(angular.module('common.core'));

The login function calls the membershipService’s login and passes a success callback. If the login succeed it does three more things: First, it saves user’s credentials through membershipService and then displays logged-in user’s info through the rootCtrl controller. Finally, checks if the user ended in login view cause authentication required to access another view and if so, redirects him/hem to that view. Let me remind you a small part of the apiService.

if (error.status == '401') {
      notificationService.displayError('Authentication required.');
      $rootScope.previousState = $location.path();
       $location.path('/login');
  }

The register.html template and its respective controller work in exactly the same way.

<div class="row">
    <form class="form-signin" role="form" novalidate angular-validator name="userRegistrationForm" angular-validator-submit="register()">
        <h3 class="form-signin-heading">Register <label class="label label-danger">Admin</label></h3>
        <div class="form-group">
            <label for="inputUsername" class="">Username</label>
            <input type="text" name="inputUsername" class="form-control" ng-model="user.username"
                   placeholder="Username" validate-on="blur" required required-message="'Username is required'">
        </div>
        <div class="form-group">
            <label for="inputPassword">Password</label>
            <input type="password" name="inputPassword" class="form-control" ng-model="user.password" placeholder="Password"
                    validate-on="blur" required required-message="'Password is required'">
        </div>
        <div class="form-group">
            <label for="inputEmail">Email</label>
            <input type="email" name="inputEmail" class="form-control" ng-model="user.email" placeholder="Email address"
                   validate-on="blur" required required-message="'Email is required'">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
    </form>
</div>
(function (app) {
    'use strict';

    app.controller('registerCtrl', registerCtrl);

    registerCtrl.$inject = ['$scope', 'membershipService', 'notificationService', '$rootScope', '$location'];

    function registerCtrl($scope, membershipService, notificationService, $rootScope, $location) {
        $scope.pageClass = 'page-login';
        $scope.register = register;
        $scope.user = {};

        function register() {
            membershipService.register($scope.user, registerCompleted)
        }

        function registerCompleted(result) {
            if (result.data.success) {
                membershipService.saveCredentials($scope.user);
                notificationService.displaySuccess('Hello ' + $scope.user.username);
                $scope.userData.displayUserInfo();
                $location.path('/');
            }
            else {
                notificationService.displayError('Registration failed. Try again.');
            }
        }
    }

})(angular.module('common.core'));

spa-webapi-angular-20

Customers

The Customers feature is consisted by 2 views in our SPA application. The first view is responsible to display all customers. It also supports pagination, filtering current view data and start a new server search. The second one is the registration view where an employee can register a new customer. We will start from the server side required components first and then with the front-end as we did before. Add a new CustomersController Web API controller inside the controllers folder.

    [Authorize(Roles="Admin")]
    [RoutePrefix("api/customers")]
    public class CustomersController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Customer> _customersRepository;

        public CustomersController(IEntityBaseRepository<Customer> customersRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
    }

First feature we want to support is the pagination with an optional filter search parameter. For this to work, the data returned by the Web API action must also include some pagination related information so that the front-end components can re-build the paginated list. Add the following generic PaginationSet class inside the Infrastructure/core folder.

public class PaginationSet<T>
    {
        public int Page { get; set; }

        public int Count
        {
            get
            {
                return (null != this.Items) ? this.Items.Count() : 0;
            }
        }

        public int TotalPages { get; set; }
        public int TotalCount { get; set; }

        public IEnumerable<T> Items { get; set; }
    }

This class holds the list of items we want to render plus all the pagination information we need to build a paginated list at the front side. Customer entity will have its own ViewModel so let’s create the CustomerViewModel and its validator. I assume that at this point you know where to place the following files.

   [Bind(Exclude = "UniqueKey")]
    public class CustomerViewModel : IValidatableObject
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string IdentityCard { get; set; }
        public Guid UniqueKey { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Mobile { get; set; }
        public DateTime RegistrationDate { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new CustomerViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }

I have excluded the UniqueKey property from binding since that’s a value to be created on server side.

public class CustomerViewModelValidator : AbstractValidator<CustomerViewModel>
    {
        public CustomerViewModelValidator()
        {
            RuleFor(customer => customer.FirstName).NotEmpty()
                .Length(1, 100).WithMessage("First Name must be between 1 - 100 characters");

            RuleFor(customer => customer.LastName).NotEmpty()
                .Length(1, 100).WithMessage("Last Name must be between 1 - 100 characters");

            RuleFor(customer => customer.IdentityCard).NotEmpty()
                .Length(1, 100).WithMessage("Identity Card must be between 1 - 50 characters");

            RuleFor(customer => customer.DateOfBirth).NotNull()
                .LessThan(DateTime.Now.AddYears(-16))
                .WithMessage("Customer must be at least 16 years old.");

            RuleFor(customer => customer.Mobile).NotEmpty().Matches(@"^\d{10}$")
                .Length(10).WithMessage("Mobile phone must have 10 digits");

            RuleFor(customer => customer.Email).NotEmpty().EmailAddress()
                .WithMessage("Enter a valid Email address");

        }
    }

Don’t forget to add the Automapper mapping from Customer to CustomerViewModel so switch to DomainToViewModelMappingProfile and add the following line inside the Configure() function.

Mapper.CreateMap<Customer, CustomerViewModel>();

Now we can go to CustomersController and create the Search method.

We will continue with the front-end required angularJS components. As opposed from the routes we defined in the app.js, we need two files to display the customers view, the customers.html template and a customersCtrl controller inside a customersCtrl.js file.

.when("/customers", {
        templateUrl: "scripts/spa/customers/customers.html",
         controller: "customersCtrl"
    })

Go ahead and add a customers folder inside the spa and create the following customers.html template.

<div class="row">
    <div class="panel panel-primary">
        <div class="panel-heading clearfix">
            <h4 class="panel-title pull-left" style="padding-top: 7.5px;">Home Cinema Customers</h4>
            <div class="input-group">
                <input id="inputSearchCustomers" type="search" ng-model="filterCustomers" class="form-control shortInputSearch" placeholder="Filter, search customers..">
                <div class="input-group-btn">
                    <button class="btn btn-primary" ng-click="search();"><i class="glyphicon glyphicon-search"></i></button>
                    <button class="btn btn-primary" ng-click="clearSearch();"><i class="glyphicon glyphicon-remove-sign"></i></button>
                </div>
            </div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div class="col-sm-6" ng-repeat="customer in Customers | filter:filterCustomers">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            <strong>{{customer.FirstName}} {{customer.LastName}}</strong><br/>

                        </div>
                        <div class="panel-body">
                            <div class="table-responsive">
                                <table class="table table-condensed shortMargin">
                                    <tr>
                                        <td class="shortPadding">Email:</td>
                                        <td class="shortPadding"><i>{{customer.Email}}</i></td>
                                    </tr>
                                    <tr>
                                        <td class="shortPadding">Mobile:</td>
                                        <td class="shortPadding"><i>{{customer.Mobile}}</i></td>
                                    </tr>
                                    <tr>
                                        <td class="shortPadding">Birth:</td>
                                        <td class="shortPadding"><i>{{customer.DateOfBirth | date:'mediumDate'}}</i></td>
                                    </tr>
                                    <tr>
                                        <td class="shortPadding">Registered:</td>
                                        <td class="shortPadding"><i>{{customer.RegistrationDate | date:'mediumDate'}}</i></td>
                                    </tr>
                                </table>
                            </div>
                        </div>
                        <div class="panel-footer clearfix">
                            <label class="label label-danger">{{customer.IdentityCard}}</label>
                            <div class="pull-right">
                                <buton class="btn btn-primary btn-xs" ng-click="openEditDialog(customer);">Edit <i class="fa fa-pencil pull-right"></i></buton>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="panel-footer">
            <div class="text-center">
                <custom-pager page="{{page}}" pages-count="{{pagesCount}}" total-count="{{totalCount}}" search-func="search(page)"></custom-pager>
            </div>
        </div>
    </div>
</div>

The most important highlighted line is the last one where we build a custom pager element. The directive we are going to add is responsible to render a paginated list depending on pagination information retrieved from the server (page, pages-count, total-count). Add the following pager.html template and its definition directive inside the layout folder.

<div>
    <div ng-hide="(!pagesCount || pagesCount < 2)" style="display:inline">
        <ul class="pagination pagination-sm">
            <li><a ng-hide="page == 0" ng-click="search(0)"><<</a></li>
            <li><a ng-hide="page == 0" ng-click="search(page-1)"><</a></li>
            <li ng-repeat="n in range()" ng-class="{active: n == page}">
                <a ng-click="search(n)" ng-if="n != page">{{n+1}}</a>
                <span ng-if="n == page">{{n+1}}</span>
            </li>
            <li><a ng-hide="page == pagesCount - 1" ng-click="search(pagePlus(1))">></a></li>
            <li><a ng-hide="page == pagesCount - 1" ng-click="search(pagesCount - 1)">>></a></li>
        </ul>
    </div>
</div>
(function(app) {
    'use strict';

    app.directive('customPager', customPager);

    function customPager() {
        return {
            scope: {
                page: '@',
                pagesCount: '@',
                totalCount: '@',
                searchFunc: '&',
                customPath: '@'
            },
            replace: true,
            restrict: 'E',
            templateUrl: '/scripts/spa/layout/pager.html',
            controller: ['$scope', function ($scope) {
                $scope.search = function (i) {
                    if ($scope.searchFunc) {
                        $scope.searchFunc({ page: i });
                    }
                };

                $scope.range = function () {
                    if (!$scope.pagesCount) { return []; }
                    var step = 2;
                    var doubleStep = step * 2;
                    var start = Math.max(0, $scope.page - step);
                    var end = start + 1 + doubleStep;
                    if (end > $scope.pagesCount) { end = $scope.pagesCount; }

                    var ret = [];
                    for (var i = start; i != end; ++i) {
                        ret.push(i);
                    }

                    return ret;
                };

                $scope.pagePlus = function(count)
                {
                    console.log($scope.page);
                    return +$scope.page + count;
                }
            }]
        }
    }

})(angular.module('homeCinema'));

spa-webapi-angular-02
Now let’s see the customersCtrl controller. This controller is responsible to retrieve data from Web API and start a new search if the user presses the magnify button next to the textbox. More over it’s the one that will open a modal popup window when the employee decides to edit a specific customer. For this popup window we will use the angular-ui $modal service.

(function (app) {
    'use strict';

    app.controller('customersCtrl', customersCtrl);

    customersCtrl.$inject = ['$scope','$modal', 'apiService', 'notificationService'];

    function customersCtrl($scope, $modal, apiService, notificationService) {

        $scope.pageClass = 'page-customers';
        $scope.loadingCustomers = true;
        $scope.page = 0;
        $scope.pagesCount = 0;
        $scope.Customers = [];

        $scope.search = search;
        $scope.clearSearch = clearSearch;

        $scope.search = search;
        $scope.clearSearch = clearSearch;
        $scope.openEditDialog = openEditDialog;

        function search(page) {
            page = page || 0;

            $scope.loadingCustomers = true;

            var config = {
                params: {
                    page: page,
                    pageSize: 4,
                    filter: $scope.filterCustomers
                }
            };

            apiService.get('/api/customers/search/', config,
            customersLoadCompleted,
            customersLoadFailed);
        }

        function openEditDialog(customer) {
            $scope.EditedCustomer = customer;
            $modal.open({
                templateUrl: 'scripts/spa/customers/editCustomerModal.html',
                controller: 'customerEditCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                clearSearch();
            }, function () {
            });
        }

        function customersLoadCompleted(result) {
            $scope.Customers = result.data.Items;

            $scope.page = result.data.Page;
            $scope.pagesCount = result.data.TotalPages;
            $scope.totalCount = result.data.TotalCount;
            $scope.loadingCustomers = false;

            if ($scope.filterCustomers && $scope.filterCustomers.length) {
                notificationService.displayInfo(result.data.Items.length + ' customers found');
            }

        }

        function customersLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function clearSearch() {
            $scope.filterCustomers = '';
            search();
        }

        $scope.search();
    }

})(angular.module('homeCinema'));

Let’s focus on the following part of the customersCtrl controller where the modal window pops up.

function openEditDialog(customer) {
            $scope.EditedCustomer = customer;
            $modal.open({
                templateUrl: 'scripts/spa/customers/editCustomerModal.html',
                controller: 'customerEditCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                clearSearch();
            }, function () {
            });
        }

When we decide to edit a customer we don’t have to request any data from the server. We have them already and we can pass them to the customerEditCtrl through the $scope

$scope.EditedCustomer = customer;

The popup window isn’t a new view to be rendered but a single pop-up window with a template and a custom controller. Let’s view both of those components, the editCustomerModal.html template and the customerEditCtrl controller.

<div class="panel panel-primary">
    <div class="panel-heading">
        Edit {{EditedCustomer.FirstName}} {{EditedCustomer.LastName}}
    </div>
    <div class="panel-body">
        <form role="form" novalidate angular-validator name="addCustomerForm" angular-validator-submit="Register()">
            <div class="">
                <div class="form-group">
                    <div class="row">
                        <div class="col-sm-6">
                            <label class="control-label" for="firstName">First Name</label>
                            <input type="text" class="form-control" ng-model="EditedCustomer.FirstName" name="firstName" id="firstName" placeholder="First Name"
                                   validate-on="blur" required required-message="'First Name is required'">
                        </div>

                        <div class="col-sm-6 selectContainer">
                            <label class="control-label" for="lastName">Last Name</label>
                            <input type="text" class="form-control" ng-model="EditedCustomer.LastName" name="lastName" id="lastName" placeholder="Last Name"
                                   validate-on="blur" required required-message="'Last Name is required'">
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label" for="email">Email Address</label>
                            <input type="email" class="form-control" ng-model="EditedCustomer.Email" id="email" name="email" placeholder="Email address"
                                   validate-on="blur" required required-message="'Email is required'">
                        </div>

                        <div class="col-xs-6 selectContainer">
                            <label class="control-label" for="IdentityCard">Identity Card</label>
                            <input type="text" class="form-control" ng-model="EditedCustomer.IdentityCard" id="identityCard" name="identityCard" placeholder="Identity Card number"
                                   validate-on="blur" required required-message="'Identity Card is required'">
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label" for="mobilePhone">Mobile</label>
                            <input type="text" ng-model="EditedCustomer.Mobile" class="form-control" id="mobilePhone" name="mobilePhone" placeholder="Mobile phone"
                                   validate-on="blur" required required-message="'Mobile phone is required'">
                        </div>
                        <div class="col-xs-6 selectContainer">
                            <label class="control-label" for="dateOfBirth">Date of Birth</label>
                            <p class="input-group">
                                <input type="text" class="form-control" name="dateOfBirth" datepicker-popup="{{format}}" ng-model="EditedCustomer.DateOfBirth" is-open="datepicker.opened"
                                        datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                                       validate-on="blur" required required-message="'Date of birth is required'" />
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                                </span>
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
    <div class="panel-footer clearfix">
        <div class="pull-right">
            <button type="button" class="btn btn-danger" ng-click="cancelEdit()">Cancel</button>
            <button type="button" class="btn btn-primary" ng-click="updateCustomer()">Update</button>
        </div>
    </div>
</div>
(function (app) {
    'use strict';

    app.controller('customerEditCtrl', customerEditCtrl);

    customerEditCtrl.$inject = ['$scope', '$modalInstance','$timeout', 'apiService', 'notificationService'];

    function customerEditCtrl($scope, $modalInstance, $timeout, apiService, notificationService) {

        $scope.cancelEdit = cancelEdit;
        $scope.updateCustomer = updateCustomer;

        $scope.openDatePicker = openDatePicker;
        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        function updateCustomer()
        {
            console.log($scope.EditedCustomer);
            apiService.post('/api/customers/update/', $scope.EditedCustomer,
            updateCustomerCompleted,
            updateCustomerLoadFailed);
        }

        function updateCustomerCompleted(response)
        {
            notificationService.displaySuccess($scope.EditedCustomer.FirstName + ' ' + $scope.EditedCustomer.LastName + ' has been updated');
            $scope.EditedCustomer = {};
            $modalInstance.dismiss();
        }

        function updateCustomerLoadFailed(response)
        {
            notificationService.displayError(response.data);
        }

        function cancelEdit() {
            $scope.isEnabled = false;
            $modalInstance.dismiss();
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $timeout(function () {
                $scope.datepicker.opened = true;
            });

			$timeout(function () {
                $('ul[datepicker-popup-wrap]').css('z-index', '10000');
            }, 100);

        };

    }

})(angular.module('homeCinema'));

When the update finishes we ensure that we call the $modalInstance.dismiss() function to close the modal popup window.
spa-webapi-angular-03
You also need to add the Update Web API action method to the CustomersController

        [HttpPost]
        [Route("update")]
        public HttpResponseMessage Update(HttpRequestMessage request, CustomerViewModel customer)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateResponse(HttpStatusCode.BadRequest,
                        ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                              .Select(m => m.ErrorMessage).ToArray());
                }
                else
                {
                    Customer _customer = _customersRepository.GetSingle(customer.ID);
                    _customer.UpdateCustomer(customer);

                    _unitOfWork.Commit();

                    response = request.CreateResponse(HttpStatusCode.OK);
                }

                return response;
            });
        }

Let’s procceed with the customer’s registration feature by adding the register.html template and the customersRegCtrl controller.

<hr />
<div class="alert alert-info alert-dismissable">
    <a class="panel-close close" data-dismiss="alert">×</a>
    <i class="fa fa-user-plus fa-3x"></i>
    Register <strong>{{movie.Title}}</strong> new customer. Make sure you fill all required fields.
</div>
<div class="row">
    <form role="form" novalidate angular-validator name="addCustomerForm" angular-validator-submit="Register()">
        <div class="col-sm-6">
            <div class="form-group">
                <label for="firstName">First Name</label>
                <div class="form-group">
                    <input type="text" class="form-control" ng-model="newCustomer.FirstName" name="firstName" id="firstName" placeholder="First Name"
                           validate-on="blur" required required-message="'First Name is required'">

                </div>
            </div>
            <div class="form-group">
                <label for="lastName">Last Name</label>
                <div class="form-group">
                    <input type="text" class="form-control" ng-model="newCustomer.LastName" name="lastName" id="lastName" placeholder="Last Name"
                           validate-on="blur" required required-message="'Last Name is required'">

                </div>
            </div>
            <div class="form-group">
                <label for="email">Email address</label>
                <div class="form-group">
                    <input type="email" class="form-control" ng-model="newCustomer.Email" id="email" name="email" placeholder="Email address"
                           validate-on="blur" required required-message="'Email is required'">
                </div>
            </div>
            <div class="form-group">
                <label for="identityCard">Identity Card</label>
                <div class="form-group">
                    <input type="text" class="form-control" ng-model="newCustomer.IdentityCard" id="identityCard" name="identityCard" placeholder="Identity Card number"
                           validate-on="blur" required required-message="'Identity Card is required'">
                </div>
            </div>
            <div class="form-group">
                <label for="dateOfBirth">Date of Birth</label>
                <p class="input-group">
                    <input type="text" class="form-control" name="dateOfBirth" datepicker-popup="{{format}}" ng-model="newCustomer.DateOfBirth" is-open="datepicker.opened" datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                           validate-on="blur" required required-message="'Date of birth is required'" />
                    <span class="input-group-btn">
                        <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                    </span>
                </p>
            </div>
            <div class="form-group">
                <label for="mobilePhone">Mobile phone</label>
                <div class="form-group">
                    <input type="text" ng-model="newCustomer.Mobile" class="form-control" id="mobilePhone" name="mobilePhone" placeholder="Mobile phone"
                           validate-on="blur" required required-message="'Mobile phone is required'">

                </div>
            </div>
            <input type="submit" name="submit" id="submit" value="Submit" class="btn btn-info pull-right">
        </div>
    </form>
    <div class="col-sm-5 col-md-push-1">
        <div class="col-md-12">
            <div class="alert alert-success">
                <ul>
                    <li ng-repeat="message in submission.successMessages track by $index">
                        <strong>{{message}}</strong>
                    </li>
                </ul>
                <strong ng-bind="submission.successMessage"><span class="glyphicon glyphicon-ok"></span> </strong>
            </div>
            <div class="alert alert-danger">
                <ul>
                    <li ng-repeat="error in submission.errorMessages track by $index">
                        <strong>{{error}}</strong>
                    </li>
                </ul>
            </div>
        </div>
    </div>
</div>
(function (app) {
    'use strict';

    app.controller('customersRegCtrl', customersRegCtrl);

    customersRegCtrl.$inject = ['$scope', '$location', '$rootScope', 'apiService'];

    function customersRegCtrl($scope, $location, $rootScope, apiService) {

        $scope.newCustomer = {};

        $scope.Register = Register;

        $scope.openDatePicker = openDatePicker;
        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        $scope.submission = {
            successMessages: ['Successfull submission will appear here.'],
            errorMessages: ['Submition errors will appear here.']
        };

        function Register() {
            apiService.post('/api/customers/register', $scope.newCustomer,
           registerCustomerSucceded,
           registerCustomerFailed);
        }

        function registerCustomerSucceded(response) {
            $scope.submission.errorMessages = ['Submition errors will appear here.'];
            console.log(response);
            var customerRegistered = response.data;
            $scope.submission.successMessages = [];
            $scope.submission.successMessages.push($scope.newCustomer.LastName + ' has been successfully registed');
            $scope.submission.successMessages.push('Check ' + customerRegistered.UniqueKey + ' for reference number');
            $scope.newCustomer = {};
        }

        function registerCustomerFailed(response) {
            console.log(response);
            if (response.status == '400')
                $scope.submission.errorMessages = response.data;
            else
                $scope.submission.errorMessages = response.statusText;
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $scope.datepicker.opened = true;
        };
    }

})(angular.module('homeCinema'));

There’s a small problem when rendering the customers registration template. You see there is no authorized resource to call when this template is rendered but the post action will force the user to authenticate himself/herself. We would like to avoid this and render the view only if the user is logged in. What we have used till now (check the customers view), is that when the controller bound to the view is activated and requests data from the server, if the server requires the user to be authenticated, then the apiService automatically redirects the user to the login view.

if (error.status == '401') {
        notificationService.displayError('Authentication required.');
        $rootScope.previousState = $location.path();
        $location.path('/login');
    }

On the other hand, in the register customer view the user will be requested to be authenticated when the employee tries to post the data (new customer) to the server. We can overcome this by adding a resolve function through the route provider for this route. Switch to the app.js and make the following modification.

.when("/customers/register", {
                templateUrl: "scripts/spa/customers/register.html",
                controller: "customersRegCtrl",
                resolve: { isAuthenticated: isAuthenticated }
            })
// code omitted
isAuthenticated.$inject = ['membershipService', '$rootScope','$location'];

function isAuthenticated(membershipService, $rootScope, $location) {
        if (!membershipService.isUserLoggedIn()) {
            $rootScope.previousState = $location.path();
            $location.path('/login');
        }
    }

We use a route resolve function when we want to check a condition before the route actually changes. In our application we can use it to check if the user is logged in or not and if not redirect to login view. Now add the Register Web API action to the CustomersController.

public HttpResponseMessage Register(HttpRequestMessage request, CustomerViewModel customer)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateResponse(HttpStatusCode.BadRequest,
                        ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                              .Select(m => m.ErrorMessage).ToArray());
                }
                else
                {
                    if (_customersRepository.UserExists(customer.Email, customer.IdentityCard))
                    {
                        ModelState.AddModelError("Invalid user", "Email or Identity Card number already exists");
                        response = request.CreateResponse(HttpStatusCode.BadRequest,
                        ModelState.Keys.SelectMany(k => ModelState[k].Errors)
                              .Select(m => m.ErrorMessage).ToArray());
                    }
                    else
                    {
                        Customer newCustomer = new Customer();
                        newCustomer.UpdateCustomer(customer);
                        _customersRepository.Add(newCustomer);

                        _unitOfWork.Commit();

                        // Update view model
                        customer = Mapper.Map<Customer, CustomerViewModel>(newCustomer);
                        response = request.CreateResponse<CustomerViewModel>(HttpStatusCode.Created, customer);
                    }
                }

                return response;
            });
        }

I have highlighted the line where we update the database customer entity using an extension method. We have an Automapper map from Customer entity to CustomerViewModel but not vice-versa. You could do it but I recommend you not to cause it doesn’t work so well with Entity Framework. That’s why I created an extension method for Customer entities. Add a new folder named Extensions inside the Infrastructure and create the following class. Then make sure you include the namespace in the CustomersController class.

public static class EntitiesExtensions
    {
        public static void UpdateCustomer(this Customer customer, CustomerViewModel customerVm)
        {
            customer.FirstName = customerVm.FirstName;
            customer.LastName = customerVm.LastName;
            customer.IdentityCard = customerVm.IdentityCard;
            customer.Mobile = customerVm.Mobile;
            customer.DateOfBirth = customerVm.DateOfBirth;
            customer.Email = customerVm.Email;
            customer.UniqueKey = (customerVm.UniqueKey == null || customerVm.UniqueKey == Guid.Empty)
                ? Guid.NewGuid() : customerVm.UniqueKey;
            customer.RegistrationDate = (customer.RegistrationDate == DateTime.MinValue ? DateTime.Now : customerVm.RegistrationDate);
        }
    }

Movies

The most complex feature in our application is the Movies and that’s because several requirements are connected to that feature. Let’s recap what we need to do.

  1. All movies must be displayed with their relevant information (availability, trailer etc..)
  2. Pagination must be used for faster results, and user can either filter the already displayed movies or search for new ones
  3. Clicking on a DVD image must show the movie’s Details view where user can either edit the movie or rent it to a specific customer if available. This view is accessible only to authenticated users
  4. When employee decides to rent a specific DVD to a customer through the Rent view, it should be able to search customers through an auto-complete textbox
  5. The details view displays inside a panel, rental-history information for this movie, that is the dates rentals and returnings occurred. From this panel user can search a specific rental and mark it as returned
  6. Authenticated employees should be able to add a new entry to the system. They should be able to upload a relevant image for the movie as well

We will start with the first two of them that that is display all movies with pagination, filtering and searching capabilities. We have seen such features when we created the customers base view. First, let’s add the required Web API action method in the MoviesController.

        [AllowAnonymous]
        [Route("{page:int=0}/{pageSize=3}/{filter?}")]
        public HttpResponseMessage Get(HttpRequestMessage request, int? page, int? pageSize, string filter = null)
        {
            int currentPage = page.Value;
            int currentPageSize = pageSize.Value;

            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                List<Movie> movies = null;
                int totalMovies = new int();

                if (!string.IsNullOrEmpty(filter))
                {
                    movies = _moviesRepository.GetAll()
                        .OrderBy(m => m.ID)
                        .Where(m => m.Title.ToLower()
                        .Contains(filter.ToLower().Trim()))
                        .ToList();
                }
                else
                {
                    movies = _moviesRepository.GetAll().ToList();
                }

                totalMovies = movies.Count();
                movies = movies.Skip(currentPage * currentPageSize)
                        .Take(currentPageSize)
                        .ToList();

                IEnumerable<MovieViewModel> moviesVM = Mapper.Map<IEnumerable<Movie>, IEnumerable<MovieViewModel>>(movies);

                PaginationSet<MovieViewModel> pagedSet = new PaginationSet<MovieViewModel>()
                {
                    Page = currentPage,
                    TotalCount = totalMovies,
                    TotalPages = (int)Math.Ceiling((decimal)totalMovies / currentPageSize),
                    Items = moviesVM
                };

                response = request.CreateResponse<PaginationSet<MovieViewModel>>(HttpStatusCode.OK, pagedSet);

                return response;
            });
        }

As you can see, this view doesn’t require the user to be authenticated. Once more we used the PaginationSet class to return additional information for pagination purposes. On the front-end side, create a movies folder inside the spa, add the movies.html template and the moviesCtrl.js controller as follow.

<div class="row">
    <div class="panel panel-primary">
        <div class="panel-heading clearfix">
            <h4 class="panel-title pull-left" style="padding-top: 7.5px;">Home Cinema Movies</h4>
            <div class="input-group">
                <input id="inputSearchMovies" type="search" ng-model="filterMovies" class="form-control shortInputSearch" placeholder="Filter, search movies..">
                <div class="input-group-btn">
                    <button class="btn btn-primary" ng-click="search();"><i class="glyphicon glyphicon-search"></i></button>
                    <button class="btn btn-primary" ng-click="clearSearch();"><i class="glyphicon glyphicon-remove-sign"></i></button>
                </div>
            </div>
        </div>

        <div class="panel-body">
            <div class="row">
                <div class="col-xs-12 col-sm-6 col-md-4" ng-repeat="movie in Movies | filter:filterMovies">
                    <div class="media">
                        <a class="pull-left" ng-href="#/movies/{{movie.ID}}" title="View {{movie.Title}} details">
                            <img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
                        </a>
                        <div class="media-body">
                            <h4 class="media-heading">{{movie.Title}}</h4>
                            Director: <strong>{{movie.Director}}</strong>
                            <br />
                            Writer: <strong>{{movie.Writer}}</strong>
                            <br />
                            Producer: <strong>{{movie.Producer}}</strong>
                            <br />
                            <a class="fancybox-media" ng-href="{{movie.TrailerURI}}">Trailer<i class="fa fa-video-camera fa-fw"></i></a>
                        </div>
                        <div class="media-bottom">
                            <span component-rating="{{movie.Rating}}"></span>
                        </div>
                        <label class="label label-info">{{movie.Genre}}</label>
                        <available-movie is-available="{{movie.IsAvailable}}"></available-movie>
                    </div>
                    <br /><br />
                </div>
            </div>
        </div>
        <div class="panel-footer">
            <div class="text-center">
                <custom-pager page="{{page}}" custom-path="{{customPath}}" pages-count="{{pagesCount}}" total-count="{{totalCount}}" search-func="search(page)"></custom-pager>
            </div>
        </div>
    </div>
</div>

Once again we used both the available-movie and custom-pager directives. Moreover, check that when we click on an image (lines 18:20) we want to change route and display selected movie details.

(function (app) {
    'use strict';

    app.controller('moviesCtrl', moviesCtrl);

    moviesCtrl.$inject = ['$scope', 'apiService','notificationService'];

    function moviesCtrl($scope, apiService, notificationService) {
        $scope.pageClass = 'page-movies';
        $scope.loadingMovies = true;
        $scope.page = 0;
        $scope.pagesCount = 0;

        $scope.Movies = [];

        $scope.search = search;
        $scope.clearSearch = clearSearch;

        function search(page) {
            page = page || 0;

            $scope.loadingMovies = true;

            var config = {
                params: {
                    page: page,
                    pageSize: 6,
                    filter: $scope.filterMovies
                }
            };

            apiService.get('/api/movies/', config,
            moviesLoadCompleted,
            moviesLoadFailed);
        }

        function moviesLoadCompleted(result) {
            $scope.Movies = result.data.Items;
            $scope.page = result.data.Page;
            $scope.pagesCount = result.data.TotalPages;
            $scope.totalCount = result.data.TotalCount;
            $scope.loadingMovies = false;

            if ($scope.filterMovies && $scope.filterMovies.length)
            {
                notificationService.displayInfo(result.data.Items.length + ' movies found');
            }

        }

        function moviesLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function clearSearch() {
            $scope.filterMovies = '';
            search();
        }

        $scope.search();
    }

})(angular.module('homeCinema'));

spa-webapi-angular-05
Let’s continue with the movie details page. Think this page as an control panel for selected movie where you can edit or rent this movie to a customer and last but not least view all rental history related to that movie, in other words, who borrowed that movie and its rental status (borrowed, returned). First, we will prepare the server side part so swith to the MoviesController and add the following action that returns details for a specific movie. Check that this action is only available for authenticated users and hence when an employee tries to display the details view he/she will be forced to log in first.

        [Route("details/{id:int}")]
        public HttpResponseMessage Get(HttpRequestMessage request, int id)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;
                var movie = _moviesRepository.GetSingle(id);

                MovieViewModel movieVM = Mapper.Map<Movie, MovieViewModel>(movie);

                response = request.CreateResponse<MovieViewModel>(HttpStatusCode.OK, movieVM);

                return response;
            });
        }

The movie details page also displays rental-history information so let’s see how to implement this functionality. What we mean by movie rental-history is all rentals occurred on stock items related to a specific movie. I remind you that a specific movie may have multiple stock items (DVDs) and more over, a rental is actually assigned to the stock item, not the movie entity.
spa-webapi-angular-22
Let’s create a new ViewModel named RentalHistoryViewModel to hold the information about a specific rental. Add the following class in the Models folder.

public class RentalHistoryViewModel
    {
        public int ID { get; set; }
        public int StockId { get; set; }
        public string Customer { get; set; }
        public string Status { get; set; }
        public DateTime RentalDate { get; set; }
        public Nullable<DateTime> ReturnedDate { get; set; }
    }

The purpose is to return a list of RentalHistoryViewModel items related to the movie being displayed on the details view. In other words, find all rentals related to stock items that have foreign key the selected movie’s ID. Add the following Web API RentalsController controller.

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/rentals")]
    public class RentalsController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Rental> _rentalsRepository;
        private readonly IEntityBaseRepository<Customer> _customersRepository;
        private readonly IEntityBaseRepository<Stock> _stocksRepository;
        private readonly IEntityBaseRepository<Movie> _moviesRepository;

        public RentalsController(IEntityBaseRepository<Rental> rentalsRepository,
            IEntityBaseRepository<Customer> customersRepository, IEntityBaseRepository<Movie> moviesRepository,
            IEntityBaseRepository<Stock> stocksRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _rentalsRepository = rentalsRepository;
            _moviesRepository = moviesRepository;
            _customersRepository = customersRepository;
            _stocksRepository = stocksRepository;
        }
    }

We need a private method in this controller which returns the rental-history items as we previously described.

        private List<RentalHistoryViewModel> GetMovieRentalHistory(int movieId)
        {
            List<RentalHistoryViewModel> _rentalHistory = new List<RentalHistoryViewModel>();
            List<Rental> rentals = new List<Rental>();

            var movie = _moviesRepository.GetSingle(movieId);

            foreach (var stock in movie.Stocks)
            {
                rentals.AddRange(stock.Rentals);
            }

            foreach (var rental in rentals)
            {
                RentalHistoryViewModel _historyItem = new RentalHistoryViewModel()
                {
                    ID = rental.ID,
                    StockId = rental.StockId,
                    RentalDate = rental.RentalDate,
                    ReturnedDate = rental.ReturnedDate.HasValue ? rental.ReturnedDate : null,
                    Status = rental.Status,
                    Customer = _customersRepository.GetCustomerFullName(rental.CustomerId)
                };

                _rentalHistory.Add(_historyItem);
            }

            _rentalHistory.Sort((r1, r2) => r2.RentalDate.CompareTo(r1.RentalDate));

            return _rentalHistory;
        }

And now we can create the Web API action that the client will invoke when requesting rental history information.

        [HttpGet]
        [Route("{id:int}/rentalhistory")]
        public HttpResponseMessage RentalHistory(HttpRequestMessage request, int id)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                List<RentalHistoryViewModel> _rentalHistory = GetMovieRentalHistory(id);

                response = request.CreateResponse<List<RentalHistoryViewModel>>(HttpStatusCode.OK, _rentalHistory);

                return response;
            });
        }

In case we wanted to request rental history for movie with ID=4 then the request would be in the following form:

api/rentals/4/rentalhistory

The employee must be able to mark a specific movie rental as Returned when the customer returns the DVD so let’s add a Return action method as well.

        [HttpPost]
        [Route("return/{rentalId:int}")]
        public HttpResponseMessage Return(HttpRequestMessage request, int rentalId)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                var rental = _rentalsRepository.GetSingle(rentalId);

                if (rental == null)
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid rental");
                else
                {
                    rental.Status = "Returned";
                    rental.Stock.IsAvailable = true;
                    rental.ReturnedDate = DateTime.Now;

                    _unitOfWork.Commit();

                    response = request.CreateResponse(HttpStatusCode.OK);
                }

                return response;
            });
        }

You can mark a movie with ID=4 as Returned with a POST request such as:

api/rentals/return/4

spa-webapi-angular-23
At this point we can switch to the front-end and create the details.html template and its relative controller movieDetailsCtrl. Add the following files inside the movies folder.

<hr />
<div class="jumbotron">
    <div class="container text-center">
        <img alt="{{movie.Title}}" ng-src="../../../Content/images/movies/{{movie.Image}}" class="pull-left" height="120" />
        <div class="movieDescription"><i><i class="fa fa-quote-left"></i>{{movie.Description}}<i class="fa fa-quote-right"></i></i></div>
        <br />
        <div class="btn-group">
            <button ng-if="movie.IsAvailable" type="button" ng-click="openRentDialog();" class="btn btn-sm btn-primary">Rent movie<i class="fa fa-book pull-right"></i></button>
            <a href="#/movies/edit/{{movie.ID}}" class="btn btn-sm btn-default">Edit movie<i class="fa fa-pencil-square-o pull-right"></i></a>
        </div> <!-- end btn-group -->
    </div> <!-- end container -->
</div>

<div class="row">
    <div class="col-md-6">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h5>{{movie.Title}}</h5>
            </div>
            <div class="panel-body" ng-if="!loadingMovie">
                <div class="media">
                    <a class="pull-right" ng-href="#/movies/{{movie.ID}}" title="View {{movie.Title}} details">
                        <img class="media-object" height="120" ng-src="../../Content/images/movies/{{movie.Image}}" alt="" />
                    </a>
                    <div class="media-body">
                        <h4 class="media-heading">{{movie.Title}}</h4>
                        Directed by: <label>{{movie.Director}}</label><br />
                        Written by: <label>{{movie.Writer}}</label><br />
                        Produced by: <label>{{movie.Producer}}</label><br />
                        Rating: <span component-rating='{{movie.Rating}}'></span>
                        <br />
                        <label class="label label-info">{{movie.Genre}}</label>
                        <available-movie is-available="{{movie.IsAvailable}}"></available-movie>
                    </div>
                </div>
            </div>
            <div class="panel-footer clearfix" ng-if="!loadingMovie">
                <div class="pull-right">
                    <a ng-href="{{movie.TrailerURI}}" class="btn btn-primary fancybox-media">View Trailer <i class="fa fa-video-camera pull-right"></i></a>
                    <a ng-href="#/movies/edit/{{movie.ID}}" class="btn btn-default">Edit movie <i class="fa fa-pencil-square pull-right"></i></a>
                </div>
            </div>
            <div ng-if="loadingMovie">
                <div class="col-xs-4"></div>
                <div class="col-xs-4">
                    <i class="fa fa-refresh fa-4x fa-spin"></i> <label class="label label-primary">Loading movie data...</label>
                </div>
                <div class="col-xs-4"></div>
            </div>
        </div>

    </div>
    <div class="col-md-6">
        <div class="panel panel-danger shortPanel">
            <div class="panel-heading clearfix">
                <h5 class="pull-left">Rentals</h5>
                <div class="input-group">
                    <input id="inputSearchMovies" type="search" ng-model="filterRentals" class="form-control" placeholder="Filter..">
                    <div class="input-group-btn">
                        <button class="btn btn-primary" ng-click="clearSearch();"><i class="glyphicon glyphicon-remove-sign"></i></button>
                    </div>
                </div>
            </div>
            <div class="table-responsive" ng-if="!loadingRentals">
                <table class="table table-bordered">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Name</th>
                            <th>Rental date</th>
                            <th>Status</th>
                            <th></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat="rental in rentalHistory | filter:filterRentals">
                            <td>{{rental.ID}}</td>
                            <td>{{rental.Customer}}</td>
                            <td>{{rental.RentalDate | date:'fullDate'}}</td>
                            <td ng-class="getStatusColor(rental.Status)">{{rental.Status}}</td>
                            <td class="text-center">
                                <button ng-if="isBorrowed(rental)" type="button" class="btn btn-primary btn-xs" ng-click="returnMovie(rental.ID)">Return</button>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <div ng-if="loadingRentals">
                <div class="col-xs-4"></div>
                <div class="col-xs-4">
                    <i class="fa fa-refresh fa-4x fa-spin"></i> <label class="label label-primary">Loading rental history...</label>
                </div>
                <div class="col-xs-4"></div>
            </div>
        </div>
    </div>
</div>
(function (app) {
    'use strict';

    app.controller('movieDetailsCtrl', movieDetailsCtrl);

    movieDetailsCtrl.$inject = ['$scope', '$location', '$routeParams', '$modal', 'apiService', 'notificationService'];

    function movieDetailsCtrl($scope, $location, $routeParams, $modal, apiService, notificationService) {
        $scope.pageClass = 'page-movies';
        $scope.movie = {};
        $scope.loadingMovie = true;
        $scope.loadingRentals = true;
        $scope.isReadOnly = true;
        $scope.openRentDialog = openRentDialog;
        $scope.returnMovie = returnMovie;
        $scope.rentalHistory = [];
        $scope.getStatusColor = getStatusColor;
        $scope.clearSearch = clearSearch;
        $scope.isBorrowed = isBorrowed;

        function loadMovie() {

            $scope.loadingMovie = true;

            apiService.get('/api/movies/details/' + $routeParams.id, null,
            movieLoadCompleted,
            movieLoadFailed);
        }

        function loadRentalHistory() {
            $scope.loadingRentals = true;

            apiService.get('/api/rentals/' + $routeParams.id + '/rentalhistory', null,
            rentalHistoryLoadCompleted,
            rentalHistoryLoadFailed);
        }

        function loadMovieDetails() {
            loadMovie();
            loadRentalHistory();
        }

        function returnMovie(rentalID) {
            apiService.post('/api/rentals/return/' + rentalID, null,
            returnMovieSucceeded,
            returnMovieFailed);
        }

        function isBorrowed(rental)
        {
            return rental.Status == 'Borrowed';
        }

        function getStatusColor(status) {
            if (status == 'Borrowed')
                return 'red'
            else {
                return 'green';
            }
        }

        function clearSearch()
        {
            $scope.filterRentals = '';
        }

        function movieLoadCompleted(result) {
            $scope.movie = result.data;
            $scope.loadingMovie = false;
        }

        function movieLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function rentalHistoryLoadCompleted(result) {
            console.log(result);
            $scope.rentalHistory = result.data;
            $scope.loadingRentals = false;
        }

        function rentalHistoryLoadFailed(response) {
            notificationService.displayError(response);
        }

        function returnMovieSucceeded(response) {
            notificationService.displaySuccess('Movie returned to HomeCinema succeesfully');
            loadMovieDetails();
        }

        function returnMovieFailed(response) {
            notificationService.displayError(response.data);
        }

        function openRentDialog() {
            $modal.open({
                templateUrl: 'scripts/spa/rental/rentMovieModal.html',
                controller: 'rentMovieCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                loadMovieDetails();
            }, function () {
            });
        }

        loadMovieDetails();
    }
})(angular.module('homeCinema'));

spa-webapi-angular-24
There is one more requirement we need to implement in the details view, the rental. As you may noticed from the movieDetailsCtrl controller, the rental works with a $modal popup window.

function openRentDialog() {
            $modal.open({
                templateUrl: 'scripts/spa/rental/rentMovieModal.html',
                controller: 'rentMovieCtrl',
                scope: $scope
            }).result.then(function ($scope) {
                loadMovieDetails();
            }, function () {
            });
        }

We have seen the $modal popup in action when we were at the edit customer view. Create the rentMovieModal.html and the rentMovieCtrl controller inside a new folder named Rental under the spa.


<div class="panel panel-primary">
    <div class="panel-heading">
        Rent {{movie.Title}}
    </div>
    <div class="panel-body">
        <form class="form-horizontal" role="form">
            <div class="form-group">
                <div class="col-xs-8 selectContainer">
                    <label class="control-label">Available Stock items</label>
                    <select ng-model="selectedStockItem" class="form-control black" ng-options="option.ID as option.UniqueKey for option in stockItems" required></select>
                </div>
            </div>
            <div class="form-group">
                <div class="col-xs-8">
                    <label class="control-label">Select customer</label>
                    <angucomplete-alt id="members"
                                      placeholder="Search customers"
                                      pause="200"
                                      selected-object="selectCustomer"
                                      input-changed="selectionChanged"
                                      remote-url="/api/customers?filter="
                                      remote-url-data-field=""
                                      title-field="FirstName,LastName"
                                      description-field="Email"
                                      input-class="form-control form-control-small"
                                      match-class="red"
                                      text-searching="Searching customers.."
                                      text-no-results="No customers found matching your filter." />
                </div>
            </div>
        </form>
    </div>
    <div class="panel-footer clearfix">
        <div class="pull-right">
            <button type="button" class="btn btn-danger" ng-click="cancelRental()">Cancel</button>
            <button type="button" class="btn btn-primary" ng-click="rentMovie()" ng-disabled="!isEnabled">Rent movie</button>
        </div>
    </div>
</div>

One new thing to notice in this template is the use of the angucomplete-alt directive. We use it in order search customers with auto-complete support. In this directive we declared where to request the data from, the fields to display when an option is selected, a text to display till the request is completed, what to do when an option is selected or changed, etc.. You can find more info about this awesome auto-complete directive here.
spa-webapi-angular-25

(function (app) {
    'use strict';

    app.controller('rentMovieCtrl', rentMovieCtrl);

    rentMovieCtrl.$inject = ['$scope', '$modalInstance', '$location', 'apiService', 'notificationService'];

    function rentMovieCtrl($scope, $modalInstance, $location, apiService, notificationService) {

        $scope.Title = $scope.movie.Title;
        $scope.loadStockItems = loadStockItems;
        $scope.selectCustomer = selectCustomer;
        $scope.selectionChanged = selectionChanged;
        $scope.rentMovie = rentMovie;
        $scope.cancelRental = cancelRental;
        $scope.stockItems = [];
        $scope.selectedCustomer = -1;
        $scope.isEnabled = false;

        function loadStockItems() {
            notificationService.displayInfo('Loading available stock items for ' + $scope.movie.Title);

            apiService.get('/api/stocks/movie/' + $scope.movie.ID, null,
            stockItemsLoadCompleted,
            stockItemsLoadFailed);
        }

        function stockItemsLoadCompleted(response) {
            $scope.stockItems = response.data;
            $scope.selectedStockItem = $scope.stockItems[0].ID;
            console.log(response);
        }

        function stockItemsLoadFailed(response) {
            console.log(response);
            notificationService.displayError(response.data);
        }

        function rentMovie() {
            apiService.post('/api/rentals/rent/' + $scope.selectedCustomer + '/' + $scope.selectedStockItem, null,
            rentMovieSucceeded,
            rentMovieFailed);
        }

        function rentMovieSucceeded(response) {
            notificationService.displaySuccess('Rental completed successfully');
            $modalInstance.close();
        }

        function rentMovieFailed(response) {
            notificationService.displayError(response.data.Message);
        }

        function cancelRental() {
            $scope.stockItems = [];
            $scope.selectedCustomer = -1;
            $scope.isEnabled = false;
            $modalInstance.dismiss();
        }

        function selectCustomer($item) {
            if ($item) {
                $scope.selectedCustomer = $item.originalObject.ID;
                $scope.isEnabled = true;
            }
            else {
                $scope.selectedCustomer = -1;
                $scope.isEnabled = false;
            }
        }

        function selectionChanged($item) {
        }

        loadStockItems();
    }

})(angular.module('homeCinema'));

When an employee wants to rent a specific movie to a customer, first he must find the stock item using a code displayed on the DVD the customer requested to borrow. That’s why I highlighted the above lines in the rentMovieCtrl controller. Moreover, when he finally selects the stock item and the customer, he needs to press the Rent movie button and send a request to server with information about the selected customer and stock item as well. With all that said, we need to implement two more Web API actions. The first one will be in a new Web API Controller named StocksController and the second one responsible for movie rentals, inside the RentalsController.

    [Authorize(Roles="Admin")]
    [RoutePrefix("api/stocks")]
    public class StocksController : ApiControllerBase
    {
        private readonly IEntityBaseRepository<Stock> _stocksRepository;
        public StocksController(IEntityBaseRepository<Stock> stocksRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _stocksRepository = stocksRepository;
        }

        [Route("movie/{id:int}")]
        public HttpResponseMessage Get(HttpRequestMessage request, int id)
        {
            IEnumerable<Stock> stocks = null;

            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                stocks = _stocksRepository.GetAvailableItems(id);

                IEnumerable<StockViewModel> stocksVM = Mapper.Map<IEnumerable<Stock>, IEnumerable<StockViewModel>>(stocks);

                response = request.CreateResponse<IEnumerable<StockViewModel>>(HttpStatusCode.OK, stocksVM);

                return response;
            });
        }
    }

We need to create the StockViewModel class with its validator and of course the Automapper mapping.

public class StockViewModel : IValidatableObject
    {
        public int ID { get; set; }
        public Guid UniqueKey { get; set; }
        public bool IsAvailable { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var validator = new StockViewModelValidator();
            var result = validator.Validate(this);
            return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
        }
    }
public class StockViewModelValidator : AbstractValidator<StockViewModel>
    {
        public StockViewModelValidator()
        {
            RuleFor(s => s.ID).GreaterThan(0)
                .WithMessage("Invalid stock item");

            RuleFor(s => s.UniqueKey).NotEqual(Guid.Empty)
                .WithMessage("Invalid stock item");
        }
    }
protected override void Configure()
        {
            // code omitted
            Mapper.CreateMap<Stock, StockViewModel>();
        }

For the rental functionality we need add the following action in the RentalsController

        [HttpPost]
        [Route("rent/{customerId:int}/{stockId:int}")]
        public HttpResponseMessage Rent(HttpRequestMessage request, int customerId, int stockId)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                var customer = _customersRepository.GetSingle(customerId);
                var stock = _stocksRepository.GetSingle(stockId);

                if (customer == null || stock == null)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid Customer or Stock");
                }
                else
                {
                    if (stock.IsAvailable)
                    {
                        Rental _rental = new Rental()
                        {
                            CustomerId = customerId,
                            StockId = stockId,
                            RentalDate = DateTime.Now,
                            Status = "Borrowed"
                        };

                        _rentalsRepository.Add(_rental);

                        stock.IsAvailable = false;

                        _unitOfWork.Commit();

                        RentalViewModel rentalVm = Mapper.Map<Rental, RentalViewModel>(_rental);

                        response = request.CreateResponse<RentalViewModel>(HttpStatusCode.Created, rentalVm);
                    }
                    else
                        response = request.CreateErrorResponse(HttpStatusCode.BadRequest, "Selected stock is not available anymore");
                }

                return response;
            });
        }

The action accepts the customer id selected from the auto-complete textbox plus the stock’s item id. Once more we need to add the required view model and Automapper mapping as follow (no validator this time..).

public class RentalViewModel
    {
        public int ID { get; set; }
        public int CustomerId { get; set; }
        public int StockId { get; set; }
        public DateTime RentalDate { get; set; }
        public DateTime ReturnedDate { get; set; }
        public string Status { get; set; }
    }
protected override void Configure()
        {
            // code omitted
            Mapper.CreateMap<Rental, RentalViewModel>();
        }

From the Details view the user has the option to edit the movie by pressing the related button. This button redirects to route /movies/edit/:id where id is selected movie’s ID. Let’s see the related route definition in app.js.

.when("/movies/edit/:id", {
       templateUrl: "scripts/spa/movies/edit.html",
       controller: "movieEditCtrl"
 })

Here we ‘ll see for the first time how an angularJS controller can capture such a parameter from the route. Add the edit.html and its controller movieEditCtrl inside the movies folder.

<div id="editMovieWrapper">
    <hr>
    <div class="row" ng-if="!loadingMovie">
        <!-- left column -->
        <div class="col-xs-3">
            <div class="text-center">
                <img ng-src="../../Content/images/movies/{{movie.Image}}" class="avatar img-responsive" alt="avatar">
                <h6>Change photo...</h6>

                <input type="file" ng-file-select="prepareFiles($files)">
            </div>
        </div>

        <!-- edit form column -->
        <div class="col-xs-9 personal-info">
            <div class="alert alert-info alert-dismissable">
                <a class="panel-close close" data-dismiss="alert">×</a>
                <i class="fa fa-pencil-square-o"></i>
                Edit <strong>{{movie.Title}}</strong> movie. Make sure you fill all required fields.
            </div>
            <form class="form-horizontal" role="form" novalidate angular-validator name="editMovieForm" angular-validator-submit="UpdateMovie()">
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-8">
                            <label class="control-label">Movie title</label>
                            <input class="form-control" name="title" type="text" ng-model="movie.Title"
                                   validate-on="blur" required required-message="'Movie title is required'">
                        </div>

                        <div class="col-xs-4 selectContainer">
                            <label class="control-label">Genre</label>
                            <select ng-model="movie.GenreId" class="form-control black" ng-options="option.ID as option.Name for option in genres" required></select>
                            <input type="hidden" name="GenreId" ng-value="movie.GenreId" />
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-4">
                            <label class="control-label">Director</label>
                            <input class="form-control" type="text" ng-model="movie.Director" name="director"
                                   validate-on="blur" required required-message="'Movie director is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Writer</label>
                            <input class="form-control" type="text" ng-model="movie.Writer" name="writer"
                                   validate-on="blur" required required-message="'Movie writer is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Producer</label>
                            <input class="form-control" type="text" ng-model="movie.Producer" name="producer"
                                   validate-on="blur" required required-message="'Movie producer is required'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label">Release Date</label>
                            <p class="input-group">
                                <input type="text" class="form-control" name="dateReleased" datepicker-popup="{{format}}" ng-model="movie.ReleaseDate" is-open="datepicker.opened" datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                                       validate-on="blur" required required-message="'Date Released is required'" />
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                                </span>
                            </p>
                        </div>

                        <div class="col-xs-6">
                            <label class="control-label">Youtube trailer</label>
                            <input class="form-control" type="text" ng-model="movie.TrailerURI" name="trailerURI"
                                   validate-on="blur" required required-message="'Movie trailer is required'" ng-pattern="/^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+$/"
                                   invalid-message="'You must enter a valid YouTube URL'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label class="control-label">Description</label>
                    <textarea class="form-control" ng-model="movie.Description" name="description" rows="3"
                              validate-on="blur" required required-message="'Movie description is required'" />
                </div>

                <div class="form-group col-xs-12">
                    <label class="control-label">Rating</label>
                    <span component-rating="{{movie.Rating}}" ng-model="movie.Rating" class="form-control"></span>
                </div>
                <br />
                <div class="form-group col-xs-4">
                    <label class="control-label"></label>
                    <div class="">
                        <input type="submit" class="btn btn-primary" value="Update" />
                        <span></span>
                        <a class="btn btn-default" ng-href="#/movies/{{movie.ID}}">Cancel</a>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <hr>
</div>

spa-webapi-angular-08
We used an ng-file-select directive which in conjunction with the 3rd party library angular-file-upload will handle the movie image uploading through a Web API controller action. You can read more about file uploading using Web API and angularJS here, where I described the process step by step.

(function (app) {
    'use strict';

    app.controller('movieEditCtrl', movieEditCtrl);

    movieEditCtrl.$inject = ['$scope', '$location', '$routeParams', 'apiService', 'notificationService', 'fileUploadService'];

    function movieEditCtrl($scope, $location, $routeParams, apiService, notificationService, fileUploadService) {
        $scope.pageClass = 'page-movies';
        $scope.movie = {};
        $scope.genres = [];
        $scope.loadingMovie = true;
        $scope.isReadOnly = false;
        $scope.UpdateMovie = UpdateMovie;
        $scope.prepareFiles = prepareFiles;
        $scope.openDatePicker = openDatePicker;

        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        var movieImage = null;

        function loadMovie() {

            $scope.loadingMovie = true;

            apiService.get('/api/movies/details/' + $routeParams.id, null,
            movieLoadCompleted,
            movieLoadFailed);
        }

        function movieLoadCompleted(result) {
            $scope.movie = result.data;
            $scope.loadingMovie = false;

            loadGenres();
        }

        function movieLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function genresLoadCompleted(response) {
            $scope.genres = response.data;
        }

        function genresLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function loadGenres() {
            apiService.get('/api/genres/', null,
            genresLoadCompleted,
            genresLoadFailed);
        }

        function UpdateMovie() {
            if (movieImage) {
                fileUploadService.uploadImage(movieImage, $scope.movie.ID, UpdateMovieModel);
            }
            else
                UpdateMovieModel();
        }

        function UpdateMovieModel() {
            apiService.post('/api/movies/update', $scope.movie,
            updateMovieSucceded,
            updateMovieFailed);
        }

        function prepareFiles($files) {
            movieImage = $files;
        }

        function updateMovieSucceded(response) {
            console.log(response);
            notificationService.displaySuccess($scope.movie.Title + ' has been updated');
            $scope.movie = response.data;
            movieImage = null;
        }

        function updateMovieFailed(response) {
            notificationService.displayError(response);
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $scope.datepicker.opened = true;
        };

        loadMovie();
    }

})(angular.module('homeCinema'));

The movieEditController sets a $scope variable named isReadOnly so that the rating component be editable as we have already discussed. When the user submits the form, if the form is valid it checks if any selected file exists. If so, starts with the image file uploading and continues with the movie details updating. If user hasn’t selected an image then only the movie details are being updated (lines: 60-66). For the image file uploading feature, we injected an fileUploadService service in our controller. Let’s create that service inside the services folder.

(function (app) {
    'use strict';

    app.factory('fileUploadService', fileUploadService);

    fileUploadService.$inject = ['$rootScope', '$http', '$timeout', '$upload', 'notificationService'];

    function fileUploadService($rootScope, $http, $timeout, $upload, notificationService) {

        $rootScope.upload = [];

        var service = {
            uploadImage: uploadImage
        }

        function uploadImage($files, movieId, callback) {
            //$files: an array of files selected
            for (var i = 0; i < $files.length; i++) {
                var $file = $files[i];
                (function (index) {
                    $rootScope.upload[index] = $upload.upload({
                        url: "api/movies/images/upload?movieId=" + movieId, // webapi url
                        method: "POST",
                        file: $file
                    }).progress(function (evt) {
                    }).success(function (data, status, headers, config) {
                        // file is uploaded successfully
                        notificationService.displaySuccess(data.FileName + ' uploaded successfully');
                        callback();
                    }).error(function (data, status, headers, config) {
                        notificationService.displayError(data.Message);
                    });
                })(i);
            }
        }

        return service;
    }

})(angular.module('common.core'));

It is time to switch to server side again and support the image file uploading. We need to create two helper classes. The first one is the class that will contain the file upload result. Add the FileUploadResult class inside the Infrastructure.Core folder.

public class FileUploadResult
    {
        public string LocalFilePath { get; set; }
        public string FileName { get; set; }
        public long FileLength { get; set; }
    }

We also need to ensure that the request’s content for file uploading is MIME multipart. Let’s create the following Web API action filter inside the Infrastructure.Core folder.

public class MimeMultipart : System.Web.Http.Filters.ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (!actionContext.Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(
                    new HttpResponseMessage(
                        HttpStatusCode.UnsupportedMediaType)
                );
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {

        }
    }

And here’s the images/upload Web API action in the MoviesController.

        [MimeMultipart]
        [Route("images/upload")]
        public HttpResponseMessage Post(HttpRequestMessage request, int movieId)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                var movieOld = _moviesRepository.GetSingle(movieId);
                if (movieOld == null)
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid movie.");
                else
                {
                    var uploadPath = HttpContext.Current.Server.MapPath("~/Content/images/movies");

                    var multipartFormDataStreamProvider = new UploadMultipartFormProvider(uploadPath);

                    // Read the MIME multipart asynchronously
                    Request.Content.ReadAsMultipartAsync(multipartFormDataStreamProvider);

                    string _localFileName = multipartFormDataStreamProvider
                        .FileData.Select(multiPartData => multiPartData.LocalFileName).FirstOrDefault();

                    // Create response
                    FileUploadResult fileUploadResult = new FileUploadResult
                    {
                        LocalFilePath = _localFileName,

                        FileName = Path.GetFileName(_localFileName),

                        FileLength = new FileInfo(_localFileName).Length
                    };

                    // update database
                    movieOld.Image = fileUploadResult.FileName;
                    _moviesRepository.Edit(movieOld);
                    _unitOfWork.Commit();

                    response = request.CreateResponse(HttpStatusCode.OK, fileUploadResult);
                }

                return response;
            });
        }

For the Update movie operation we need to add an extra extension method as we did with the Customers. Add the following method inside the EntitiesExtensions class.

public static void UpdateMovie(this Movie movie, MovieViewModel movieVm)
        {
            movie.Title = movieVm.Title;
            movie.Description = movieVm.Description;
            movie.GenreId = movieVm.GenreId;
            movie.Director = movieVm.Director;
            movie.Writer = movieVm.Writer;
            movie.Producer = movieVm.Producer;
            movie.Rating = movieVm.Rating;
            movie.TrailerURI = movieVm.TrailerURI;
            movie.ReleaseDate = movieVm.ReleaseDate;
        }

Check that the Image property is missing since this property is changed only when uploading a movie image. Here’s the Update action in the MoviesController.

        [HttpPost]
        [Route("update")]
        public HttpResponseMessage Update(HttpRequestMessage request, MovieViewModel movie)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
                }
                else
                {
                    var movieDb = _moviesRepository.GetSingle(movie.ID);
                    if (movieDb == null)
                        response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid movie.");
                    else
                    {
                        movieDb.UpdateMovie(movie);
                        movie.Image = movieDb.Image;
                        _moviesRepository.Edit(movieDb);

                        _unitOfWork.Commit();
                        response = request.CreateResponse<MovieViewModel>(HttpStatusCode.OK, movie);
                    }
                }

                return response;
            });
        }

Add movie

The add movie feature is pretty much the same as the edit one. We need one template named add.html for the add operation and a controller named movieAddCtrl. Add the following files inside the movies folder.

<div id="editMovieWrapper">
    <hr>
    <div class="row">
        <!-- left column -->
        <div class="col-xs-3">
            <div class="text-center">
                <img ng-src="../../Content/images/movies/unknown.jpg" class="avatar img-responsive" alt="avatar">
                <h6>Add photo...</h6>

                <input type="file" ng-file-select="prepareFiles($files)">
            </div>
        </div>

        <!-- edit form column -->
        <div class="col-xs-9 personal-info">
            <div class="alert alert-info alert-dismissable">
                <a class="panel-close close" data-dismiss="alert">×</a>
                <i class="fa fa-plus"></i>
                Add <strong>{{movie.Title}}</strong> movie. Make sure you fill all required fields.
            </div>

            <form class="form-horizontal" role="form" novalidate angular-validator name="addMovieForm" angular-validator-submit="AddMovie()">
                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6 col-sm-4">
                            <label class="control-label">Movie title</label>
                            <input class="form-control" name="title" type="text" ng-model="movie.Title"
                                   validate-on="blur" required required-message="'Movie title is required'">
                        </div>

                        <div class="col-xs-6 col-sm-4 selectContainer">
                            <label class="control-label">Genre</label>
                            <select ng-model="movie.GenreId" class="form-control black" ng-options="option.ID as option.Name for option in genres" required></select>
                            <input type="hidden" name="GenreId" ng-value="movie.GenreId" />
                        </div>

                        <div class="col-xs-6 col-sm-4">
                            <label class="control-label">Stocks</label>
                            <div class="input-group number-spinner">
                                <span class="input-group-btn">
                                    <button class="btn btn-default" data-dir="dwn"><span class="glyphicon glyphicon-minus"></span></button>
                                </span>
                                <input type="text" class="form-control text-center" id="inputStocks" ng-model="movie.NumberOfStocks">
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" id="btnSetStocks" data-dir="up"><span class="glyphicon glyphicon-plus"></span></button>
                                </span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-4">
                            <label class="control-label">Director</label>
                            <input class="form-control" type="text" ng-model="movie.Director" name="director"
                                   validate-on="blur" required required-message="'Movie director is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Writer</label>
                            <input class="form-control" type="text" ng-model="movie.Writer" name="writer"
                                   validate-on="blur" required required-message="'Movie writer is required'">
                        </div>

                        <div class="col-xs-4">
                            <label class="control-label">Producer</label>
                            <input class="form-control" type="text" ng-model="movie.Producer" name="producer"
                                   validate-on="blur" required required-message="'Movie producer is required'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <div class="row">
                        <div class="col-xs-6">
                            <label class="control-label">Release Date</label>
                            <p class="input-group">
                                <input type="text" class="form-control" name="dateReleased" datepicker-popup="{{format}}" ng-model="movie.ReleaseDate" is-open="datepicker.opened" datepicker-options="dateOptions" ng-required="true" datepicker-append-to-body="true" close-text="Close"
                                       validate-on="blur" required required-message="'Date Released is required'" />
                                <span class="input-group-btn">
                                    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                                </span>
                            </p>
                        </div>

                        <div class="col-xs-6">
                            <label class="control-label">Youtube trailer</label>
                            <input class="form-control" type="text" ng-model="movie.TrailerURI" name="trailerURI"
                                   validate-on="blur" required required-message="'Movie trailer is required'" ng-pattern="/^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+$/"
                                   invalid-message="'You must enter a valid YouTube URL'">
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label class="control-label">Description</label>
                    <textarea class="form-control" ng-model="movie.Description" name="description" rows="3"
                              validate-on="blur" required required-message="'Movie description is required'" />
                </div>

                <div class="form-group col-xs-12">
                    <label class="control-label">Rating</label>
                    <span component-rating="{{movie.Rating}}" ng-model="movie.Rating" class="form-control"></span>
                </div>
                <br/>
                <div class="form-group col-xs-4">
                    <label class="control-label"></label>
                    <div class="">
                        <input type="submit" class="btn btn-primary" value="Submit movie" />
                        <span></span>
                        <a class="btn btn-default" ng-href="#/movies/{{movie.ID}}">Cancel</a>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <hr>
</div>

spa-webapi-angular-26

(function (app) {
    'use strict';

    app.controller('movieAddCtrl', movieAddCtrl);

    movieAddCtrl.$inject = ['$scope', '$location', '$routeParams', 'apiService', 'notificationService', 'fileUploadService'];

    function movieAddCtrl($scope, $location, $routeParams, apiService, notificationService, fileUploadService) {

        $scope.pageClass = 'page-movies';
        $scope.movie = { GenreId: 1, Rating: 1, NumberOfStocks: 1 };

        $scope.genres = [];
        $scope.isReadOnly = false;
        $scope.AddMovie = AddMovie;
        $scope.prepareFiles = prepareFiles;
        $scope.openDatePicker = openDatePicker;
        $scope.changeNumberOfStocks = changeNumberOfStocks;

        $scope.dateOptions = {
            formatYear: 'yy',
            startingDay: 1
        };
        $scope.datepicker = {};

        var movieImage = null;

        function loadGenres() {
            apiService.get('/api/genres/', null,
            genresLoadCompleted,
            genresLoadFailed);
        }

        function genresLoadCompleted(response) {
            $scope.genres = response.data;
        }

        function genresLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        function AddMovie() {
            AddMovieModel();
        }

        function AddMovieModel() {
            apiService.post('/api/movies/add', $scope.movie,
            addMovieSucceded,
            addMovieFailed);
        }

        function prepareFiles($files) {
            movieImage = $files;
        }

        function addMovieSucceded(response) {
            notificationService.displaySuccess($scope.movie.Title + ' has been submitted to Home Cinema');
            $scope.movie = response.data;

            if (movieImage) {
                fileUploadService.uploadImage(movieImage, $scope.movie.ID, redirectToEdit);
            }
            else
                redirectToEdit();
        }

        function addMovieFailed(response) {
            console.log(response);
            notificationService.displayError(response.statusText);
        }

        function openDatePicker($event) {
            $event.preventDefault();
            $event.stopPropagation();

            $scope.datepicker.opened = true;
        };

        function redirectToEdit() {
            $location.url('movies/edit/' + $scope.movie.ID);
        }

        function changeNumberOfStocks($vent)
        {
            var btn = $('#btnSetStocks'),
            oldValue = $('#inputStocks').val().trim(),
            newVal = 0;

            if (btn.attr('data-dir') == 'up') {
                newVal = parseInt(oldValue) + 1;
            } else {
                if (oldValue > 1) {
                    newVal = parseInt(oldValue) - 1;
                } else {
                    newVal = 1;
                }
            }
            $('#inputStocks').val(newVal);
            $scope.movie.NumberOfStocks = newVal;
        }

        loadGenres();
    }

})(angular.module('homeCinema'));

There are two things here to notice. The first one is that we have to setup somehow the number of stocks for that movie. Normally, each DVD stock item has its own code but just for convenience, we will create this code automatically in the controller. The other is that since we need the movie to exist before we upload an image, we ensure that the upload image operation comes second by uploading the image (if exists) after the movie has been added to database. When the operation is completed, we redirect to edit the image. Now let’s see the required Add Web API action in the MoviesController.

        [HttpPost]
        [Route("add")]
        public HttpResponseMessage Add(HttpRequestMessage request, MovieViewModel movie)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                if (!ModelState.IsValid)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
                }
                else
                {
                    Movie newMovie = new Movie();
                    newMovie.UpdateMovie(movie);

                    for (int i = 0; i < movie.NumberOfStocks; i++)
                    {
                        Stock stock = new Stock()
                        {
                            IsAvailable = true,
                            Movie = newMovie,
                            UniqueKey = Guid.NewGuid()
                        };
                        newMovie.Stocks.Add(stock);
                    }

                    _moviesRepository.Add(newMovie);

                    _unitOfWork.Commit();

                    // Update view model
                    movie = Mapper.Map<Movie, MovieViewModel>(newMovie);
                    response = request.CreateResponse<MovieViewModel>(HttpStatusCode.Created, movie);
                }

                return response;
            });
        }

Rental history

One last thing remained for our Single Page Application is to display a stastistic rental history for movies that have been rented at least one. We want to create a specific chart for each movie rented, that shows for each date how many rentals occurred. Let’s start from the server-side. We need the following ViewModel class that will hold the information required for each movie’s rental statistics.

public class TotalRentalHistoryViewModel
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Image { get; set; }
        public int TotalRentals
        {
            get
            {
                return Rentals.Count;
            }
            set { }
        }
        public List<RentalHistoryPerDate> Rentals { get; set; }
    }

    public class RentalHistoryPerDate
    {
        public int TotalRentals { get; set; }
        public DateTime Date { get; set; }
    }

Switch to the RentalsController and add the following GetMovieRentalHistoryPerDates private method. If you remember, we have already created a private method GetMovieRentalHistory to get rental history for a specific movie so getting the rental history for all movies should not be a problem.

private List<RentalHistoryPerDate> GetMovieRentalHistoryPerDates(int movieId)
        {
            List<RentalHistoryPerDate> listHistory = new List<RentalHistoryPerDate>();
            List<RentalHistoryViewModel> _rentalHistory = GetMovieRentalHistory(movieId);
            if (_rentalHistory.Count > 0)
            {
                List<DateTime> _distinctDates = new List<DateTime>();
                _distinctDates = _rentalHistory.Select(h => h.RentalDate.Date).Distinct().ToList();

                foreach (var distinctDate in _distinctDates)
                {
                    var totalDateRentals = _rentalHistory.Count(r => r.RentalDate.Date == distinctDate);
                    RentalHistoryPerDate _movieRentalHistoryPerDate = new RentalHistoryPerDate()
                    {
                        Date = distinctDate,
                        TotalRentals = totalDateRentals
                    };

                    listHistory.Add(_movieRentalHistoryPerDate);
                }

                listHistory.Sort((r1, r2) => r1.Date.CompareTo(r2.Date));
            }

            return listHistory;
        }

Only thing required here was to sort the results by ascending dates. And now the action.

        [HttpGet]
        [Route("rentalhistory")]
        public HttpResponseMessage TotalRentalHistory(HttpRequestMessage request)
        {
            return CreateHttpResponse(request, () =>
            {
                HttpResponseMessage response = null;

                List<TotalRentalHistoryViewModel> _totalMoviesRentalHistory = new List<TotalRentalHistoryViewModel>();

                var movies = _moviesRepository.GetAll();

                foreach (var movie in movies)
                {
                    TotalRentalHistoryViewModel _totalRentalHistory = new TotalRentalHistoryViewModel()
                    {
                        ID = movie.ID,
                        Title = movie.Title,
                        Image = movie.Image,
                        Rentals = GetMovieRentalHistoryPerDates(movie.ID)
                    };

                    if (_totalRentalHistory.TotalRentals > 0)
                        _totalMoviesRentalHistory.Add(_totalRentalHistory);
                }

                response = request.CreateResponse<List<TotalRentalHistoryViewModel>>(HttpStatusCode.OK, _totalMoviesRentalHistory);

                return response;
            });
        }

On the front-side we need to create a rental.html template and a rentalStatsCtrl controller. Add the following files inside the rental folder.

<hr />
<div class="row" ng-repeat="movie in rentals" ng-if="!loadingStatistics">
    <div class="panel panel-primary">
        <div class="panel-heading">
            <span>{{movie.Title}}</span>
        </div>
        <div class="panel-body">
            <div class="col-xs-3">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <strong class="ng-binding">{{movie.Title}} </strong>
                    </div>
                    <div class="panel-body">
                        <div class="media">
                            <img class="media-object center-block img-responsive center-block"
                                 ng-src="../../Content/images/movies/{{movie.Image}}" alt="{{movie.Title}}">
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-xs-9">
                <div class="panel panel-default text-center">
                    <div class="panel-body">
                        <div id="statistics-{{movie.ID}}">

                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div> <!-- end row -->

spa-webapi-angular-10

(function (app) {
    'use strict';

    app.controller('rentStatsCtrl', rentStatsCtrl);

    rentStatsCtrl.$inject = ['$scope', 'apiService', 'notificationService', '$timeout'];

    function rentStatsCtrl($scope, apiService, notificationService, $timeout) {
        $scope.loadStatistics = loadStatistics;
        $scope.rentals = [];

        function loadStatistics() {
            $scope.loadingStatistics = true;

            apiService.get('/api/rentals/rentalhistory', null,
            rentalHistoryLoadCompleted,
            rentalHistoryLoadFailed);
        }

        function rentalHistoryLoadCompleted(result) {
            $scope.rentals = result.data;

            $timeout(function () {
                angular.forEach($scope.rentals, function (rental) {
                    if (rental.TotalRentals > 0) {

                        var movieRentals = rental.Rentals;

                        Morris.Line({
                            element: 'statistics-' + rental.ID,
                            data: movieRentals,
                            parseTime: false,
                            lineWidth: 4,
                            xkey: 'Date',
                            xlabels: 'day',
                            resize: 'true',
                            ykeys: ['TotalRentals'],
                            labels: ['Total Rentals']
                        });
                    }
                })
            }, 1000);

            $scope.loadingStatistics = false;
        }

        function rentalHistoryLoadFailed(response) {
            notificationService.displayError(response.data);
        }

        loadStatistics();
    }

})(angular.module('homeCinema'));

You need to create the Morris chart after the data have been loaded, otherwise it won’t work. Notice that we also needed a different id for the element where the chart will be hosted and for that we used the movie’s id.
At this point, you should be able to run the HomeCinema Single Page Application. In case you have any problems, you can always download the source code from my Github account and follow the installation instructions.

Discussion

There are 2 main things I would like to discuss about the application we created. The first one is talk about scaling the application to support more roles. I’m talking about feature-level scaling for example suppose that you have an extra requirement that this web application should be accessible by external users too, the Customers. What would that mean for this application? The first thing popped up to my mind is more templates, more Web API controllers and last but not least even more JavaScript files. Guess what, that’s all true. The thing is that after such requirement our HomeCinema application will not be entirely Single Page Application. It could, but it would be wrong.
Let me explain what I mean by that. From the server side and the Web API framework, we have no restrictions at all. We were clever enough to create a custom membership schema with custom roles and then apply Basic Authentication through message handlers. This means that if you want to restrict new views only to customers, what you need to do is create a new Role named Customer and apply an [Authorize(Roles = “Customer”)] attribute to the respective controllers or actions. The big problem is on the front-end side. You could add more functionality, templates and controllers upon the same root module homeCinema but believe me, very soon you wouldn’t be able to maintain all those files. Instead, you can simply break your application in two Single Page Applications, one responsible for employees and another for customers. This means that you would have two MVC views, not one (one more action and view in the HomeController. Each of these views its a Single Page Application by itself and has its own module. And if you wish, you can go ever further and adapt a new architecture for the front-end side of your application. Let’s say that both the Admin and Customer roles, require really many views which means you still have the maintainability issue. Then you have to break your SPAs even more and have a specific MVC View (spa) for each sub-feature. All the relative sub-views can share the same Layout page which is responsible to render the required JavaScript files. Let’s see the architecture graphically.
spa-webapi-angular-27
_Layout pages may have a RenderScript Razor definitions as follow:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body data-ng-app="adminApp">
        <div class="wrapper">
            <div class="container">
                    <section>
                        @RenderBody()
                    </section>
                </div>
            </div>
        </div>

        @Scripts.Render("~/bundles/vendors")
        <script src="@Url.Content("~/Scripts/spa/app.js")" type="text/javascript"></script>

        @RenderSection("scripts", required: false)
        <script type="text/javascript">
            @RenderSection("customScript", required: false)
        </script>
    </body>
</html>

What we did here is render all the required css and vendor files but all MVC views having this _Layout page can (and must) render their own JavaScript files through the scripts and customScript sections. The first section concerns angularJS required components and the latter concerns custom JavaScript code. Remember the following part we added in the run method of the app.js file?

$(document).ready(function () {
            $(".fancybox").fancybox({
                openEffect: 'none',
                closeEffect: 'none'
            });

            $('.fancybox-media').fancybox({
                openEffect: 'none',
                closeEffect: 'none',
                helpers: {
                    media: {}
                }
            });

            $('[data-toggle=offcanvas]').click(function () {
                $('.row-offcanvas').toggleClass('active');
            });
        });

That’s the type of code that should be written in the customScript section of an MVC view. Now let’s how an MVC View would look like with this architecture.

@section scripts
{
    <script src="@Url.Content("~/Scripts/spa/customController.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/spa/customDirective.js")" type="text/javascript"></script>
}
@section footerScript
{
    angular.bootstrap(document.getElementById("customApp"),['customApp']);

    $(document).ready(function () {
       //...
    });
}
<div ng-app="customApp" id="customApp">
    <div ng-view></div>
</div>

One last thing I would like to discuss is the way data repositories are injected into Web API controllers. We followed a generic repository pattern and all we have to do in order to use a specific repository is inject it to the constructor. But let’s take a look the RentalsController constructor.

        private readonly IEntityBaseRepository<Rental> _rentalsRepository;
        private readonly IEntityBaseRepository<Customer> _customersRepository;
        private readonly IEntityBaseRepository<Stock> _stocksRepository;
        private readonly IEntityBaseRepository<Movie> _moviesRepository;

        public RentalsController(IEntityBaseRepository<Rental> rentalsRepository,
            IEntityBaseRepository<Customer> customersRepository, IEntityBaseRepository<Movie> moviesRepository,
            IEntityBaseRepository<Stock> stocksRepository,
            IEntityBaseRepository<Error> _errorsRepository, IUnitOfWork _unitOfWork)
            : base(_errorsRepository, _unitOfWork)
        {
            _rentalsRepository = rentalsRepository;
            _moviesRepository = moviesRepository;
            _customersRepository = customersRepository;
            _stocksRepository = stocksRepository;
        }

It’s kind of a mess, isn’t it? What if a controller requires much more repositories? Well, there’s another trick you can do to keep your controllers cleaner as much as possible and this is via the use of a Generic DataRepository Factory. Let’s see how we can accomplish it and re-write the RentalsController.
First, we need to add a new HttpRequestMessage extension which will allow us to resolve instances of IEntityBaseRepository<T> so switch to RequestMessageExtensions file and add the following method.

internal static IEntityBaseRepository<T> GetDataRepository<T>(this HttpRequestMessage request) where T : class, IEntityBase, new()
        {
            return request.GetService<IEntityBaseRepository<T>>();
        }

Here we can see how useful the IEntityBase interface is in our application. We can use this interface to resolve data repositories for our entities. Now let’s create the generic Factory. Add the following file inside the Infrastructure/Core folder.

public class DataRepositoryFactory : IDataRepositoryFactory
    {
        public IEntityBaseRepository<T> GetDataRepository<T>(HttpRequestMessage request) where T : class, IEntityBase, new()
        {
            return request.GetDataRepository<T>();
        }
    }

    public interface IDataRepositoryFactory
    {
        IEntityBaseRepository<T> GetDataRepository<T>(HttpRequestMessage request) where T : class, IEntityBase, new();
    }

This factory has a generic method of type IEntityBase that invokes the extension method we wrote before. We will use this method in the ApiControllerBase class, which the base class for our Web API controllers. Before doing this, switch to AutofacWebapiConfig class where we configured the dependency injection and add the following lines before the end of the RegisterServices function.

// code omitted

            // Generic Data Repository Factory
            builder.RegisterType<DataRepositoryFactory>()
                .As<IDataRepositoryFactory>().InstancePerRequest();

            Container = builder.Build();

            return Container;

I don’t want to change the current ApiControllerBase implementation so I will write a new one just for the demonstration. For start let’s see its definition along with its variables.

public class ApiControllerBaseExtended : ApiController
    {
        protected List<Type> _requiredRepositories;

        protected readonly IDataRepositoryFactory _dataRepositoryFactory;
        protected IEntityBaseRepository<Error> _errorsRepository;
        protected IEntityBaseRepository<Movie> _moviesRepository;
        protected IEntityBaseRepository<Rental> _rentalsRepository;
        protected IEntityBaseRepository<Stock> _stocksRepository;
        protected IEntityBaseRepository<Customer> _customersRepository;
        protected IUnitOfWork _unitOfWork;

        private HttpRequestMessage RequestMessage;

        public ApiControllerBaseExtended(IDataRepositoryFactory dataRepositoryFactory, IUnitOfWork unitOfWork)
        {
            _dataRepositoryFactory = dataRepositoryFactory;
            _unitOfWork = unitOfWork;
        }

        private void LogError(Exception ex)
        {
            try
            {
                Error _error = new Error()
                {
                    Message = ex.Message,
                    StackTrace = ex.StackTrace,
                    DateCreated = DateTime.Now
                };

                _errorsRepository.Add(_error);
                _unitOfWork.Commit();
            }
            catch { }
        }
    }

This base class holds references for all types of Data repositories that your application may need. The most important variable is the _requiredRepositories which eventually will hold the types of Data repositories a Web API action may require. its constructor has only two dependencies one of type IDataRepositoryFactory and another of IUnitOfWork. The first one is required to resolve the data repositories using the new extension method and the other is the one for committing database changes. Now let’s see the esense of this base class, the extended CreateHttpResponse method. Add the following methods in to the new base class as well.

protected HttpResponseMessage CreateHttpResponse(HttpRequestMessage request, List<Type> repos, Func<HttpResponseMessage> function)
        {
            HttpResponseMessage response = null;

            try
            {
                RequestMessage = request;
                InitRepositories(repos);
                response = function.Invoke();
            }
            catch (DbUpdateException ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.BadRequest, ex.InnerException.Message);
            }
            catch (Exception ex)
            {
                LogError(ex);
                response = request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
            }

            return response;
        }

        private void InitRepositories(List<Type> entities)
        {
            _errorsRepository = _dataRepositoryFactory.GetDataRepository<Error>(RequestMessage);

            if (entities.Any(e => e.FullName == typeof(Movie).FullName))
            {
                _moviesRepository = _dataRepositoryFactory.GetDataRepository<Movie>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(Rental).FullName))
            {
                _rentalsRepository = _dataRepositoryFactory.GetDataRepository<Rental>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(Customer).FullName))
            {
                _customersRepository = _dataRepositoryFactory.GetDataRepository<Customer>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(Stock).FullName))
            {
                _stocksRepository = _dataRepositoryFactory.GetDataRepository<Stock>(RequestMessage);
            }

            if (entities.Any(e => e.FullName == typeof(User).FullName))
            {
                _stocksRepository = _dataRepositoryFactory.GetDataRepository<Stock>(RequestMessage);
            }
        }

This method is almost the same as the relative method in the ApiControllerBase class, except that is accepts an extra parameter of type List<Type>. This list will be used to initialized any repositories the caller action requires using the private InitRepositories method. Now let’s see how the RentalsController Web API controller could be written using this new base class. I created a new RentalsExtendedController class so that you don’t have to change the one you created before. Let’s the the new definition. Ready?

    [Authorize(Roles = "Admin")]
    [RoutePrefix("api/rentalsextended")]
    public class RentalsExtendedController : ApiControllerBaseExtended
    {
        public RentalsExtendedController(IDataRepositoryFactory dataRepositoryFactory, IUnitOfWork unitOfWork)
            : base(dataRepositoryFactory, unitOfWork) { }
    }

Yup, that’s it, no kidding. Now let’s re-write the Rent action that rents a specific movie to a customer. This method requires 3 data repositories of types Customer, Stock and Rental.

        [HttpPost]
        [Route("rent/{customerId:int}/{stockId:int}")]
        public HttpResponseMessage Rent(HttpRequestMessage request, int customerId, int stockId)
        {
            _requiredRepositories = new List<Type>() { typeof(Customer), typeof(Stock), typeof(Rental) };

            return CreateHttpResponse(request, _requiredRepositories, () =>
            {
                HttpResponseMessage response = null;

                var customer = _customersRepository.GetSingle(customerId);
                var stock = _stocksRepository.GetSingle(stockId);

                if (customer == null || stock == null)
                {
                    response = request.CreateErrorResponse(HttpStatusCode.NotFound, "Invalid Customer or Stock");
                }
                else
                {
                    if (stock.IsAvailable)
                    {
                        Rental _rental = new Rental()
                        {
                            CustomerId = customerId,
                            StockId = stockId,
                            RentalDate = DateTime.Now,
                            Status = "Borrowed"
                        };

                        _rentalsRepository.Add(_rental);

                        stock.IsAvailable = false;

                        _unitOfWork.Commit();

                        RentalViewModel rentalVm = Mapper.Map<Rental, RentalViewModel>(_rental);

                        response = request.CreateResponse<RentalViewModel>(HttpStatusCode.Created, rentalVm);
                    }
                    else
                        response = request.CreateErrorResponse(HttpStatusCode.BadRequest, "Selected stock is not available anymore");
                }

                return response;
            });
        }

The only thing an action needs to do is initialize the list of data repositories needs to perform. If you download the source code of this application from my Github account you will find that I have re-written the RentalsController and the MoviesController classes with the extended base class. It’s up to you which of the base classes you prefer to use.

That’s it, we finally finished this post having described step by step how to build a Single Page Application using Web API and AngularJS. Let me remind you that this post has an e-book PDF version which you can download for free. You can download the source code for this project we built here where you will also find instructions on how to run the HomeCinema application. I hope you enjoyed this post or its e-book version as much I did writing both of them. Please take a moment and post your review at the comments section below.
spa-webapi-angular-28

In case you find my blog’s content interesting, register your email to receive notifications of new posts and follow chsakell’s Blog on its Facebook or Twitter accounts.

Facebook Twitter
.NET Web Application Development by Chris S.
facebook twitter-small

If you are interested to learn how to build Single Page Applications using the latest ASP.NET 5 framework, Angular 2 and TypeScript you can read my post Cross-platform Single Page Applications with ASP.NET 5, Angular 2 & TypeScript.


Viewing all 43 articles
Browse latest View live