.net MVC 3 displaying values in a many-to-one virtual object - asp.net-mvc

I'm creating an expense report project - my first using MVC.
This is a Database First project and I'm using Oracle ODP.
I have an entity model with the following classes:
ExpenseReport
ExpenseItem
ExpenseType
The expense report will have many expense items.
Each expense item will be of a specific expense type from the list of types in that ExpenseType class - thus a many-to-one relationship.
A single expense type record contains for each type a category, and headings for description/comment field to go with that type.
In my view, I am able to display the report with a list of all the expense items for that report. I am doing this through my Edit or Details controllers with the following code:
public ActionResult Details(long id)
{
using (var db = new Entities())
{
var thisReport = db.ExpenseReport.Find(id);
thisReport.expItems = db.ExpenseItem.Where(e => e.BB_EXPREPORT_ID == id).ToList();
return View(thisReport);
}
}
I tried adding this to the code (just above the return View line) to also include the expense type values (category, headings) for each type but it is failing due to a casting issue - cannot implicitly convert (are you missing a cast?)
foreach (ExpenseItem item in thisReport.expItems)
{
item.expType = db.ExpenseType.Where(e => e.BB_EXP_TYPE == item.BB_EXP_TYPE);
}
My questions:
Isn't there a way I can set up my model classes so that I don't need
to add these statements? I.E. Can't I modify the virtual object Get
statement to pull them there? Or can I modify the entitymodel file
to get these values? Is it a loading issue? I turned off lazy
loading.
If there is not a way to do this at the model level so that virtual
objects are included in the get, then how can I set the cast in my
code above to pull the values from the ExpenseType table for that
given expense type?
Thanks.

Admittedly, I'm not sure of what you're trying to do, but it looks like you just want to load up your object all at once. You can do this with Entity Framework using the Include method.
I'd assume you're using the "pluralization" feature for naming your db sets, but then again, your call to "db.ExpenseReport" is missing an (s) at the end.
public ActionResult Details(long id) {
using (var db = new Entities()) {
ExpenseReport thisReport = db.ExpenseReport.Include("ExpenseItems.ExpenseType").Single(id);
return View(thisReport);
}
}
Then you can use it in your Razor view like so.
<ul>
foreach (var item in Model.ExpenseItems) {
<li>Name: #item.YourExpenseItemName - Type: #item.ExpenseType.YourTypeName</li>
}
</ul>

Related

MVC accessing linked table value using entity framework

I am new to entity framework and I am trying to get my head around it. I am used to writing stored procedures which have all the data I need on a example by example basis.
I am under the impression that I can get all values from a particular table including the foreign key values direct using entity framework without having to write a select query which joins the data.
I have the following in my controller
public ActionResult Patient()
{
using (var context = new WaysToWellnessDB())
{
var patients = context.Patients.ToList();
return View(patients);
}
}
In my view I have the following
#foreach (var item in Model)
{
<p>
#item.FirstName #item.Surname #item.Gender.GenderDesc
</p>
}
I have two tables, Patient and Gender, GenderId is a foreign key which I am trying to get the GenderDesc from that table.
I am getting the following message
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Can someone explain why I cannot access GenderDesc. It does work if I remove the using() around my context, but I don't really want to leave that open, is there a way to get this to work still having the using around?
Thanks in advance.
Correct, you have disposed of the context as it is within a using statement, so anything you try to access from then on will not be able to be lazy loaded. The disadvantage with lazy loading is that it will perform a query for the gender for every patient you are iterating over, which is handy, but bad! I would load the related table at query time using Include.
You'll need a new import:
using System.Data.Entity;
And then include the related table:
var patients = context.Patients.Include(p => p.Gender).ToList();
That will result in a query which will join to your "Gender" table and you should be able to output item.Gender.GenderDesc in your view.

Can I delete a single child entity without loading the entire collection?

