Ok!
I have to say both technology are great. Although there seems that something I do not get it.
You have a data in you database (and let say you want to show data from a table that has references to other tables).
I have a model with List or IEnumerable or IQueryable or whatever...
So in my view I want do foreach through the list of object and take advantage of cool feature of references to other tables. No problem in controller while you are in
using (var datatabse = new MyEntity)
{
}
But when you get out of using db has disposed and you get common error The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
So i do not see other options as creating in memory copies of entity objects...but you loose all cool EF4 references and you have to manually load data first in your model and then with foreach show it on the view.
So instead of List<(EF4Type)> or IEnumerable<(EF4Type)> or IQueryable<(EF4Type)>
you have to do List<(MyCustomHelperClass)> where MyCustomHelperClass represents a class with properties similiar to entity objects and probably some additional beacuse you do not have access to properties of referenced tables Then you have to do foreach and Load data into this List and the another #foreach on the view with Razor to show all.
Twice as much work and if project is big...you can see a bigger picture of how manny those helperClasses you need. Was all this cool new technology really meant to be used in that way?....or am I missing something.
You are probably getting that error when you reference a lazy loaded property in your view. You should eager load everything you need in the Controller before passing it to the View.
See Loading Related Objects (Entity Framework).
The following example will cause all courses to be retrieved with the departments in the same query. This is eager loading.
// Load all departments and related courses
var departments1 = context.Departments
.Include(d => d.Courses)
.ToList();
Without the Include() part, courses could be retrieved later (possibly after your context has been disposed in the view). This is called lazy loading.
Along with eager loading as remembered by jrummell, there's also another way of loading related entries, it's explicit loading. Let's suppose you have a User entity, with many Groups entities related to it. You can explicitly load them:
var user = context.Users.Find(id); // Load the user.
context.Entry(user)
.Collection(u => u.Groups)
.Load();
This way you don't have to use the .Include(), and you can even filter the Groups:
context.Entry(user)
.Collection(u => u.Groups)
.Query()
.Where(g => g.SomeProperty.Contains("something"))
.Load();
TheMentor,
Depending on whether you have a repository or a db context, this object should only live for the duration of the controller action (Request), so you should be able to do everything required within the confines of the action.
Maybe i've misunderstood, but based on your question, this is what your issue appears to be. If I have misunderstood, then I'd still suggest that the db repository or db context should be referenced across the controller, rather then invoking it inside the action each time.
so you should see something like this in your controller:
public class TasksController : BaseController
{
private readonly TaskService _serviceTasks;
public TasksController(IRepository repository)
{
_serviceTasks = new TaskService(repository);
}
//
// GET: /Tasks/
public ActionResult Index()
{
var viewModel = _serviceTasks.All<Task>();
return View(viewModel);
}
public ActionResult Details(int id)
{
var domainModel = _serviceTasks.GetById<Task>(id);
var viewModel = PopulateDetailsViewModel(domainModel);
return View(viewModel);
}
//.. rest of actions cut
}
Related
From what I've read, I'm supposed to be using ViewModels to populate my views in MVC, rather than the model directly. This should allow me to pass not just the contents of the model, but also other information such as login state, etc. to the view instead of using ViewBag or ViewData. I've followed the tutorials and I've had both a model and a viewmodel successfully sent to the view. The original problem I had was that I needed a paginated view, which is simple to do when passing a model alone, but becomes difficult when passing a viewmodel.
With a model of
public class Instructor {
public string forename { get; set; }
public string surname { get; set; }
}
and a viewmodel of
public class InstructorVM {
public Instructor Instructors { get; set; }
public string LoggedIn { get; set; }
}
I can create a paginated list of the instructors using the pure model Instructor but I can't pass InstructorVM to the view and paginate it as there are other properties that aren't required in the pagination LoggedIn cause issues. If I pass InstructorVM.Instructors to the view, I get the pagination, but don't get the LoggedIn and as this is just the model, I may has well have passed that through directly.
An alternative that was suggested was to convert/expand the viewmodel into a list or somesuch which would produce an object like this that gets passed to the view
instructor.forename = "dave", instructor.surname = "smith", LoggedIn="Hello brian"
instructor.forename = "alan", instructor.surname = "jones", LoggedIn="Hello brian"
instructor.forename = "paul", instructor.surname = "barns", LoggedIn="Hello brian"
where the LoggedIn value is repeated in every row and then retrieved in the row using Model[0].LoggedIn
Obviously, this problem is caused because you can only pass one object back from a method, either Instructor, InstructorVM, List<InstructorVM>, etc.
I'm trying to find out the best option to give me pagination (on part of the returned object) from a viewmodel while not replicating everything else in the viewmodel.
One suggestion was to use a JavaScript framework like React/Angular to break up the page into a more MVVM way of doing things, the problem with that being that despite looking for suggestions and reading 1001 "Best JS framework" lists via Google, they all assume I have already learned all of the frameworks and can thus pick the most suitable one from the options available.
When all I want to do is show a string and a paginated list from a viewmodel on a view. At this point I don't care how, I don't care if I have to learn a JS framework or if I can do it just using MVC core, but can someone tell me how to do this thing I could do quite simply in ASP.NET? If it's "use a JS framework" which one?
Thanks
I'm not exactly sure what the difficulty is here, as pagination and using a view model aren't factors that play on one another. Pagination is all about selecting a subset of items from a data store, which happens entirely in your initial query. For example, whereas you might originally have done something like:
var widgets = db.Widgets.ToList();
Instead you would do something like:
var widgets = db.Widgets.Skip((pageNumber - 1) * itemsPerPage).Take(itemsPerPage).ToList();
Using a view model is just a layer on top of this, where you then just map the queried data, no matter what it is onto instances of your view model:
var widgetViewModels = widgets.Select(w => new WidgetViewModel
{
...
});
If you're using a library like PagedList or similar, this behavior may not be immediately obvious, since the default implementation depends on having access to the queryset (in order to do the skip/take logic for you). However, PagedList, for example has StaticPagedList which allows you to create an IPagedList instance with an existing dataset:
var pagedWidgets = new StaticPagedList<WidgetViewModel>(widgetViewModels, pageNumber, itemsPerPage, totalItems);
There, the only part you'd be missing is totalItems, which is going to require an additional count query on the unfiltered queryset.
If you're using a different library, there should be some sort of similar functionality available. You'll just need to confer with the documentation.
I'm using MVC3 with EF (version 4, but not sure if 4.0,4.1,etc). I've been fighting with this since yesterday and I don't find the answer anywhere. I'm using the book "Pro Entity Framework 4.0".
I did Model First approach and because I want to use inheritance I created a basic model to do the first testings (sorry, click the link, I don't have enough rep to put a picture):
EF Model
Then with this model I created the database. I'm not very happy with the naming convention, because in spite of pluralizing the entity names, for the derived class table it created a prefixed-single table name. I'm sorry I don't have SSMS installed but have a look through the Server Explorer, see the picture:
DB created from EF Model
Then I created controllers for BaseClass with the template "Controller with read/write actions and views, using Entity Framework". It works great! It created all the views, CRUD.
For instance in the Details view I have this code:
//
// GET: /BaseClass/Details/5
public ViewResult Details(int id)
{
BaseClass baseclass = db.BaseClasses.Single(b => b.Id == id);
return View(baseclass);
}
It works fine.
Then I did the same for the DerivedClass and I got the controller with all the CRUD actions and the views. And now the problem. For instance the Details controller of the DerivedClass is like this:
//
// GET: /DerivedClass/Details/5
public ViewResult Details(int id)
{
DerivedClass derivedclass = db.BaseClasses.Single(d => d.Id == id);
return View(derivedclass);
}
As you can see it tries to get db.BaseClasses instead of db.DerivedClasses, with gives a compilation error, but db does not provide any access to the DerivedClass entity, there is nothing in db at all related with DerivedClass.But if I create manually an instance of DerivedClass in the code it is possible:
MyNamespace.Blablabla.Site.Models.DerivedClass dev = new Models.DerivedClass();
Am I missing anything? Thanks in advance.
Inheritance hierarchies are mapped to one DbSet. If you want to filter on inherited entities you can use:
DerivedClass derivedclass = db.BaseClasses.OfType<DerivedClass>().Single(d => d.Id == id);
The OfType<>() filters the object set for instances of the type you specify.
For adding and updating a derived entity you can also the parent DbSet and EF will map it to the correct tables.
First of all, I am new to ASP.Net MVC 3, and I am also using EF 4.1.
I have a complex object, something similar to let's say a Product object containing a Category object. So we have Product.CategoryId, Product.Category and some extra properties. I also have a form to create products with a dropdown list to select the category.
In my controller, after the product has been created, I need to have access to some property of the Category to perform some extra stuff. However, although Product.CategoryId is set, I cannot access Product.Category.SomeProperty because Product.Category is null. I expected Product.Category would be loaded automatically using some lazy loading, but it does not seem to be.
The code in my Controller looks like this:
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
string someString = product.Category.SomeProperty;
...
Now, this does not work because product.Category is null. What do I need to add so that I can access SomeProperty?
Lazy loading will not work in this scenario because you are adding a new object. Lazy loading will work on "Proxy" entities created by EF context.
So what you can do here is explicitly load the navigational property.
db.Products.Add(product);
db.SaveChanges();
db.Entry(product).Reference(p => p.Category).Load();
string someString = product.Category.SomeProperty;
Lazy loading doesn't work in your case because the product which is passed in into the controller action is not a proxy object but created as an ordinary Product instance by the model binder.
What you expect would work if product is created as a proxy:
var product = db.Products.Create();
product.CategoryId = 1;
db.Products.Add(product);
db.SaveChanges();
string someString = product.Category.SomeProperty;
// Category gets lazily loaded now
The Category property on the Product class must be virtual of course to have lazy loading working at all.
It doesn't help you in your situation because the model binder doesn't create a proxy.
Solutions: Either explicite loading (see #Eranga's answer) or in case you really only need to inspect the SomeProperty of the category fetch the value in a projection:
string someString = db.Entry(product).Reference(p => p.Category).Query()
.Select(c => c.SomeProperty).SingleOrDefault();
...or (because you have the key of the category)...
string someString = db.Categories.Where(c => c.Id == product.CategoryId)
.Select(c => c.SomeProperty).SingleOrDefault();
You may need to explicitly enable lazy loading in your entity framework object context, as described in the MSDN article How to: Use Lazy Loading to Load Related Objects
In the Entity Framework runtime, the default value of the
LazyLoadingEnabled property in an instance of ObjectContext is false.
db.ContextOptions.LazyLoadingEnabled = true;
More detail is provided in the Loading Related Objects article, just look in the section labeled "Lazily Loading Entity Objects".
I'd like to create an "API-like" layer in my code that effectively cordons-off database access to higher level code. For example, I might have the following function:
class MyApi {
private $my_user_id;
function getContacts() {
$contacts = $em->getRepository('Contacts')->findByOwner($this->my_user_id);
$em->clear();
return $contacts;
}
function getGroups() {
$groups = $em->getRepository('Groups')->findByOwner($this->my_user_id);
//hydrate each group's contacts list
foreach ($groups as $group) {
$group->getContacts();
}
$em->clear();
return $groups;
}
}
I'm using $em->clear() to detach the Entities from the EntityManger before returning them, so my Views can't accidentally modify managed entities. However, I run into problems when I want to compare entities returned by two sequential API functions. Ideally, I'd like a view/controller to contain:
$my_contacts = $myapi->getContacts();
$my_groups = $myapi->getGroups();
foreach($my_groups as $group) {
foreach ($my_contacts as $contact) {
if ($group->getContacts()->contains($contact)) {
echo "{$group->getName()} contains {$contact->getName()}!<br/>";
} else {
echo "{$group->getName()} does not contain {$contact->getName()}!<br/>";
}
}
}
However, since I detached all of the Contacts from the EntityManager at the end of the getContacts API call, the objects returned by $group->getContacts() are different PHP objects than those returned by $api->getContacts(), so the contains() function doesn't work properly.
Do I have any options for "defanging" my entities, making them effectively read-only, without giving up the benefits that the EntityManager provides? (Such as all managed entities representing the same database entry being the same object, being able to further hydrate associated objects after they've been passed back from the API, etc.)
Why would you worry that your views are going to make changes that will be committed back to the database? If your views don't know about the EM (and they shouldn't), any changes they make to the entities will disappear at the end of the request.
The only other option I can think of is to hydrate your results as arrays when they're destined to be fed to the view script. But that gives up a lot of handy functionality.
Maybe this is a little late, but it can be useful for anyone who still needs answer on this issue...
I think there is missing a Domain Driven Design principle here: Command Query Separation.
On every object you can only have two kind of methods:
doSomething() {
//this kind of method can change the internal state
}
getSomething() {
//this kind of method NEVER changes internal state
}
Keeping proper MVC in mind, views should only need get-methods and they can never change a thing.
Detaching you entities is not necessary and no worries are needed, if keeping just CQS and MVC in mind.
I'm using Subsonic 3 and Automapper on an asp.net MVC3 project.
In my HttpPost ActionResult, I'm taking my model and mapping it to my Subsonic generated entity.
The mapping works no probs, but I can't update the entity.
Upon further inspection, it is because I have no dirty columns, therefore my call to Update() fails as Subsonic doesn't think it needs to update anything.
I've re-jigged the code loads - even forcing the method to load the entity from the db again before mapping against the model. It just seems that the mapping destroys the dirtyColumns tracking. E.g. if I map after loading from the DB, and then change a random property, it doesn't get marked as a dirty column.
I've also tried using the SetIsLoaded(true) method call. No joy after mapping.
Here's my method:
[HttpPost]
public virtual ActionResult Edit(SinglePersonModel model)
{
if (ModelState.IsValid)
{
Data.Person person;
//Now Map my model to my entity - this works
Mapper.CreateMap<SinglePersonModel, Data.Person>();
person = Mapper.Map<SinglePersonModel, Data.Person>(model);
//THIS DOESN'T SET MY COLUMN TO DIRTY
person.Link = "asdjsadij";
//THIS DOESN'T SET MY COLUMN TO DIRTY EITHER
person.SetIsLoaded(true);
person.Link = "asdjsadij";
if (person.PersonId > 0)
PersonRepository.UpdatePerson(person);
else
PersonRepository.CreatePerson(person);
return RedirectToAction(MVC.SecureAdministration.Person.Index());
}
else return View(model);
}
The Static methods on my PersonRepository just call subsonic's Update() and Save() respectively.
Any ideas would be much appreciated. I'm now thinking that I may need to put some additional properties into my model to make sure that they get carried over into the entity by the automapper.
In the worst case I'll have to just not use the Automapper when mapping back to entities from the model, which would suck.
AutoMapper.Mapper.Map<SinglePersonModel, Data.Person>(model, person); - Have you tried it like this? This doesn't assign a new instance of the object but assigns it to the existing object. Just a thought. I understand the want of not loading it from the db. But figured this might help a bit :)
Thanks for that - glad to help out :)