Entity Fw 4, MVC3, Derived class does not appear in the ObjectContext - asp.net-mvc-3

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.

Related

MVC 3 and Entity Framework 4.1 data loading issue

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
}

how to fill a viewmodel EF 4.1

I am working on asp.net MVC 3 project. I am using EF 4.1 code first approach. I have entity class called disputes. It maps to a table in database name tblDisptes. It has three properties names Lastviewedby, Lastupdatedby, LastRespondedBy ... all three integers. I have created a viewmodel 'disputeviewmodel' with three more properties Lastviewedbyname, Lastupdatedbyname, LastRespondedByname and a property named dispute. Now my repository function returns list of disputes. how to convert this list to List of disputeviewmodel so that these three properties are filled with the names ?
Please suggest.
Your view model doesn't really need a property named dispute. A view model should not reference your domain models.
As far as the mapping is concerned one possibility is to manually do it but that could quickly become cumbersome with more complex models:
public ActionResult Foo()
{
IEnumerable<disputes> disputes = ... fetch from repo
IEnumerable<disputeviewmodel> disputeViewModels = disputes.Select(x => new disputeviewmodel
{
Lastviewedbyname = x.Lastviewedby,
Lastupdatedbyname = x.Lastupdatedby,
LastRespondedByname = x.LastRespondedBy
});
return View(disputeViewModels);
}
So a better approach would be to use AutoMapper:
public ActionResult Foo()
{
IEnumerable<disputes> disputes = ... fetch from repo
IEnumerable<disputeviewmodel> disputeViewModels = Mapper.Map<IEnumerable<disputes>, IEnumerable<disputeviewmodel>>(disputes);
return View(disputeViewModels);
}
Why your Dispute doesn't look like that?
class Dispute
{
public User LastViewedBy;
public User Lastupdatedbyname
public User LastRespondedByname
}
That's how it should look. Then problem solved. When you query for Disputes you already have Users (with there usernames) ready.

MVC 3 / EF 4.2 - Editing against ViewModel, do I save against Model or ViewModel?

My first MVC3 EF 4.2 site and I'm confused on some things, currently on ViewModels when querying and saving. Please correct me if I explain this poorly, i'm not sure how to term this. The .edmx automatically created the table classes but I read it was better to create a ViewModel, considering I need to join tables to display/edit my Product completely. The controller code below is where I join tables to output a Product to edit, and then save. My question - what is the right way to save the Product, to the Product.cs model generated by DbContext or my own ProductViewModel.cs?
Is there an easier method to query a product and join the tables and then map to the viewmodels parameters, or do I keep doing all this in the controller like below?
I also want to save/update the product each time someone views/clicks on the product, so I wasn't sure if I create a separate ViewModel for updating just that parameter or again, use the Product model.
Hope that makes sense! I can explain further if needed.
private SiteForgeEntities db = new SiteForgeEntities();
public ActionResult Edit(int id)
{
var viewModel = (
from a in db.Products
join b in db.Sites
on a.SiteId equals b.SiteId
join c in db.Sections
on a.SectionId equals c.SectionId
join d in db.Affiliates
on a.AffiliateId equals d.AffiliateId
select new ProductViewModel()
{
ProductId = a.ProductId,
Product = a.Product,
Description = a.Description,
Image = a.Image,
Price = a.Price,
Clicks = a.Clicks,
Link = a.Link,
Site = b.Site,
Section = c.Section,
Affiliate = d.Affiliate
}).Single(x => x.ProductId == id);
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(Product product)
{
...update database...do I pass in and save back to Product or my ProductViewModel
}
You use ViewModel to pass multiple models to the view, but when you save data, you need to save it to the appropriate model. If you are adding or modifying products, you will add items to products (using your DbContext). If you have one-to-many relationship defined between two models (in your Product.cs model you might have a property declared as:
public virtual ICollection<SomeOtherModel> SomeOtherData { get; set; }
you can use this to build a table instead of passing everything in a ViewModel. There is a nice tutorial here regarding the CRUD operations using EF4. Have a look at these short tutorials that can give you an idea about your strategy http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc.

ASP.Net MVC 3, Complex Objects and Lazy Loading

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".

Subsonic and Automapper - dirtyColumns collection is empty therefore can't update

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 :)

Resources