CodeTrigger  

Code Generation For C#, WPF, WCF, SQL SERVER/ORACLE/MYSQL and Visual Studio 2013-2019

Tutorial - Using Dependency Injection (Ninject) in a test application with CodeTrigger
To work with these sample projects you will need CodeTrigger installed, Visual Studio 2013-2017, and a test database with some data.
This tutorial/sample is a follow-on from a previous tutorial/sample that you can find here: Tutorial - Generating a sample repository-pattern test application with CodeTrigger
You need to perform the steps in the previous article before carrying on with the steps in this article.
This guide has been updated for version 5.1.0.0 (released June 10th 2017)

Step 8 - Regenerate the code with Injection attributes enabled

Build the solution you created in the previous articule (steps 1 - 7) and confirm it builds successfully.
Return to the tab on the CodeTrigger user interface called 'Advanced Options'. In addition to the settings you chose previously, now tick 'Enable for dependency injection pattern (Unity & Ninject)
ie, The settings in the Advanced options section should be :

Generate interface definitions: Ticked
Generate using repository pattern (enable Mocking eg Moq): Ticked
Enable for dependency injection pattern (Unity & Ninject): Ticked
Generate data versioning and concurrency management: Ticked


Leave all other settings the same as in the previous tutorial and click the 'Generate Code' button. In addition to the code previously generated, CodeTrigger will decorate specific properties with a newly defined attribute, marking them as Injection points.

Step 9 - Configure a Ninject container & RUN!

Build the solution you created in the previous step (step 10) and confirm it builds successfully.
Now we create a basic test to demonstrate the usage of using CodeTrigger generated code with the Ninject container & Dependency Injection. The following steps are all applied in the SampleRepositoryPattern.Tests project you created in the previous tutorial

(i) Add a reference to the Ninject.dll assembly (we downloaded Ninject 2.2 version from the internet)
(ii) Create a folder called Extensions in the project.
(iii) Add the following class files to the MockRepository project
InjectionPointPolicyNinject.cs
The contents of this file are listed at the end of the article.

Thats it, you are ready to consume the business and data layer via your repository pattern and inject your dependencies.
In the UnitTest1 class, add the following using namespace declarations:
using Ninject;
using Ninject.Selection.Heuristics;
using SampleRepositoryPattern.Tests.Extensions;

Now all you have to do is resolve your business layer objects using the Ninject container rather than directly.
You can setup the container to do this by adding our custom injection point extension and registering the required types, like so:

/*Create repository factory and declare the repositories you need for your build configuration*/
RF repoFactory = RF.New();
ICategoriesRepository categoriesRepository = repoFactory.CategoriesRepository;
IProductsRepository productsRepository = repoFactory.ProductsRepository;

IKernel kernel = new StandardKernel();
kernel.Components.Add<IInjectionHeuristic, InjectionPointPolicyNinject>();
kernel.Bind<ICategoriesRepository>().ToConstant(categoriesRepository);
kernel.Bind<IProductsRepository>().ToConstant(productsRepository);
kernel.Bind<BOCategories>().To<BOCategories>();

And then to get the Ninject container to construct the business object instance with all the required components injected (in this example just the repository):
BOCategories boCategory = kernel.Get<BOCategories>();

As you can see, we dont explicitly create the CategoriesRepository or assign it to the boCategories object, the repository is created by the Ninject container
and injected into the boCategories object, because CodeTrigger has automatically decorated the BOCategories class with the attribute [InjectionPoint]
at specific points. This attribute has been designed to be agnostic so it is recognised in this example by the Ninject container, and in other samples by the Unity container, so you can swap the container implementation without changing the Business project tier or the definitions of the business objects.

The code listing below shows a complete test definition called create_update_delete_Category_usingNinject() which we can use add to our UnitTest1 class, and which creates, updates, and
deletes a test category object using a repository implementation that has been injected using the Ninject container.
And thats it, you have successfully created and unit tested using the Ninject container, a multi-tier, database enabled, Repository pattern & Dependency Injection based application, using CodeTrigger

We have used the Ninject container to instantiate our objects and Inject an implementation of a repository into the object, and we have done it without tie-ing our business objects definition and implementation to any particular container implementation, so that we know we can swap out our Ninject container and use Unity instead without regeneration or re-compiling the business tier of our project.



//new addition to our UnitTest1 class
[TestMethod]
public void create_update_delete_Category_usingNinject()
{
    string initialName = "My Test Category";
    string initialDescription = "initial desc";
    string updatedDescription = "updated desc";
    //setup the test
    IKernel kernel = new StandardKernel();
    kernel.Components.Add<IInjectionHeuristic, InjectionPointPolicyNinject>();

    RF repoFactory = RF.New();
    ICategoriesRepository categoriesRepository = repoFactory.CategoriesRepository;
    IProductsRepository productsRepository = repoFactory.ProductsRepository;

    kernel.Bind<ICategoriesRepository>().ToConstant(categoriesRepository);
    kernel.Bind<IProductsRepository>().ToConstant(productsRepository);
    kernel.Bind<BOCategories>().To<BOCategories>();

    BOCategories boCategory = kernel.Get<BOCategories>();

    try
    {
        //test saving a new category
        boCategory.CategoryName = initialName;
        boCategory.Description = initialDescription;
        boCategory.SaveNew();

        //test#1 that the versioning is working properly, here the version should be 0 when we load 
        int categoryID = (int)boCategory.CategoryID;
        boCategory.Init(categoryID);
        Assert.AreEqual(boCategory.CtrVersion, 0);

        //test updating an existing category
        boCategory.Description = updatedDescription;
        boCategory.Update();
        boCategory.Init(categoryID);
        Assert.AreEqual(boCategory.Description, updatedDescription);

        //test#2 that the concurrency & versioning is working properly, 
        //here the incremented version should be 1 after previous update 
        Assert.AreEqual(boCategory.CtrVersion, 1);

        //test that we can delete
        boCategory.Delete();

        try
        {
            boCategory = kernel.Get<BOCategories>();
            boCategory.Init(categoryID);
            Assert.Fail("Test failed, object has been deleted and should no longer exist");
        }
        catch (Exception ex)
        {
            //test passed because we are expecting this exception as we are trying to initialise 
            //with an id belonging to an object that no longer exists (since we deleted it above)
            StringAssert.Contains(ex.Message, "Object reference");
        }
    }
    catch
    {
        //catch all to try and get rid of this test category so we can run this test again
        boCategory.Delete();
    }
}


//InjectionPointPolicyNinject.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject;
using Ninject.Components;
using Ninject.Selection.Heuristics;
using System.Reflection;
using SampleRepositoryPattern.Business.Repository.Interfaces;

namespace SampleRepositoryPattern.Tests.Extensions
{
    public class InjectionPointPolicyNinject : NinjectComponent, IInjectionHeuristic
    {
        public bool ShouldInject(MemberInfo memberInfo)
        {
            return memberInfo.IsDefined(typeof(InjectionPointAttribute), true);  
        }
    }

}