Create custom report with Entity Framework OR Enterprise Library? - asp.net-mvc-3

I have developed a system for a client using ASP.Net MVC 3 and Entity Framework 4. The system is a small application which lets the client record and monitor his equipment, ie, he can add a piece of equipment such as a PC (record the Asset Number, Price, Warrenty Expires etc), assign it to a Category (ie PC System, Printer, Speaker etc) and also record the location of the equipment (ie Main Office, Store Room, 2nd Building). This all works fine, however, recently the client has asked for some reports to be built into the system.
Some reports are simple to do, ie, search by equipment asset number and then return all the additional info related to that. However, there is one report which he needs, this is to list all the Locations at the top of the report and then all the Categories at the left of the report just like a grid/ lookup table. Then this will show the total number of equipment categories in each location, for example, the total number of PC Systems at 2nd Building, total number of Speakers in the Store Room etc.
I was thinking, although I could be wrong, that this is not what Entity Framework was designed for, ie, returning objects, not datasets displaying calculations. Therefore, I was wondering, what would be the best way to do this?
I was thinking of possibly using Microsoft Enterprise Library and Stored Procedures to just return a dataset of results, however, maybe this is the wrong approach?
Again, any feedback would be much appreciated and I apologise if my request is a bit confusing.
Thanks.

What your customer wants you to show is called a pivot table. Take a look at the answer here: Pivot table in c# entity framework.
But if you have the following entities
public class Location {
public int LocationId { get; set; } // PK
}
public class Equipment {
public int EquipmentId { get; set; } // PK
}
public class EquipmentAtLocation {
public int LocationId { get; set; } // PK/FK
public int EquipmentId { get; set; } // PK/FK
public int Quantity { get; set; }
}
then building this table is nothing else than iterating over your m:n table EquipmentAtLocation and writing Quantity into the cell that is given by LocationId and EquipmentId. If you don't have that PK on that table, then you need to do a grouping by LocationId and EquipmentId on that table first.
var locations = ctx.Locations.Select((val, idx) => new { val.LocationId, idx }).ToDictionary(x => x.LocationId, y => y.idx); // get a map of location to index
var equipments = ctx.Equipments.Select((val, idx) => new { val.EquipmentId, idx }).ToDictionary(x => x.EquipmentId, y => y.idx); // get a map of equipment to index
int[,] pivot = new int[locations.Count, equipments.Count];
foreach (var entry in ctx.EquipmentAtLocations) {
pivot[locations[entry.LocationId], equipments[entry.EquipmentId]] += entry.Quantity;
}
I don't know if the data contract serializer is able to serialize multidimensional arrays, otherwise you have to use a jagged array. You should of course pack that into an object and also include the header information (the keys in the dictionary when ordered by value) so that you know which index means what.

Since you tagged this as MVC 3 - are you looking for a web interface for them to select a category, and the results show up? IF so, surely you can use Entity Framework. Simply use an Ajax request to a controller's method to then return your view with that data in it.
No procedure required - you can do this all in entity framework. Your left hand side is Entity A. When you click on one, it does an ajax request to /Category/Index/5 for example which them simply renders those results.

Related

Entity Splitting For One-To-Many table relationships

