Entity creation throws "No parameterless constructor defined for this object" - asp.net-mvc

I'm working on a basic MVC5/EF6 application and am running into the following error:
No parameterless constructor defined for this object.
This happens when I use the default Create Action and View that are scaffolded by Visual Studio 2013 when you create a new Controller. I have not adjusted anything within those generated files (TestItemController, Views/TestItem/Create.cshtml). My entities on which the controller is scaffolded look like this:
public class TestItem
{
private Category _category;
// Primary key
public int TestItemId { get; set; }
public int CategoryId { get; set; }
public string TestColumn { get; set; }
public virtual Category Category {
get { return _category; }
set { _category = value; }
}
protected TestItem()
{
}
public TestItem(Category category)
{
_category = category;
}
}
public class Category
{
private ICollection<TestItem> _testItems;
// Primary key
public int CategoryId { get; set; }
public string Description { get; set; }
public virtual ICollection<TestItem> TestItems
{
get { return _faqs; }
set { _faqs = value; }
}
public Category()
{
_testItems = new List<TestItem>();
}
}
I'm guessing this is due to the TestItem class having the constructor taking in a Category object, which is there to keep the domain model anemic. A TestItem cannot be created without a Category. But as far as I know the protected parameterless constructor should be used by EF in this exact case when lazy loading etc.
What's going on here, or what am I doing wrong?
UPDATE:
The controller looks like this (trimmed):
public class TestItemsController : Controller
{
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "TestItemId,OtherColumns")] TestItem testItem)
{
if (ModelState.IsValid)
{
db.TestItems.Add(testItem);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(testItem);
}
}

Sure, EF can use protected constructors, but scaffolding creates action methods for creating a new item. These action methods require a parameterless public constructor.
You can find some details of these create methods here.

Related

What unit test method can I write for the Delete action using xunit and moq?

