ASP.NET Core 2.0: How to Pass Data Into a Model Using DI
In this post, we take a look at how to pass default, real-time data into a model using dependency injection. This pretty cool stuff, read on to get started!
Join the DZone community and get the full member experience.
Join For FreeImagine 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 are 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.
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:
Published at DZone with permission of Paul Michaels, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments