Tag Archives: IoC

Asp.Net Core 2.0 – Passing data into a Model Using DI

Imagine you have an Asp.Net Core web page, and you would like to edit some data in a form, but you’d like to default that data to something (maybe initially specified in the Web.Config).

I’m sure there’s dozens of ways to achieve this; but this is one.

Let’s start with a bog standard MVC web app:

Step one is to define a model in which to hold your data; here’s mine:

public class CurrentAppData
{
    public string DataField1 { get; set; }
}

Let’s register that in the IoC container:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });
 
 
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
 
    services.AddTransient<CurrentAppData, CurrentAppData>(
        a => new CurrentAppData()
        {
            DataField1 = "test value"
        });

Next thing we’ll need is a View, and a corresponding view model to edit our data; here’s the view:

@model EditDataViewModel
@{
    ViewData["Title"] = "Edit Data";
}
 
<h2>@ViewData["Title"]</h2>
 
<div>
    <label>Change data here:</label>
    <input type="text" asp-for="EditData.DataField1" />
 
</div>

And now the view model (that is, the model that is bound to the view):

public class EditDataViewModel
{
    public EditDataViewModel(CurrentAppData editData)
    {
        EditData = editData;
    }
    public CurrentAppData EditData { get; set; }
}

The final step here is to adapt the controller so that the CurrentAppData object is passed through the controller:

public class EditDataController : Controller
{
    private readonly CurrentAppData _currentAppData;
 
    public EditDataController(CurrentAppData currentAppData)
    {
        _currentAppData = currentAppData;
    }
 
    public IActionResult EditData()
    {
        return View(new EditDataViewModel(_currentAppData));
 
 
    }
}

That works as far as it goes, and we now have the data displayed on the screen:

The next step is to post the edited data back to the controller; let’s change the HTML slightly:

<form asp-action="UpdateData" asp-controller="EditData" method="post" enctype="multipart/form-data">
    <label>Change data here:</label>
    <input type="text" asp-for="EditData.DataField1" />
    <br />
    <button type="submit" class="btn-default">Submit Changes</button>
</form>

We’ve added a submit button, which should cause the surrounding form element to execute whichever “method” it’s been told to (in this case, POST). It will look for an action on the controller called UpdateData, so we should create one:

public IActionResult UpdateData(EditDataViewModel editDataViewModel)
{
    System.Diagnostics.Debug.WriteLine(editDataViewModel.EditData.DataField1);
    return View("EditData", editDataViewModel);
}

Here, we’re accepting the EditDataViewModel from the view. However; when we run this, we get the following error:

Error:

InvalidOperationException: Could not create an instance of type ‘WebApplication14.Models.EditDataViewModel’. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the ‘editDataViewModel’ parameter a non-null default value.

Let’s first implement a fix for this, and then go into the whys and wherefores. The fix is actually quite straightforward; simply give the view model a parameterless constructor:

public class EditDataViewModel
{
    public EditDataViewModel()
    {
        
    }
 
    public EditDataViewModel(CurrentAppData editData)
    {
        EditData = editData;
    }
    public CurrentAppData EditData { get; set; }
}

The problem that we had here is that the `EditDataViewModel` that is returned to UpdateData is a new instance of the model. We can prove this by changing our code slightly:

Here, we’ve added a field called TestField1 to the model, and populated it just before we pass the model to the view; and on the post back, it’s gone. I’m not completely sure why the view model can’t be created by the middleware in the same way that the controller is; but that’s the subject of another post.

Finally, show the value back to the screen

To wrap up, we’ll just show the same value back to the screen; we’ll add an extra value to the model:

public class CurrentAppData
{
    public string DataField1 { get; set; }
 
    public string DisplayField1 { get; set; }
}

And we’ll just display it in the view:

<form asp-action="UpdateData" asp-controller="EditData" method="post" enctype="multipart/form-data">
    <label>Change data here:</label>
    <input type="text" asp-for="EditData.DataField1" />
    <br />
    <button type="submit" class="btn-default">Submit Changes</button>
    <br />
    <label>@Model.EditData.DisplayField1</label>
    <br />
</form>

Finally, we’ll copy that value inside the controller (obviously, this is simulating something meaningful happening), and then display the result:

public IActionResult UpdateData(EditDataViewModel editDataViewModel)
{
    editDataViewModel.EditData.DisplayField1 = editDataViewModel.EditData.DataField1;
    return View("EditData", editDataViewModel);
}

Let’s see what that looks like:

Using Entity Framework with IoC

One thing to bear in mind about using entity framework is that the DbContext object is not thread safe. This threw me when I first discovered it; it also confused why I was getting this error, as I was running in an environment that I thought was pretty much single threaded (in fact, it was an Azure function). I was using IoC and so the DbContext is shared across instances.