public class Product
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public int Price { get; set; }
}
public class ProductService : IProductService
{
private readonly DataBaseContext _context;
public ProductService(DataBaseContext context)
{
_context = context;
}
public Product GetById(long id)
{
return _context.Products.Find(id);
}
public void Remove(long id)
{
var product = _context.Products.Find(id);
_context.Products.Remove(product);
_context.SaveChanges();
}
}
I created the following code using scaffolding and I want to write a test using xunit and moq:
public class ProductController : Controller
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
// GET: ProductController/Delete/5
public ActionResult Delete(int id)
{
return View(_productService.GetById(id));
}
// POST: ProductController/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id, IFormCollection collection)
{
try
{
_productService.Remove(id);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
}
I installed the following packages on the main project And I made the relevant settings in startup:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.InMemory
I installed the following packages on the test project:
Moq
What unit test method can I write for the Delete action?
I would write 2 tests specifically for the delete function of your service. For the controller, I would probably do test using WebApplicationFactory to do the whole flow (caveat, I’m not 100% sure this is possible with mvc controllers).
For the service test, I would want to know: what happens when I pass in an id that exists? What happens when I pass in an id that doesn’t exist (Should it throw an exception etc)?
Both simply done by building your in memory database in your test and calling your service code. You can then validate that the database has removed the record with id “x”.
Example:
[Fact]
Public void WhenDeletingItsmThatExistsInDatabase_ShouldRemoveFromDatabase()
{
var ctx = new DbContext();
ctx.Products.Add(product with id 1);
ctx.SaveChanges():
var sut = new ProductService(ctx);
sut.Delete(1);
Assert.Empty(ctx.Products);
}

Multiple model parameters in action

I have an action that will be called with optional querystring parameters. These parameters however are contained in different view models. When I try and add these models to my list of parameters, only a single one is filled and the others are always null. With the exception of an empty query string, where all models are instantiated with defaults.
It is not an option to nest these models for the reason that I don't want the nested property name to be visible in the querystring. So unless that can be circumvented somehow, that would also be a viable solution.
I noticed that, when creating a quick override of the DefaultModelBuilder, all models are parsed but the end result is still that only one model is actually assigned.
This is my scenario:
public ActionResult Index(ModelA ma, ModelB ba)
{
return Content("ok");
}
public class ModelA
{
public string Test { get; set; }
public string Name { get; set; }
}
public class ModelB
{
public int? SomeInteger { get; set; }
public int? TestInteger { get; set; }
}
Desired querystring:
index?Test=Hi&SomeInteger=7
What I want to avoid:
index?ModelA.Test=Hi&ModelB.SomeInteger=7
You can try to make a class combining those two:
public class ModelPair
{
public ModelA A { get; set; }
public ModelB B { get; set; }
}
And then with
public ActionResult Index(ModelPair mp)
{
return Content("ok");
}
You can do ?A.Test=blah&B.SomeInteger=42
I ended up delving in to creating my own custom model binder that does recursive binding. So long as property names are not re-used, which is not a thing that will happen in my models anyway, this fixes my issue of not exposing the property names of the nested model classes.
So now I have the following class structure:
public class ModelA
{
public string Test { get; set; }
public string Name { get; set; }
}
public class ModelB
{
public int? SomeInteger { get; set; }
public int? TestInteger { get; set; }
}
public class ViewModel
{
public ModelA ModelA { get; set; }
public ModelB ModelB { get; set; }
}
And the action now looks like this
public ActionResult Index(ViewModel model)
{
return Content("ok");
}
Which will let me use the following querystring without exposing the ugly property names:
index?Test=Hi&SomeInteger=7&Name=Yep&TestInteger=72
Of course I haven't tested this for an extensive period yet so I have no idea what problems lurk around the corner, but all of the nested models are now properly filled with the data from the querystring and the model classes can be easily re-used : )
public class RecursiveModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = base.BindModel(controllerContext, bindingContext);
if (model != null)
{
var properties = bindingContext.ModelType.GetProperties().Where(x => x.PropertyType.IsClass && !x.PropertyType.Equals(typeof(string)) );
foreach(var property in properties)
{
var resursiveBindingContext = new ModelBindingContext(bindingContext)
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, property.PropertyType)
};
var recursiveModel = BindModel(controllerContext, resursiveBindingContext);
property.SetValue(model, recursiveModel);
}
}
return model;
}
}
As far as I know, the Default Model Binder cannot do that. we have to implement custom Model Binder as follow.
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var query = bindingContext.HttpContext.Request.Query;
var modelb = new ModelB();
if (query.TryGetValue($"{bindingContext.ModelName}.{nameof(modelb.SomeInteger)}", out var someInteger))
{
modelb.SomeInteger = Convert.ToInt32(JsonConvert.DeserializeObject(someInteger).ToString());
}
if (query.TryGetValue($"{bindingContext.ModelName}.{nameof(modelb.TestInteger)}", out var testInteger))
{
modelb.TestInteger = Convert.ToInt32(JsonConvert.DeserializeObject(testInteger).ToString());
}
bindingContext.Result = ModelBindingResult.Success(modelb);
return Task.FromResult(modelb);
}
}
In the controller Action we can use Binder as follow
public IActionResult Index(ModelA modelA, [ModelBinder(typeof(CustomModelBinder))]ModelB modelB)
{
return Json(new {modelA, modelB});
}
And in querystring we can have prefix to differentiate each model.
?modelA.test="MATests"&modelA.Name="modelANameValue"&modelB.SomeInteger="5"
Please find working sample here on github

Instantiating DbContext within ViewModel VS passing controller's instance

