Entity structure code First use context in controller
I finally got my code to work as intended, but I can't figure out why the previous way I had it installed didn't work. I kept getting an exception for a link reference, specifically: "Object reference not set on an object instance. What I want to do is pass the BlogDb model to readonly and use LINQ queries on the controller, but it seems like every I have to go through in the BlogDb model.
private readonly BlogDb model;
public PostsController(BlogDb model)
{
this.model = model;
}
public ActionResult Index()
{
return View();
}
[ValidateInput(false)]
public ActionResult Update(int? id, string title, string body, DateTime date, string tags)
{
var _db = new BlogDb();
if (!IsAdmin)
{
RedirectToAction("Index");
}
if (ModelState.IsValid)
{
Post post = GetPost(id);
post.Title = title;
post.Body = body;
post.Date = date;
post.Tags.Clear();
tags = tags ?? string.Empty;
string[] tagNames = tags.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string tagName in tagNames)
{
post.Tags.Add(GetTag(tagName));
}
if (!id.HasValue || id == 0)
{
_db.Posts.Add(post);
}
_db.SaveChanges();
return RedirectToAction("Details", new { id = post.Id });
}
return View();
}
public ActionResult Edit(int? id)
{
Post post = GetPost(id);
var tagList = new StringBuilder();
foreach (Tag tag in post.Tags)
{
tagList.AppendFormat("{0}", tag.Name);
}
ViewBag.Tags = tagList.ToString();
return View(post);
}
public Tag GetTag(string tagName)
{
//return model.Tags.FirstOrDefault(x => x.Name == tagName) ?? new Tag() { Name = tagName };
return new Tag() { Name = tagName };
}
private Post GetPost(int? id)
{
if (id.HasValue && id != 0)
{
return model.Posts.FirstOrDefault(x => x.Id == id);
}
return new Post();
}
When I had the following piece of code, it kept throwing an object instance exception when trying to SaveChanges.
if (!id.HasValue || id == 0)
{
model.Posts.Add(post);
}
model.SaveChanges();
So I had to end up throwing the local model instance and using it that way.
var _db = new BlogDb();
And further down
if (!id.HasValue || id == 0)
{
_db.Posts.Add(post);
}
_db.SaveChanges();
I just don't understand how to work with the database context? Also, what is the purpose of transitioning a constructor to a model?
source to share
private readonly BlogDb model;
public PostsController(BlogDb model)
{
this.model = model;
}
public PostsController()
{
}
You have 2 constructors - only one of them sets the field model
. The default controller factory will ever invoke a parameterless constructor without specifying parameters, leaving one unused parameterized (and therefore the field model
remains null
).
That's why you get NullReferenceException
when you access model
: a field link has never been assigned!
It:
var _db = new BlogDb();
This is a sloppy fix. Anchor your constructors instead:
public PostsController()
: this(new BlogDb())
{
}
public PostsController(BlogDb model)
{
this.model = model;
}
This way, the field model
will be assigned no matter what constructor is used to create the controller.
Note that this is called bastard injection. dependency-injectionanti-pattern. Either you are DI or you are not - in other words, if you are going to use the default controller factory, do this instead:
public PostsController()
{
this.model = new BlogDb();
}
or even:
private readonly BlogDb model = new BlogDb();
And then you can remove all constructors ... and you have a tightly coupled piece of code.
One good (excellent) read on this topic, Dependency Injection in .NET , by Mark Semann .
IDisposable
Your BlogDb
inheriting EF DbContext
, it implements the interface IDisposable
, which means you need to think about how you are going to call Dispose()
in that instance.
By making this a private instance-level field [and not doing proper dependency injection], you've made your life very difficult.
You want to get an instance for the request - because you want this resource to be in scope only as long as it should be, and that means a new instance for each method ActionResult
; something like that:
IEnumerable<Post> posts;
using (var context = new BlogDb())
{
posts = context.Posts.ToList();
return View(posts);
}
The block using
guarantees that it IDisposable.Dispose()
will be called on context
. The only other option is to call context.Dispose()
manually; if context
is some kind of field private readonly BlockDb _context;
, everything will work only until the selected context is used.
Typically, the object responsible for instantiation IDisposable
is also responsible for deleting it.
Constructor injection
Now if you implement your own IControllerFactory use an IoC container and wire it in global.asax
, that's a different story.
Now, the object responsible for instantiating the one-time context is also responsible for deleting it and can do this once per request, i.e. injecting a new instance through the constructor on every request and then deleting it.
This is when you have an private readonly BlogDb _context;
instance-level constructor injected by an IoC container because you know whoever gives you that instance will dispose of it properly (because you configured it that way).
You won't need the default constructor anymore, so keep things simple with a single constructor, which puts statically documenting every dependency that a class has through the constructor signature:
private readonly BlogDb _context;
public PostsController(BlogDb context)
{
_context = context;
}
The basis of dependency injection depends on abstractions, not implementation. By doing this ...
private readonly IPostsService _service;
public PostsController(IPostsService service)
{
_service = service;
}
Now you can inject BlogDb
(given BlogDb : IBlogService
) ... or anything else that implements this interface, perhaps a mock implementation that allows you to write unit tests that can span all controller actions without getting into the database.
Depending directly on BlogDb
, the controller is tied to the specific implementation details of this class; it is tightly bound. Loose cohesion is achieved depending on the abstraction.
source to share
The field model
will be null
because you didn't tell MVC how it should handle controllers with non-standard constructors. MVC uses something called a controller factory, which by default will select a parameterless constructor, if it exists, when you create your controllers. If you remove the parameterless constructor, you will probably get an exception.
If you want to be able to "inject" parameters into your controllers, you need to do one of the following:
- Implement the
IControllerFactory
interface and install it withControllerBuilder.Current.SetControllerFactory
. - we implement the interface
IDependencyResolver
and install it withDependencyResolver.SetResolver
.
The implementation of these interfaces is usually done using an IoC container. There are existing implementations for the most popular ones that you can just plug in and go. I suggest you read more about MVC and Dependency Injection .
source to share