Following this article (What are best practices for multi-language database design?), I have all my database tables splitted in two: the first table contains only language-neutral data (primary key, etc.) and the second table contains one record per language, containing the localized data plus the ISO code of the language. The relationship between the two tables is one to many.
Here a screenshot of the datamodel: https://dl.dropboxusercontent.com/u/17099565/datamodel.jpg
Because the website has 8 languages, for each record in table "CourseCategory" I have 8 record in table "CourseCategoryContents". The same happens with "Course" and "CourseContent"
Then I use Entity Splitting in order to have only one entity for the Course Category and one entity for the Course:
public class CourseCategoryConfiguration : EntityTypeConfiguration<WebCourseCategory>
{
public CourseCategoryConfiguration()
{
Map(m =>
{
m.Properties(i => new { i.Id, i.Order, i.Online });
m.ToTable("CourseCategories");
});
Map(m =>
{
m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords });
m.ToTable("CourseCategoryContents");
});
}
}
public class CourseConfiguration : EntityTypeConfiguration<WebCourse>
{
public CourseConfiguration()
{
Map(m =>
{
m.Properties(i => new { i.Id, i.CategoryId, i.Order, i.Label, i.ThumbnailUrl, i.HeaderImageUrl });
m.ToTable("Courses");
});
Map(m =>
{
m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords, i.Online });
m.ToTable("CourseContents");
});
}
}
Then to retrive the courses in a desired language including their category I do this:
using (WebContext dbContext = new WebContext())
{
// all courses of all categories in the desired language
return dbContext.Courses
.Include(course => course.Category)
.Where(course => course.LanguageCode == lan
&& course.Category.LanguageCode == lan)
.ToList();
}
}
Entity splitting works fine with one-to-one relationships, but here I have one-to-many relationships.
The website has contents (CourseCategories and Courses) in 3 languages ("en", "de", "fr").
EF correctly returns all the Courses with their Category in the right language (eg. in english), but returns each record 3 times. This is because I have the CourseCategory in 3 languages too.
The only one working solution I came up is avoiding using ".Include(Category)", getting all the courses in the desired language in first, then, in a foreach cycle, for each Course retriving its Category in language. I don't like this lazy loading approach, I would like to retrive all the desired data in one shot.
Thanks!
The best solution is to map tables to the model as it then in your model Course class will have a navigation property ICollection<CourseCategoryContent>.
In this case you just project this model to DTO or ViewModel "according to your application design"
e.g.
Your model will look like this
public class Course
{
public int Id {get; set;}
public int Order {get; set;}
public ICollection<CourseCategoryContent> CourseCategoryContents {get; set;}
}
public class CourseCategoryContent
{
public string LanguageId {get; set;}
public string Name {get; set;}
}
Then just create new DTO or ViewModel like :
public class CourseDTO
{
public int Id {get; set;}
public int Order {get; set;}
public string Name {get; set;}
}
Finally do the projection
public IQueryable<CourseDTO> GetCourseDTOQuery ()
{
return dbContext.Courses.Select(x=>new CourseDTO{
Id = x.Id,
Order = x.Order,
Name = x.CourseCategoryContents.FirstOrDefault(lang => lang.LanguageId == lang).Name,
});
}
And note that the return type is IQueryable so you could do any filter, Order or grouping operation on it before hitting the database.
hope this helped
No fix-all answer i'm afraid, every way has a compromise.
I've used both the database approach (10+ language dependent tables) and the resource file approach in fairly large projects, if the data is static and doesn't change (i.e you don't charge a different price or whatever) I would definately consider abstracting language away from your database model and using Resource keys then loading your data from files.
The reason or this is the problem you are experiencing right now where you can't filter includes (this may have changed in EF6 perhaps? I know it's on the list of things to do). You might be able to get away with reading it into memory and filtering them though like you're doing but this meant it wasn't very performant for us and I had to write Stored Procedures that I just passed the iso language and executed in EF.
From a maintenance point of view it was easier as well, for the DB project I had to write an admin console so people could log on and edit values for different languages etc. Using resource files I just copy-pasted the values into excel and emailed them to the people we use to translate.
It depends on the complexity of your project and what you prefer, i'd still consider both approaches in future.
TLDR: options that i've found are:
1) filter in memory
2) lazy load with filter
3) write stored procedure to EF and map that result
4) use resources instead
Hope this helps
EDIT: After looking at diagram it looks like you may need to search against the language dependant values? In that case resources probably won't work. If you're just letting them navigate off a menu then you're good to go.

How to use a Dictionary or Hashtable for LINQ query performance underneath an OData service

