I've got a page which will have about ten drop down lists which are generated from my SQL database. What's the best way to populate them? I was going to use a stored procedure with LINQ to return multiple result sets, but it seems a bit overkill. Is there another way of doing it? Using HtmlHelpers or something like that?
Seeing as though everyone seems to be confused by this, I will elaborate.
This isn't to do with caching, that's not what the problem states
This isn't to do with ASP.NET controls such as DropDownList, I tagged it asp.net-mvc
This isn't to do with code-behind models, I thought that was implicitly obvious by how I tagged this question originally as ASP.NET MVC
The problem is that multiple results sets are required on the page for drop down lists (think HTML!). So I have a drop down list for your favourite breed of badger, a drop down list for how many birthdays you've had, a drop down list for how many clouds are in the sky today. All of these are dynamically populated (please note, I am joking, this is a finance system I work on). I need all of them to be on my view page, but I'd rather NOT use the IMultipleResult return type in a LINQ stored procedure to bring back multiple result sets. It just gets messy.
So in basic, I want about 10 drop down lists on my view page, all of which are populated with data from a database (which constantly change). What is the best way to get them on the view?
I would just pass the required data to the view, either in multiple ViewData dictionaries or as a special view model if you want strongly typed access. Then use HtmlHelper.DropDownList() to display the actual drop downs.
Weakly typed solution
Controller
ViewData["Data1"] = SomeRepository.GetList();
ViewData["Data2"] = SomeRepositoty.GetList();
return View();
View
<%= Html.DropDownList("Data1") %>
<%= Html.DropDownList("Data2") %>
Strongly typed solution
View model
public class DataViewModel
{
public IEnumerable<string> Data1 { get; set; }
public IEnumerable<string> Data2 { get; set;}
}
Controller
var model = SomeRepository.GetModel(); // returns an instance of DataViewModel
return View(model);
View
<%= Html.DropDownList("Data1", new SelectList(Model.Data1)) %>
<%= Html.DropDownList("Data2", new SelectList(Model.Data2)) %>
It depends on the data, if the data in the database is not updated that often, then you could have a process that creates XML files once a day. Then use the XML files as the source to the dropdowns, this would speed up the application and limit the calls to the database server.
This is really a question of data management, not anything to do with HtmlHelpers until you get to the very pointy end of things. Anyhow, the first thing I would ask myself is "how is this data updated and what kind of filtering do I need?"
If this list is pretty much constant, then you could go for a bit of caching.
If this list is pretty fluid, then just pull it out of the database as needed and be done with it. Worry about caching if your DB box starts melting.
Either way you probably want to wrap things up in some sort of LookupService class, but there isn't enough to go on to make any more specific recommendations.
I second Wyatt's recommendation about grabbing from the DB each time if the list is fluid.
However, I'm doing a similar thing (with 10+ dropdowns on each page, all very static... the values will rarely change).
The first version of the application had a helper method for each dropdown list (about 20 total by the end of the project) that grabbed from the respective tables and cached via another helper. Before I started caching, there was no db context available for the view (i created it in the controller and didn't pass it), each dropdown had to create a new connection. This got noticeably slow. Plus, I had some problems with my caching routines, and saving them with 20 magic strings in the cache, etc. Also, I had a separate querying object where I had to manually build the relationships, and having to create the 20 relationships for inner joins was a pain.
So... my new version:
I'm using a single "selectables" table. There's a PK, and a "selectable type" (which I have to admit is a string). There's a selectables enumerable, which makes things a bit cleaner. There's a main getAllSelectables() method that looks for the entire result set in the cach (and gets all rows from the db if its not in the cache) and returns it. Then there's a getSelectables(enum) that grabs out only the relevant values, and a third function getSelectListItems(enum) that calls getSelectables(enum) and returns a ienumerable for the mvc helper function.
Hope that helps,
James
Related
I'm new to the whole ASP world and I'm getting my feet wet by building a C# MVC3/EF4 project. I'm finding it hard to keep from duplicating a bunch of code in my models and view models. Consider an object Foo. I need to do the following things with Foo:
Store records of type Foo in my database.
Allow users to lookup records of an individual Foo (pass instances of Foo to a view).
Allow users to create new instances of Foo (pass instances of Foo to a form).
Let's say I also have a type Bar. A Bar contains a list of Foos. There are two requirements here:
Users can view a list of Bars.
When the user clicks on a specific Bar, it shows all of its Foos.
So, a sketch of my basic objects look like this:
class Foo
{
string FooName;
int Id;
}
class Bar
{
List<Foo> FooList;
int Id;
string Baz;
}
But when I start thinking about the different views, it begins to get messy:
The views shouldn't have any write access to any of the data members.
There's one view that takes a list of Bars but doesn't care about Bar.FooList. Let's say I also want to be good about resource management and close the DbContext as soon as possible (i.e. after the object is in memory but before I render the view). If I just pass it a list of Bars and the designer tries to access the FooList by mistake, we'll get a runtime error. Yuck!
Ok fine, I just create a distinct ViewModel for each view that has read only datamembers, no problem.
But both the database model and the form models will need to have DataAnnotations attached which say which fields are required, max length of the strings, etc. If I create separate form models and database models then I end up having to duplicate all these annotations. Yuck!
So, that's my architectural dilemma: I want to have succinct view models which restrict the views only to reading the data they are supposed to access. I want to avoid repeating data annotations all over the place. And I want to be able to aggressively free my DB resources as soon as possible. What's the best way to achieve my goals?
My advice is do NOT use data annotations in your EF entity classes. Instead, try out the fluent API. It keeps persistence concerns out of the model classes themselves. You can even define the modelbuilder stuff in an entirely different library.
As for having duplicate properties, properties are cheap:
public string MyProp { get; set; }
Not a lot of code, and you may begin to see, the viewmodels and entities need not always be exact duplicates of each other. For example, what if you want to apply [HiddenInput] to a viewmodel, to get it to render as <input type="hidden" />? Would you apply that to the entity? Why? It belongs in the viewmodel (the namespace is even System.Web.Mvc, not System.ComponentModel.DataAnnotations).
As far as duplicating error messages during validation (if you validate the MVC and EF layers separately), you can use resx resources.
As far as freeing db resources, let EF manage that. I find it's best to keep a single DbContext instance per HttpContext. You can open it up using a factory, an OnActionExecuting action filter, Application_BeginRequest, with an IoC container, or whatever. Then dispose context during OnResultExecued, Application_EndRequest, etc. Keeps things simple.
A month or two ago when I started doing asp.net MVC work I remember reading that it is not recommended to call object methods like the following in the view:
<%: Html.ListBoxFor( m => Model.SelectedId, Model.SelectItems.ToSelectList() ) %>
That above is just an example, not a direct line of code I am using. I am using extension methods to turn my enumerables into ListBox binding-ready IEnumerable collections. I know there was a concern that someone had raised in an article about this, but I cannot remember what it was. I am not actually coding it this was at the moment, but the way I am doing it is becoming a burden. I am writing extension methods to convert all of my IEnumerable objects into both SelectListItem collections and now a MultiSelectList object. So my view models are becoming bloated with "potential" ways of displaying the data. Instead of their just being an enumerable of available options, I am tracking 4 new pieces of data: SelectListItem collection, SelectListValue object, MultiSelectList control, MultiSelectListValues collection.
I am not opposed to keep going about this, but I would like to remember why it was recommend not to call the extension method on the View if I'm using strongly-typed full/partial views. I tried to find that article and others like it but I could not. I wish my memory was better than this, but hopefully someone out there knows this offhand and would be willing to share it with me!
Thanks in advance!
The idea your talking about is based on a principal that the view in MVC should be as simple as possible. Because it is the most difficult to test unnecessary bloat should be avoided.
Your example line could be paired down a few different ways. You could try writing an extension that takes the enumeration from your model and converts it to a select list that can be fed into a listbox, or you can change the model's property to return an enumeration of list items. The later is my preferred method. The model exists to organize the data to be consumed by the view so theres no reason to have it output data that cannot be consumed by the view directly. Either one of these methods would take complexity out of the view and place it somewhere that is easier to unit test.
Hope this helps.
Assuming you wanted to develop your Controllers so that you use a ViewModel to contain data for the Views you render, should all data be contained within the ViewModel? What conditions would it be ok to bypass the ViewModel?
The reason I ask is I'm in a position where some of the code is using ViewData and some is using the ViewModel. I want to distribute a set of guidelines in the team on when it's right to use the ViewData, and when it's just taking shortcuts. I would like opinions from other developers who have dealt with this so that I know my guidelines aren't just me being biased.
Just to further Fabian's comment; you can explicitly ensure viewdata is never used by following the steps outlined in this article. There's really no excuse not to use models for everything.
If you have no choice but to use ViewData (say on an existing project); at the very least use string constants to resolve the names to avoid using 'magic strings'. Something along the lines of: ViewData[ViewDataKeys.MyKey] = myvalue; Infact, I use this for just about anything that needs to be "string-based" (Session Keys, Cache Keys, VaryByCustom output cache keys, etc).
One approach you may wish to consider as your views become more complex, is to reserve the use of Models for input fields, and use ViewData to support anything else the View needs to render.
There are at least a couple of arguments to support this:
You have a master-page that requires some data to be present (e.g. something like the StackOverflow user information in the header). Applying a site-wide ActionFilter makes it easy to populate this information in ViewData after every action. To put it in model would require that every other Model in the site then inherit from a base Model (this may not seem bad initially, but it can become complicated quickly).
When you are validating a posted form, if there are validation errors you are probably going to want to rebind the model (with the invalid fields) back to the view and display validation messages. This is fine, as data in input fields is posted back and will be bound to the model, but what about any other data your view requires to be re-populated? (e.g. drop-down list values, information messages, etc) These will not be posted back, and it can become messy re-populating these onto the model "around" the posted-back input values. It is often simpler to have a method which populates the ViewData with the..view data.
In my experience I have found this approach works well.
And, in MVC3, the dynamic ViewModels means no more string-indexing!
I personally never use ViewData, everything goes through the Model, except when im testing something and i quickly need to be able to see the value on the view. Strongtyping!
In terms of ASP.NET MVC 2, ViewModel pattern is the preferred approach. The approach takes full advantage of compile time static type checking. This in combination with compiling mvc views will make your development work-flow much faster and more productive since errors are detected during build/compile time as opposed to run time.
Let's start with this basic scenario:
I have a bunch of Tables that are essentially rarely changed Enums (e.g. GeoLocations, Category, etc.) I want to load these into my EF ObjectContext so that I can assign them to entities that reference them as FK. These objects are also used to populate all sorts of dropdown controls. Pretty standard scenarios so far.
Since a new controller is created for each page request in MVC, a new entity context is created and these "enum" objects are loaded repeatedly. I thought about using a static context object across all instances of controllers (or repository object).
But will this require too much locking and therefore actually worsen perf?
Alternatively, I'm thinking of using a static context only for read-only tables. But since entities that reference them must be in the same context anyway, this isn't any different from the above.
I also don't want to get into the business of attaching/detaching these enum objects. Since I believe once I attach a static enum object to an entity, I can't attach it again to another entity??
Please help, I'm quite new to EF + MVC, so am wondering what is the best approach.
Personally, I never have any static Context stuff, etc. For me, when i call the database (CRUD) I use that context for that single transaction/unit of work.
So in this case, what you're suggesting is that you wish to retrieve some data from the databse .. and this data is .. more or less .. read only and doesn't change / static.
Lookup data is a great example of this.
So your Categories never change. Your GeoLocations never change, also.
I would not worry about this concept on the database/persistence level, but on the application level. So, just forget that this data is static/readonly etc.. and just get it. Then, when you're in your application (ie. ASP.NET web MVC controller method or in the global.asax code) THEN you should cache this ... on the UI layer.
If you're doing a nice n-tiered MVC app, which contains
UI layer
Services / Business Logic Layer
Persistence / Database data layer
Then I would cache this in the Middle Tier .. which is called by the UI Layer (ie. the MVC Controller Action .. eg. public void Index())
I think it's important to know how to seperate your concerns .. and the database stuff is should just be that -> CRUD'ish stuff and some unique stored procs when required. Don't worry about caching data, etc. Keep this layer as light as possible and as simple as possible.
Then, your middle Tier (if it exists) or your top tier should worry about what to do with this data -> in this case, cache it because it's very static.
I've implemented something similar using Linq2SQL by retrieving these 'lookup tables' as lists on app startup and storing them in ASP's caching mechanism. By using the ASP cache, I don't have to worry about threading/locking etc. Not sure why you'd need to attach them to a context, something like that could easily be retrieved if necessary via the table PK id.
I believe this is as much a question of what to cache as how. When your are dealing with EF, you can quickly run into problems when you try to persist EF objects across different contexts and attempt to detach/attach those objects. If you are using your own POCO objects with custom t4 templates then this isn't an issue, but if you are using vanilla EF then you will want to create POCO objects for your cache.
For most simple lookup items (i.e numeric primary key and string text description), you can use Dictionary. If you have multiple fields you need to pass and back with the UI then you can build a more complete object model. Since these will be POCO objects they can then be persisted pretty much anywhere and any way you like. I recommend using caching logic outside of your MVC application such that you can easily mock the caching activity for testing. If you have multiple lists you need to cache, you can put them all in one container class that looks something like this:
public class MyCacheContainer
{
public Dictionary<int, string> GeoLocations { get; set; }
public List<Category> Categories { get; set; }
}
The next question is do you really need these objects in your entity model at all. Chances are all you really need are the primary keys (i.e. you create a dropdown list using the keys and values from the dictionary and just post the ID). Therefore you could potentially handle all of the lookups to the textual description in the construction of your view models. That could look something like this:
MyEntityObject item = Context.MyEntityObjects.FirstOrDefault(i => i.Id == id);
MyCacheContainer cache = CacheFactory.GetCache();
MyViewModel model = new MyViewModel { Item = item, GeoLocationDescription = GeoLocations[item.GeoLocationId] };
If you absolutely must have those objects in your context (i.e. if there are referential entities that tie 2 or more other tables together), you can pass that cache container into your data access layer so it can do the proper lookups.
As for assigning "valid" entities, in .Net 4 you can just set the foreign key properties and don't have to actually attach an object (technically you can do this in 3.5, but it requires magic strings to set the keys). If you are using 3.5, you might just try something like this:
myItem.Category = Context.Categories.FirstOrDefault(c => c.id == id);
While this isn't the most elegant solution and does require an extra roundtrip to the DB to get a category you don't really need, it works. Doing a single record lookup based on a primary key should not really be that big of a hit especially if the table is small like the type of lookup data you are talking about.
If you are stuck with 3.5 and don't want to make that extra round trip and you want to go the magic string route, just make sure you use some type of static resource and/or code generator for your magic strings so you don't fat finger them. There are many examples here that show how do assign a new EntityKey to a reference without going to the DB so I won't go into that on this question.
I want to use an example to explain what I want.
Assume I've following DB design:
Item (id, name, categoryID);
Category (id, name);
When user wants to create an Item (fill in form), I'll give a list of categories in a dropdownlist, and when user chooses one of the categories ASP.NET MVC will automatically bind categoryID, to the selected one. I need to present same dropdown list when editing the item with correct selected one.
Question:
But my DB is very big, and it requires around 30-40 (maybe even more) category-like tables, that contain just "id" and "name", and all tables need to be shown in dropdown list while creating some other object, and also needs to be presented while editing the object. Definitely above schema doesn't work, because it's tedious to write same logic 100 times with just different table names. (I'm using Linq2SQL)
Currently my solution is:
Make a view that's based in all such tables and in application I just call a function that construction dropdownlist from that single view. But it's still tedious to change view definition everytime I add a new table.
Do you guys think of a better solution for this tedious work, possibly using reflection or some other tecnologies.
It is not a problem "Definitely above schema doesn't work, because it's tedious to write same logic 100 times with just different table names."
If I were you, I will mark an addition interface on these class using "partial class" feature.
Then, I will write few extension method for the partial class.
If anyone interested in the solution:
I've used reflection to solve this problem.
I use reflection over DataContext to get the Table (by string name), and get its fields and construct the optionlist.