Because the error is based on, effectively, a race condition, your mileage may vary, but you’ll typically get one of the following errors:

System.InvalidOperationException: ‘The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.’

System.InvalidOperationException: ‘A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.’

I had a good idea what the cause of this might be, but in order to investigate, I set-up a console app accessing Entity Framework; in this case, EF Core.

If you’re here to find a solution, then you can probably scroll down to the section labelled To Fix.

Reproducing the error

To reproduce the error, we need to introduce Unity (I imagine the same would be true of any IoC provider, as the problem is more with the concept than the implementation):

Install-Package Unity

The next step is to abstract away the data access layer, in order to provide a base for our dependency injection:

As you can see, we’re introducing a data access layer – and we’re creating an interface for our DbContext. The idea being that this can subsequently be resolved by Unity. Here are our interfaces:

public interface IDataAccess
{
    List<string> GetData();
 
    void AddData(List<string> newData);
}

public interface IMyDbContext
{
    DbSet<MyData> MyData { get; set; }
 
    void SaveChanges();
}

public interface IDoStuff
{
    void DoStuffQuickly();
}

Finally, we can implement the interfaces:

public class DataAccess : IDataAccess
{
    private readonly IMyDbContext _myDbContext;
 
    public DataAccess(IMyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }
    public void AddData(List<string> newData)
    {
        foreach (var data in newData)
        {
            MyData myData = new MyData()
            {
                FieldOne = data
            };
            _myDbContext.MyData.Add(myData);
        }
        _myDbContext.SaveChanges();
    }
 
    public List<string> GetData()
    {
        List<string> data = 
            _myDbContext.MyData.Select(a => a.FieldOne).ToList();
 
        return data;
    }
}

The DbContext:

public class MyDbContext : DbContext, IMyDbContext
{
    public DbSet<MyData> MyData { get; set; }
 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string cn = @"Server=.\SQLEXPRESS;Database=test-db . . .";
        optionsBuilder.UseSqlServer(cn);
 
        base.OnConfiguring(optionsBuilder);
    }
 
    void IMyDbContext.SaveChanges()
    {
        base.SaveChanges();
    }
}

Let’s register a class to actually start the process:

public class DoStuff : IDoStuff
{
    private readonly IDataAccess _dataAccess;
    private readonly ILogger _logger;
 
    public DoStuff(IDataAccess dataAccess, ILogger logger)
    {
        _dataAccess = dataAccess;
        _logger = logger;
    }
 
    public void DoStuffQuickly()
    {
        _dataAccess.AddData(new List<string>()
        {
            "testing new data",
            "second test",
            "last test"
        });
 
        Parallel.For(1, 100, (a) =>
        {
            List<string> data = _dataAccess.GetData();
            foreach(string text in data)
            {
                _logger.Log(text);
            }
        });
    }
}

Now let’s register the types in out IoC container:

static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
 
    UnityContainer container = new UnityContainer();
    container.RegisterType<IDataAccess, DataAccess.DataAccess>();
    container.RegisterType<IMyDbContext, MyDbContext>();
    container.RegisterType<IDoStuff, DoStuff>();
    container.RegisterType<ILogger, ConsoleLogger>();
 
    container.Resolve<IDoStuff>().DoStuffQuickly();
}

(Console logger is just a class that calls Console.WriteLine()).

Now, when you run it, you’ll get the same error as above (or something to the same effect).

To fix

One possible fix is to simply instantiate a DbContext as you need it; for example:

public List<string> GetData()
{
    using (var myDbContext = new MyDbContext())
    {
        List<string> data =
            myDbContext.MyData.Select(a => a.FieldOne).ToList();
        return data;
    }
    
}

However, the glaring problem that this creates is unit testing. The solution is a simple factory class:

public interface IGenerateDbContext
{
    IMyDbContext GenerateNewContext();
}

public class GenerateDbContext : IGenerateDbContext
{
    public IMyDbContext GenerateNewContext()
    {
        IMyDbContext myDbContext = new MyDbContext();
 
        return myDbContext;
    }
}

We’ll need to make the DbContext implementation disposable:

public interface IMyDbContext : IDisposable
{
    DbSet<MyData> MyData { get; set; }
 
    void SaveChanges();
}

And, finally, we can change the data access code:

public void AddData(List<string> newData)
{
    using (IMyDbContext myDbContext = _generateDbContext.GenerateNewContext())
    {
        foreach (string data in newData)
        {
            MyData myData = new MyData()
            {
                FieldOne = data
            };
            myDbContext.MyData.Add(myData);
        }
        myDbContext.SaveChanges();
    }
}
 