I am very new to OData (only started on it yesterday) so please excuse me if this question is too dumb :-)
I have built a test project as a Proof of Concept for migrating our current web services to OData. For this test project, I am using Reflection Providers to expose POCO classes via OData. These POCO classes come from in-memory cache. Below is the code so far:
public class DataSource
{
public IQueryable<Category> CategoryList
{
get
{
List<Category> categoryList = GetCategoryListFromCache();
return categoryList.AsQueryable();
}
}
// below method is only required to allow navigation
// from Category to Product via OData urls
// eg: OData.svc/CategoryList(1)/ProductList(2) and so on
public IQueryable<Category> ProductList
{
get
{
return null;
}
}
}
[DataServiceKeyAttribute("CategoryId")]
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public List<Product> ProductList { get; set; }
}
[DataServiceKeyAttribute("ProductId")]
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
To the best of my knowledge, OData is going to use LINQ behind the scenes to query these in-memory objects, ie: List in this case if somebody navigates to OData.svc/CategoryList(1)/ProductList(2) and so on.
Here is the problem though: In the real world scenario, I am looking at over 18 million records inside the cache representing over 24 different entities.
The current production web services make very good use of .NET Dictionary and Hashtable collections to ensure very fast look ups and to avoid a lot of looping. So to get to a Product having ProductID 2 under Category having CategoryID 1, the current web services just do 2 look ups, ie: first one to locate the Category and the second one to locate the Product inside the Category. Something like a btree.
I wanted to know how could I follow a similar architecture with OData where I could tell OData and LINQ to use Dictionary or Hashtables for locating records rather than looping over a Generic List?
Is it possible using Reflection Providers or I am left with no other choice but to write my custom provider for OData?
Thanks in advance.
You will need to process expression trees, so you will need at least partial IQueryable implementation over the underlying LINQ to Objects. For this you don't need a full blown custom provider though, just return you IQueryable from the propties on the context class.
In that IQueryable you would have to recognize filters on the "key" properties (.Where(p => p.ProductID = 2)) and translate that into a dictionary/hashtable lookup. Then you can use LINQ to objects to process the rest of the query.
But if the client issues a query with filter which doesn't touch the key property, it will end up doing a full scan. Although, your custom IQueryable could detect that and fail such query if you choose so.

How to update tables with a many-to-many join with a bridging table that has payload