I have 2 classes, like the below.
They can have very large collections - a Website may have 2,000+ WebsitePages and vice-versa.
class WebsitePage
{
public int ID {get;set;}
public string Title {get;set;}
public List<Website> Websites {get;set;}
}
class Website
{
public int ID {get;set;}
public string Title {get;set;}
public List<WebsitePage> WebsitePages {get;set;}
}
I am having trouble removing a WebsitePage from a Website. Particularly when removing a WebsitePage from mutliple Websites.
For example, I might have code like this:
var pageToRemove = db.WebsitePages.FirstOrDefault();
var websites = db.Websites.Include(i => i.WebsitePages).ToList();
foreach(var website in websites)
{
website.WebsitePages.Remove(pageToRemove)
}
If each website Include() 2k pages, you can imagine it takes ages to load that second line.
But if I don't Include() the WebsitePages when fetching the Websites, there is no child collection loaded for me to delete from.
I have tried to just Include() the pages that I need to delete, but of course when saving that gives me an empty collection.
Is there a recommended or better way to approach this?
I am working with an existing MVC site and I would rather not have to create an entity class for the join table unless absolutely necessary.
No, you can't... normally.
A many-to-many relationship (with a hidden junction table) can only be affected by adding/removing items in the nested collections. And for this the collections must be loaded.
But there are some options.
Option 1.
Delete data from the junction table by raw SQL. Basically this looks like
context.Database.ExecuteSqlCommand(
"DELETE FROM WebsiteWebsitePage WHERE WebsiteID = x AND WebsitePageID = y"));
(not using parameters).
Option 2.
Include the junction into the class model, i.e. map the junction table to a class WebsiteWebsitePage. Both Website and WebsitePage will now have
public ICollection<WebsiteWebsitePage> WebsiteWebsitePages { get; set; }
and WebsiteWebsitePage will have reference properties to both Website and WebsitePage. Now you can manipulate the junctions directly through the class model.
I consider this the best option, because everything happens the standard way of working with entities with validations and tracking and all. Also, chances are that sooner or later you will need an explicit junction class because you're going to want to add more data to it.
Option 3.
The box of tricks.
I tried to do this by removing a stub entity from the collection. In your case: create a WebsitePage object with a valid primary key value and remove it from Website.WebsitePages without loading the collection. But EF doesn't notice the change because it isn't tracking Website.WebsitePages, and the item is not in the collection to begin with.
But this made me realize I had to make EF track a Website.WebsitePages collection with 1 item in it and then remove that item. I got this working by first building the Website item and then attaching it to a new context. I'll show the code I used (a standard Product - Category model) to prevent typos.
Product prd;
// Step 1: build an object with 1 item in its collection
Category cat = new Category { Id = 3 }; // Stub entity
using(var db = new ProdCatContext())
{
db.Configuration.LazyLoadingEnabled = false;
prd = db.Products.First();
prd.Categories.Add(cat);
}
// Step 2: attach to a new context and remove the category.
using(var db = new ProdCatContext())
{
db.Configuration.LazyLoadingEnabled = false;
db.Products.Attach(prd);
prd.Categories.Remove(cat);
db.SaveChanges(); // Deletes the junction record.
}
Lazy loading is disabled, otherwise the Categories would still be loaded when prd.Categories is addressed.
My interpretation of what happens here is: In the second step, EF not only starts tracking the product when you attach it, but also its associations, because it 'knows' you can't load these associations yourself in a many to many relationship. It doesn't do this, however, when you add the category in the first step.

Performant loading of collections of Model Data using Linq in MVC