public List<string> GetData()
{
    using (IMyDbContext myDbContext = _generateDbContext.GenerateNewContext())
    {
        List<string> data =
            myDbContext.MyData.Select(a => a.FieldOne).ToList();
        return data;
    }
    
}

Now we can use IoC to generate the DBContext, and therefore it is testable, but we don’t pass the DbContext itself around, and therefore there are no threading issues.

Using Unity With Azure Functions

Azure Functions are a relatively new kid on the block when it comes to the Microsoft Azure stack. I’ve previously written about them, and their limitations. One such limitation seems to be that they don’t lend themselves very well to using dependency injection. However, it is certainly not impossible to make them do so.

In this particular post, we’ll have a look at how you might use an IoC container (Unity in this case) in order to leverage DI inside an Azure function.

New Azure Functions Project

I’ve covered this before in previous posts, in Visual Studio, you can now create a new Azure Functions project:

That done, you should have a project that looks something like this:

As you can see, the elephant in the room here is there are no functions; let’s correct that:

Be sure to call your function something descriptive… like “Function1”. For the purposes of this post, it doesn’t matter what kind of function you create, but I’m going to create a “Generic Web Hook”.

Install Unity

The next step is to install Unity (at the time of writing):

Install-Package Unity -Version 5.5.6

Static Variables Inside Functions

It’s worth bearing mind that a static variable works the way you would expect, were the function a locally hosted process. That is, if you write a function such as this:

[FunctionName("Function1")]
public static object Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log)
{
    log.Info($"Webhook was triggered!");
    
    System.Threading.Thread.Sleep(10000);
    log.Info($"Index is {test}");
    return req.CreateResponse(HttpStatusCode.OK, new
    {
        greeting = $"Hello {test++}!"
    });
}

And access it from a web browser, or postman, or both as the same time, you’ll get incrementing numbers:

Whilst the values are shared across the instances, you can’t cause a conflict by updating something in one function while reading it in another (I tried pretty hard to cause this to break). What this means, then, is that we can store an IoC container that will maintain state across function calls. Obviously, this is not intended for persisting state, so you should assume your state could be lost at any time (as indeed it can).

Registering the Unity Container

One method of doing this is to use the Lazy object. This pretty much passed me by in .Net 4 (which is, apparently, when it came out). It basically provides a slightly neater way of doing this kind of thing:

private List<string> _myList;
public List<string> MyList
{
    get
    {
        if (_myList == null)
        {
            _myList = new List<string>();
        }
        return _myList;
    }
}

The “lazy” method would be:

public Lazy<List<string>> MyList = new Lazy<List<string>>(() =>
{
    List<string> newList = new List<string>();
    return newList;
});

With that in mind, we can do something like this:

public static class Function1
{
     private static Lazy<IUnityContainer> _container =
         new Lazy<IUnityContainer>(() =>
         {
             IUnityContainer container = InitialiseUnityContainer();
             return container;
         });

InitialiseUnityContainer needs to return a new instance of the container:

public static IUnityContainer InitialiseUnityContainer()
{
    UnityContainer container = new UnityContainer();
    container.RegisterType<IMyClass1, MyClass1>();
    container.RegisterType<IMyClass2, MyClass2>();
    return container;
}

After that, you’ll need to resolve the parent dependency, then you can use standard constructor injection; for example, if MyClass1 orchestrates your functionality; you could use:

_container.Value.Resolve<IMyClass1>().DoStuff();

In Practise

Let’s apply all of that to our Functions App. Here’s two new classes:

public interface IMyClass1
{
    string GetOutput();
}
 
public interface IMyClass2
{
    void AddString(List<string> strings);
}
public class MyClass1 : IMyClass1
{
    private readonly IMyClass2 _myClass2;
 
    public MyClass1(IMyClass2 myClass2)
    {
        _myClass2 = myClass2;
    }
 
    public string GetOutput()
    {
        List<string> teststrings = new List<string>();
 
        for (int i = 0; i <= 10; i++)
        {
            _myClass2.AddString(teststrings);
        }
 
        return string.Join(",", teststrings);
    }
}
public class MyClass2 : IMyClass2
{
    public void AddString(List<string> strings)
    {
        Thread.Sleep(1000);
        strings.Add($"{DateTime.Now}");
    }
}

And the calling code looks like this:

[FunctionName("Function1")]
public static object Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log)
{
    log.Info($"Webhook was triggered!");
 
    string output = _container.Value.Resolve<IMyClass1>().GetOutput();
    return req.CreateResponse(HttpStatusCode.OK, new
    {
        output
    });
}

Running it, we get an output that we might expect:

References

https://github.com/Azure/azure-webjobs-sdk/issues/1206