I am building a personal Movie Catalogue and have the following structure:
Movie table/entity
MovieID (PK identifier) +
Other movie related properties
Person table/entity
PersonID (PK identifier) +
Other person related properties.
PersonMovie table/entity
MovieID (FK)
PersonID (FK)
Other columns containing information about what the person did on the movie (I.e. charactor name or job).
I want to have a view that allows a user to create/update a movie, or a person, and have a checkbox to then allow them to select existing or create new cast members (persons), or movies.
I am struggling on two fronts:
1) how to present this type of multi-page data collection. A movie has many cast members & a person can be involved in many movies.
2) how to update 2 or 3 of the tables above depending on what the user whats to enter. A user may want to add a movie but doesnt know the cast members yet or vice versa. A user may want to add a movie and add people who already exist as cast members of the movie.
Also I do not want cascading deletes and have struggled switching it off for the relationships between the above entities.
I can do this easily with webforms but am learning MVC 3 & Entity Framework 4 and am still getting my head around it all. I have looked around and haven't come across solutions/tutorials on what I would like to achieve.
Any help would be much appreciated.
Tony
I had a similar issue when I switched from another MVC framework (Rails as in ROR). For the starters, check out Leniency's reply on the similar question, that is; relationship-with-payload or non-PJT (pure-join-table) which unfortunately ASP.NET MVC3 doesn't support explicitly.
You can create a ModelView (a virtual entity) to wrap the collections of other entity types and pass it to the View. The aforementioned post has the detailed example with the code for Model, ViewModel, View, Partial and the Controller. (read both the answers on that post, my answer is continuation of Leniency's answer there)
Hope it helps!
Vulcan's on the right track, and my response that she linked too will help you get the model setup where the linking table contains extra data.
For building the views, you'll mostly likely find that ViewModels are the way to go for more complicated setup like you're describing, then your controller and service layer will deal with processing the view model data and translating it into EF entities. Viewmodels are built specifically to the view that you need, rather than trying to hammer a domain model into a view that may not fit it.
Here's a very rough start for one of the workflows for creating a movie, with an optional list of people.
Domain - your Movie and Person class, plus a linking table similar to what I described here.
View Models - Create a movie and attach people to it
public class MovieCreatePage
{
public MovieInput Input { get; set; } // Form field data...
public IEnumerable<People> People { get; set; } // list of people for drop downs
// ... other view data needed ...
}
public class MovieInput
{
[Required, StringLength(100)]
public string Name { get; set; }
// Easiest to just submit a list of Ids rather than domain objects.
// During the View Model -> Domain Model mapping, there you inflate them.
public int[] PeopleIds { get; set; }
// ... other input fields ...
}
Create.cshtml - just make a form for your view model.
Controller:
// NOTE! The parameter name here matches the property name from the view model!
// Post values will come across as 'Input.Name', 'Input.Year', etc...
// Naming the parameter the same as the view model property name will allow
// model binding. Otherwise, you'll need an attribute: [Bind(Prefix=".....")]
[HttpPost]
public ActionResult Create(MovieInput input)
{
if (ModelState.IsValid)
{
//
// Now do your mapping - I'd suggest Automapper to help automate it,
// but for simplicity, lets just do it manually for now.
var movie = new Movie
{
Name = input.Name,
Actors = input.PeopleIds != null
? input.PeopleIds.Select(id => new Person { Id = id })
: null
};
//
// Now save to database. Usually in a service layer, but again,
// here for simplicity
// First, attach the actors as stubbed entities
if (movie.Actors != null)
{
foreach (var actor in movie.Actors)
_db.People.Attach(actor); // Attach as unmodified entities
}
_db.Movies.Add(movie);
_db.SaveChanges();
TempData["Message"] = "Success!"; // Save a notice for a successful action
return RedirectToAction("Index");
}
// Validation failed, display form again.
return View(new MovieCreatePage
{
Input = input,
// ... etc ...
});
}
Hopefully this helps you some and points you in a good direction. It does, of course, bring up a lot of other questions that will just take time (ie, automapper, service layers, all the various EF gotcha's, etc...).

Beginner EF4 / CodeFirst / MVC3 help

Although I love what I'm learning, I'm finding it a struggle and need some help
I've been using these two tutorials which I think are awesome:
http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx
http://msdn.microsoft.com/en-us/data/gg685467
Currently my main problem/confusion is:
I have a CodeFirst table/entity I don't know how to correctly get data from other tables/entities to show in my views:
public class Car {
public int ID { get; set; }
public string Name { get; set; }
public int EngineID { get; set; }
public virtual Engine { get; set; }
}
public class Engine {
public int ID { get; set; }
public string Name { get; set; }
public string Manufacturer { get; set; }
// (plus a whole lot of other things)
}
Now when I create a View for Cars (using the List type/option) I get a nice autogenerated list
#foreach (var item in Model) {
<tr>
<td>#item.ID</td>
<td>#item.Name</td>
<td>#item.EngineID</td>
</tr>
Perfect... except EngineID is mostly worthless to the viewer, and I want to show Engine.Name instead
So I assumed I could use EF lazy loading:
<td>#item.Engine.Name</td>
Unfortunately when I tried that, it says my ObjectContext has been disposed so can't get any further data requiring a connection
Then I tried going to the controller and including the Engine.Name
var cars = (from c in db.Cars.Include("Engine.Name") select c;
Which tells me: Entities.Engine does not declare a navigation property with the name 'Name'
... ? Lies
Include("Engine") works fine, but all I want is the Name, and Include("Engine") is loading a large amount of things I don't want
Previously in a situation like this I have created a view in the DB for Car that includes EngineName as well. But with CodeFirst and my noobness I haven't found a way to do this
How should I be resolving this issue?
I thought perhaps I could create a Model pretty much identical to the Car entity, but add Engine.Name to it. This would be nice as I could then reuse it in multiple places, but I am at a loss on how to populate it etc
Wanting to learn TDD as well but the above is already frustrating me :p
Ps any other tutorial links or handy things to read will be greatly appreciated
It isn't lies as you are actually trying to include a property that's a 2nd level down withouth giving it a way to navigate. If you let EF generate your DB with this structure, it would likely have made a navigation table called something like Car_Engine and if you include the name without the object it HAS mapped, then it's not got a navigation property in your new object.
The simple way around this is to go:
(from c in db.Cars.Include("Engine") select new { c, EngineName = c.Engine.Name }
If you still get navigation property errors then you might need to make sure your are mapping to your schema correctly. This can be done with EntityTypeConfiguration classes using the fluent API - very powerful.
This of course won't help in strongly typing your car object to show in MVC.
If you'd like to get around this, your gut feeling is right. It's pretty common to use viewmodels that are read only (by design, not necessarily set to readonly) classes that provide simple views of your data.
Personally I keep my model quite clean and then have another project with viewmodels and a presentation project to populate. I'd avoid using overlapping entities in your core model as it might lead to unpredictable behaviour in the data context and at least a peristance nightmare when updating multiple entities (ie who's responsible for updating the engine name?).
Using you viewmodels, you can have a class called CarSummaryView or something with only the data you want on it. This also solves the issue of being vulnerable to overposting or underposting on your site. It can be populated by the above query quite easily.
PS There's a bunch of advantages to using viewmodels beyond just not loading full heirarchies. One of the biggest is the intrinsic benefit it gives you with avoiding over and underposting scenarios.
There's loads of ways to implement viewmodels, but as a simple CarView example:
public class CarView
{
public int ID { get; set; }
public string Name { get; set; }
public string EngineName { get; set; }
}
This should be clearly seperated from your entity model. In a big project, you'd have a single viewmodels project that the presenter can return, but in a smaller one you just need them in the same layer as the service code.
To populate it directly from the query, you can do the following
List<CarView> cars = (from c in db.Cars.Include("Engine.Name") select new CarView() { ID = c.ID, Name = c.Name, EngineName = c.Engine.Name }).ToList();

ASP.Net MVC View returned, but how can I show additional information?

I have a SQL database with has the following: Customer, Item, Clothing and Food.
Item holds a key to Clothing or Food.
Item also holds a key to Customer. Therefore a customer can have an item, which may be of food or clothing.
I am using ADO.Net Entity Framework and have this generated automatically.
I currently have the following set-up: A person may enter their ID on the webpage and this is sent via a form post where the controller picks it up and queries the database using LINQ to get the customer. The customer view (details) is then returned. I can now see all the customer details etc.
However, what I want is to be able to see the items the customer has, the different food items and clothing items, but I am unsure how to do this. I also want to be able to allow the user to edit one field of the clothes and food items tables. Any idea how I would implement this?
Here is an ActionResult in my CustomerController:
public ActionResult Details(int id)
{
var cust = (from c in dataModel.Customers
where (c.MembershipID == id)
select c).First();
return View(cust);
}
I can also write cust.Items which is the entity which I want to display in the view with the customer (their items). How would I display this in the view also?
Hopefully this makes it a little more clear on what I am trying to achieve and how.
Thanks.
Using Entity Framework, if you're tables are linked properly with the right foreign keys and all that then your Customer entity should have a property that is a collection of Items.
You could also create your own strongly typed ViewModel that has a field for Customer and implement your own properties for Clothing and Food and populate those with another query.
This question was asked last night but its similar. The guy in the question wanted information to populate a dropdown passed in. You want something similar, not for a dropdown, but to fill in textboxes to edit. How to properly populate drop downs from ViewData in controller on multiple views in ASP.NET MVC
To create a ViewModel start by creating a new class and name it CustomerAndItemsViewModel, for example.
public class CustomerAndItemsViewModel
{
public Customer Customer { get; set; }
public IQueryable<Items> Items { get; set; }
}
public ActionResult Details(int id)
{
var cust = (from c in dataModel.Customers
where (c.MembershipID == id)
select c).First();
var items = (from i in dataModel.Items
where (i.MembershipID == cust.MembershipID)
select i;
return View(new CustomerAndItemsViewModel { Customer = cust, Items = items });
}
And don't forget that you will no longer be passing a Customer to your view. So you need to change the line at the top to something like:
#model Your.Path.To.CustomerAndItemsViewModel
Typically, if you want to pass back information that is not contained in just one of your entities, you have to create a class that encompasses more than one object. So, if you want a page that displays your customer information, and all their items (which they can then edit), you would need to have a the controller action pass back a "CustomerAndItems" object (or something similarly named). This object would hold a reference to the Customer as well as a collection of their Items. (You build the CustomerAndItems object within your Action.)
Then, your view would be strongly typed to CustomerAndItems, and you can then display each piece of information as you normally would.

Resources