We have controllers that read Entities with certain criteria and return a set of view models containing the data to the view. The view uses a Kendo Grid, but I don't think that makes a particular difference.
In each case, we have a Linq Query that gets the overall collection of entity rows and then a foreach loop that creates a model from each row.
Each entity has certain look ups as follows:
those with a 1:1 relationship, e.g. Assigned to (via a foreign key to a single person)
those with a 1:many relationship e.g. copy parties (to 0:many people - there are not many of these)
counts of other relationships (e.g. the number of linked orders)
any (e.g. whether any history exists)
If we do these in the model creation, the performance is not good as the queries must be run separately for each and every row.
We have also tried using includes to eager load the related entities but once you get more than two, this starts to deteriorate too.
I have seen that compiled queries and LoadProperty may be an option and I am particularly interested in the latter.
It would be great to understand best practice in these situations, so I can direct my investigations.
Thanks,
Chris.
Edit - sample code added. However, I'm looking for best practice.
public JsonResult ReadUsersEvents([DataSourceRequest]DataSourceRequest request, Guid userID)
{
var diaryEventModels = new List<DiaryEventModel>();
var events = UnitOfWork.EventRepository.All().Where(e => e.UserID == userID);
foreach (var eventItem in events)
{
var diaryModel = new DiaryEventModel(eventItem);
diaryEventModels.Add(diaryModel);
}
var result = diaryEventModels.ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
public DiaryEventModel(Event eventItem) {
// Regular property from Entity examples - no issue here as data retrived simply in original query
ID = eventItem.ID;
Start = eventItem.StartDateTime;
End = eventItem.EndDateTime;
EventDescription = eventItem.Description;
// One to one looked up Property example
eventModel.Creator = eventItem.Location.FullName;
// Calculation example based on 0 to many looked up properties, also use .Any in some cases
// This is a simplified example
eventModel.AttendeeCount = eventItem.EventAttendees.Count();
// 0 to Many looked up properties
EventAttendees = eventItem.EventAttendees.Select(e => new SelectListItem
{
Text = e.Person.FullName,
Value = e.Person.ID.ToString()
}).ToList();
}

mvc in asp.net doesnt satisfy programmers

I am learning MVC4 in Visual Studio and I have many questions about it. My first statement about MVC is that MVC's Model doesnt do what I expected. I expect Model to select and return the data rows according to the needs.
But I read many tutorial and they suggest me to let Model return ALL the data from the table and then eliminate the ones I dont need in the controller, then send it to the View.
here is the code from tutorials
MODEL
public class ApartmentContext : DbContext
{
public ApartmentContext() : base("name=ApartmentContext") { }
public DbSet<Apartment> Apartments { get; set; }
}
CONTROLLER
public ActionResult Index()
{
ApartmentContext db = new ApartmentContext();
var apartments = db.Apartments.Where(a => a.no_of_rooms == 5);
return View(apartments);
}
Is this the correct way to apply "where clause" to a select statement? I dont want to select all the data and then eliminate the unwanted rows. This seems weird to me but everybody suggest this, at least the tutorials I read suggest this.
Well which ever tutorial you read that from is wrong (in my opinion). You shouldn't be returning actual entities to your view, you should be returning view models. Here's how I would re-write your example:
public class ApartmentViewModel
{
public int RoomCount { get; set; }
...
}
public ActionResult Index()
{
using (var db = new ApartmentContext())
{
var apartments = from a in db.Apartments
where a.no_of_rooms == 5
select new ApartmentViewModel()
{
RoomCount = a.no_of_rooms
...
};
return View(apartments.ToList());
}
}
Is this the correct way to apply "where clause" to a select statement?
Yes, this way is fine. However, you need to understand what's actually happening when you call Where (and various other LINQ commands) on IQueryable<T>. I assume you are using EF and as such the Where query would not execute immediately (as EF uses delayed execution). So basically you are passing your view a query which has yet to be run and only at the point of where the view attempts to render the data is when the query will run - by which time your ApartmentContext will have been disposed and as a result throw an exception.
db.Apartments.Where(...).ToList();
This causes the query to execute immediately and means your query no longer relys on the context. However, it's still not the correct thing to do in MVC, the example I have provided is considered the recommended approach.
In our project, we will add a Data Access Layer instead of accessing Domain in controller. And return view model instead of Domain.
But your code, you only select the data you need not all the data.
If you open SQL Profiler you'll see that's a select statement with a where condition.
So if it's not a big project I think it's OK.
I can't see these tutorials but are you sure it's loading all the data? It looks like your using entity framework and entity framework uses Lazy laoding. And Lazy loading states:
With lazy loading enabled, related objects are loaded when they are
accessed through a navigation property.
So it might appear that your loading all the data but the data itself is only retrieved from SQL when you access the object itself.

How should I structure my ViewModel for this hierarchical data I need to display in ASP.NET MVC?

I have a view that will look like this:
I'm trying to figure out how I should represent my ViewModel for this view. Each "Agency" can have multiple "Business Units" and each "Business Unit" can have several "Clients".
In the database I easily represent this with a mapping table and foreign keys for the Agency, BusinessUnit and Client tables.
However, now I need to use LINQ to query this data out of the database and then construct a ViewModel object that represents this tree-like structure so my View can render it.
Can anyone give me tips on what data-structure I should use, or what my ViewModel might look like for this hierarchical structure in C# code? I need to create my ViewModel object to pass on to this view.
Any suggestions on how to represent the ViewModel are appreciated!
Just store a List instance in your view data?
public class Agency
{
public List<BusinessUnit> Units;
public string Name;
public int NumberOfAccounts
{
get
{
int ret = 0;
foreach(BusinessUnit unit in units)
ret += unit.NumberOfAccounts;
return ret;
}
}
// ... continue this strategy for other properties
}
public class BusinessUnit
{
public List<Client> clients;
public string Name;
public int NumberOfAccounts
{
get
{
int ret = 0;
foreach(Client client in clients)
ret += client.NumberOfAccounts;
return ret;
}
}
// ... continue this strategy for other properties
}
public class Client
{
public string Name;
public int NumberOfAccounts;
}
Assuming that your Linq2Sql implementation has the same relationships in it as the database (which if you did drag and drop to the designer, they definitely do), here's how I would approach it.
I would create a strongly typed partial view of type Agency that would represent each section (Agency, in your case), call it AgencyReportSection.ascx. This control will take an agency, iterate through its business units, which in turn iterate through its clients.
Wherever you are packaging up your data, do something like this:
DataContext context = new DataContext();
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Agency>(a => a.BusinessUnit);
options.LoadWith<BusinessUnit>(b => b.Client);
context.LoadOptions = options;
What this will give you is that when the context gets an agency, it will follow the defined relationships and give you those objects as well. So you get:
Agency a = context.Agency.FirstOrDefault();
IEnumerable<BusinessUnit> units = a.BusinessUnits;
IEnumerable<Client> clients = units.Clients;
Your view could do something like:
<% foreach(var agency in agencies)%{>
<% Html.RenderPartial("AgencyReportSection"); %>
<%}%>
The reason you are doing the data load option is to avoid lazy loading in the view, let the model pack up all the necessary data.
I hope I've understood your question correctly...
I have recently been using the "ADO .Net Entity Data Model" template to control connectivity to a MSSQL database that supports hierarchy data and it has been working well.
You can just tie your presentation layer directly to the data models.
If your keys are set correctly in the database its a snap to get up and running..I think it also requires ADO.Net 3.5
Creating ADO.NET Entity Framework Entity
MS information on entity

Resources