I have an editing ViewModel that does some DB work.
Does it make any difference which of these approaches I take?
Pass in the controller's DBContext instance to the ViewModel (example 1 below)
Create a new DBContext instance within the ViewModel itself (example 2 below)
If I pass in the controller's DBContext instance, that means .Dispose() will be called on it at some point (assuming there is the .Dispose() method in the controller). By creating the DBContext instance within the ViewModel method itself, this never happens. Does that matter?
Example 1:
public class ModelOne
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
// etc...
}
public class EditModelOnePropAViewModel
{
public EditModelOnePropAViewModel(ApplicationDbContext db, int id)
{
ID = id;
PropA = db.ModelOneDbSet
.Where(i => i.ID == id)
.Select(i => i.PropA)
.FirstOrDefault();
}
public void SaveChanges(ApplicationDbContext db)
{
var modelOne = db.ModelOneDbSet.FirstOrDefault(i => i.ID == ID);
modelOne.PropA = PropA;
db.SaveChanges();
}
public string PropA { get; set; }
public int ID { get; set; }
}
public class ControllerOne : Controller
{
private ApplicationDbContext DB = new ApplicationDbContext() { };
[HttpGet]
public ActionResult Edit(int id)
{
var viewModel = new EditModelOnePropAViewModel(DB, id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelOnePropAViewModel postedModel)
{
if (ModelState.IsValid)
{
postedModel.SaveChanges(DB);
return RedirectToAction("index");
}
return View(postedModel);
}
}
Example 2:
public class ModelTwo
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
public string PropC { get; set; }
// etc...
}
public class EditModelTwoPropAViewModel
{
public EditModelTwoPropAViewModel(int id)
{
using (var db = new ApplicationDbContext())
{
ID = id;
PropA = db.ModelTwoDbSet
.Where(i => i.ID == id)
.Select(i => i.PropA)
.FirstOrDefault();
}
}
public void SaveChanges()
{
using (var db = new ApplicationDbContext())
{
var modelTwo = db.ModelTwoDbSet.FirstOrDefault(i => i.ID == ID);
modelTwo.PropA = PropA;
db.SaveChanges();
}
}
public string PropA { get; set; }
public int ID { get; set; }
}
public class ControllerTwo : Controller
{
[HttpGet]
public ActionResult Edit(int id)
{
var viewModel = new EditModelTwoPropAViewModel(id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(EditModelTwoPropAViewModel postedModel)
{
if (ModelState.IsValid)
{
postedModel.SaveChanges();
return RedirectToAction("index");
}
return View(postedModel);
}
}
View models should be simple POCO's. Classes with properties which are needed for your view. Nothing else.
public class CustomerViewModel
{
public int Id {set;get;}
public string FirstName {set;get;}
public string LastName {set;get;}
}
And in your controller,
public ActionResult Edit(int id)
{
using(var db=new YourDbContext())
{
var c= db.Customers.FirstOrDefault(s=>s.Id==id);
if(c!=null)
{
var vm= new CustomerViewModel { Id=id,
FirstName=c.FirstName,
LastName=c.LastName
};
return View(vm);
}
}
return View("NotFound");
}
Even better approach is creating an abstraction on your data access layer so that your controller code will not have any idea what data access technology you are using. This will help you to unit test your controller actions.
So basically you will create an abstraction like this
public interface ICustomerRepository
{
CustomerDto GetCustomer(ind id);
}
public class EFCustomerRepository : ICustomerRepository
{
public CustomerDto GetCustomer(int id)
{
using(var db=new YourDbContext())
{
var c= db.Customers.FirstOrDefault(s=>s.Id==id);
if(c!=null)
{
return new CustomerDto { Id=id, FirstName = c.FirstName };
}
}
return null;
}
}
Assuming you have a class called CustomerDto which is a data structure to represent the customer entity and is accessible to both the Web code and the data access code( You can keep in a Common Project and add reference to that in both the projects)
And in your controller, you will be using an implementation of the ICustomerRepository
public CustomerController : Controller
{
ICustomerRepository repo;
public CustomerController(ICustomerRepository repo)
{
this.repo =repo;
}
// You will use this.repo in your action methods now.
}
This will help you to use a fake implementation of ICustomerRepository in your unit tests. You can use mocking libraries like Moq/FakeItEasy to do that.
You can use a dependency injection framework like Unity,StructureMap or Ninject to inject the concrete implementation of the interface in your app.
A ViewModel is MVVM, not MVC. The Model in MVC is the same thing as the Model in MVVM. That pattern is called Model-View-Viewmodel
In MVC the controller is responsible for the page flow. Nothing else. A DbContext has nothing to do with page flow.
The Model is responsible for the business logic. A DbContext has a lot to do with the business logic. If you want to use the database this close to the presentation layer, the Model should create the DbContext.
Your controller example 2 is better than the one proposed by Shyju. Now you are able to test the page flow without being required to use a DbContext. And since the DbContext has nothing to do with page flow, it makes much more sense.
Some nitpicking: A POCO is not an object with public properties and no logic. It is a class that is not dependent on anything framework specific (e.g. a specific interface to implement).

Why is my MVC ViewModel Empty?

Model:
public class Service
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
ViewModel:
public class FullPrice
{
public IEnumerable<Service> Services { get; set; }
}
View:
#model hotel.Models.FullPrice
#foreach(Service service in Model.Services)
{
//
}
My view model is NULL. Why?
Model would be NULL because there is no currently existing instance as the view is being executed.
Usually, the way to pass a model instance into a view would be in the corresponding action method like
public View myActionMethod()
{
hotel.Models.FullPrice model = new hotel.Models.FullPrice();
model.Services = new List<Service>();//Also prevent NULL exception with this property
return View(model);
}
I guess it's not the model what is null, but the Services. Change the action code to
FullPrice model = new FullPrice
{
Services = new List<Service>()
};
return View(model);
Change your ViewModel code to below
public class FullPrice
{
public FullPrice()
{
this.Services = new List<Service>();
}
public IEnumerable<Service> Services { get; set; }
}
so the Services property won't be null when you do this
FullPrice model = new FullPrice();

Why is not Entity Framework initializing my navigation collections?

I'm creating an ASP.NET MVC4 tournament administration system which as a containing class Tournament with a collection of Round objects. I'm new to EF Code First, but I've understood that EF is supposed to initialize my collection objects with observed proxy classes upon instantiation, as long as I've declared them as virtual. Why are they null when I try to add elements to them from the ASP.NET controller in the code below?
public class Tournament
{
public int Id { get; set; }
[Required]
public String Name { get; set; }
public virtual ICollection<Contestant> Contestants { get; set; }
public virtual ICollection<Round> Rounds { get; set; }
public void InitializeDefaults()
{
var round = new Round("Round 1");
Rounds.Add(round); // Generates NullReferenceException when called from controller
}
}
public class Round
{
public long Id { get; set; }
public int MaxContestants { get; set; }
public String Title { get; set; }
public Round() { }
public Round(string title) : this()
{
Title = title;
}
}
public class MainController {
// (...)
[HttpPost]
public ActionResult CreateTournament(Tournament tournament)
{
var db = new DataContext();
var dbTournament = db.Tournaments.Create();
dbTournament.Name = tournament.Name;
db.Tournaments.Add(dbTournament);
dbTournament.InitializeDefaults();
db.SaveChanges();
return RedirectToRoute(new { action = "Main", tournamentId = dbTournament.Id });
}
}
public class DataContext : DbContext
{
public IDbSet<Tournament> Tournaments { get; set; }
public IDbSet<Judge> Judges { get; set; }
public IDbSet<Contestant> Contestants { get; set; }
}
update
Reinitializing the dataContext after saving the entity, solves my problem. But in not the right way. Original question stands.
Altered CreateTournament-method
[HttpPost]
public ActionResult CreateTournament(Tournament tournament)
{
var db = App.ServiceLocator.GetDataBase();
db.Tournaments.Add(tournament);
db.SaveChanges();
db.Dispose();
// Reinitializing the datacontext
db = App.ServiceLocator.GetDataBase();
var dbTournament = db.GetTournament(tournament.Id);
dbTournament.InitializeDefaults();
db.SaveChanges();
return RedirectToRoute(new { action = "Main", tournamentId = dbTournament.Id });
}
It would only work as you expect if you were attaching the created dbTournament to the context. Only in that case creating the collection and preparing it for lazy loading makes sense. But you are adding the dbTournament as a new entity and in this case there can't be any dependent Rounds in the database that could refer to the new tournament, hence a lazy loading query wouldn't return a result anyway and EF doesn't create the collection for lazy loading in the first place.
You could apply tricks like attaching the dbTournament first and after that adding it to the context. But that would only yield unnecessary database queries triggered by lazy loading with no result and is rather hacky. In my opinion the simplest and most reasonable solution is either the standard collection initialization in a default constructor...
public Tournament()
{
Rounds = new HashSet<Round>();
}
...or at least a guard in your InitializeDefaults method:
public void InitializeDefaults()
{
var round = new Round("Round 1");
if (Rounds == null)
Rounds = new HashSet<Round>();
Rounds.Add(round);
}

Resources