Too many select lists polluting my controller actions - asp.net

I have an ASP.Net MVC application and some of the controller actions have about 10 select lists (others have more). Currently my code looks like this:
public ActionResult Edit(int carId)
{
CreateCar model = new CreateCar();
model.Makes = Helper.Makes();
model.Models = Helper.Models();
model.Colors = Helper.Colors();
model.EngineSizes = Helper.EngineSizes();
model.Materials = Helper.Materials();
model.FuelTypes = Helper.FuelTypes();
model.WheelSizes = Helper.WheelSizes();
model.BodyTypes = Helper.BodyTypes();
//more select lists below this
return View(model)
}
In my views i setup select lists like this:
#Html.DropDownListFor(x => x, Model.Makes)
I have code that looks like this in a number of actions and i feel there is a better way of doing this so my actions are not polluted with these select lists.
The only option i can think of to get around this is to actullly call the Helper class in the views e.g.
#Html.DropDownListFor(x => x, Helper.Makes())
Is this approach considered bad practice and are there any other approaches to deal wi this issue?

Generally, yes, it's bad practice to do things like that in your view. The controller is responsible for wiring everything up, so your code should go there. Now, it depends a lot on what your Helper class is doing. If the select lists are just generated via some bit of code, it's probably not that bad for that to happen in the view, but what you don't want to be doing is issuing database queries while the view is being rendered. If your helper is interacting with a database, then keep it in the controller.
That said, what is the real issue here? Sure that's a lot of select lists, but I wouldn't go so far as to say it's "polluting" your action. It's very clear what your doing. The action is responsible for creating the model for your view, and that's what it's doing. Just because there may be a lot of lines in your code, doesn't necessarily mean it's "bad" or "wrong".
However, if you're repeating this in a lot of places, I would recommend factoring it out into a private or protected method on your controller. For example:
public ActionResult Edit(int carId)
{
...
PopulateSelectLists(model);
return View(model);
}
[HttpPost]
public ActionResult Edit(Foo model, int carId)
{
...
PopulateSelectLists(model);
return View(model);
}
private void PopulateSelectLists(Foo model)
{
model.Makes = Helper.Makes();
model.Models = Helper.Models();
model.Colors = Helper.Colors();
model.EngineSizes = Helper.EngineSizes();
model.Materials = Helper.Materials();
model.FuelTypes = Helper.FuelTypes();
model.WheelSizes = Helper.WheelSizes();
model.BodyTypes = Helper.BodyTypes();
//more select lists below this
}
Then, everything is clean and tidy.

Related

Loading multiple sets of data in a content page in ASP.NET MVC Entitiy Framework

I need to load multiple entity types in my View page. I am using ViewModel for this purpose. However, I need to make around 5-6 database calls to load each set of data and assign them to the relevant property of the ViewModel. I wonder if this is a recommended approach since it requires multiple database calls. Or, am I being over-concerned about this? Here is a snapshot from my code:
var model = new EntryListVM();
string userid = "";
if (ViewBag.CurrentUserId == null)
userid = User.Identity.GetUserId();
else
userid = ViewBag.CurrentUserId;
ViewBag.CurrentUserId = userid;
//First database call
model.DiscussionWall = db.DiscussionWalls.Find(wallId);
//Second database call to learn if the current students has any entry
model.DiscussionWall.DoesStudentHasEntry = db.Entries.Any(ent => ent.DiscussionWallId == wallId && ent.UserId == userid);
model.PageIndex = pageIndex;
//Third database call
model.TeacherBadges = db.Badges.Where(b => b.CourseId == model.DiscussionWall.CourseId && b.IsSystemBadge == false && b.IsEnabled == true).ToList();
//Fourth database call
model.Reactions = db.Reactions.Where(re => re.CourseId == model.DiscussionWall.CourseId).ToList();
int entryPageSize = Convert.ToInt32(ConfigurationManager.AppSettings["EntryPageSize"]);
int firstChildSize = Convert.ToInt32(ConfigurationManager.AppSettings["FirstChildSize"]);
List<ViewEntryRecord> entryviews = new List<ViewEntryRecord>();
bool constrainedToGroup = false;
if (!User.IsInRole("Instructor") && model.DiscussionWall.ConstrainedToGroups)
{
constrainedToGroup = true;
}
//Fifth database call USING VIEWS
//I used views here because of paginating also to bring the first
//two descendants of every entry
entryviews = db.Database.SqlQuery<ViewEntryRecord>("DECLARE #return_value int;EXEC #return_value = [dbo].[FetchMainEntries] #PageIndex = {0}, #PageSize = {1}, #DiscussionWallId = {2}, #ChildSize={3}, #UserId={4}, #ConstrainedToGroup={5};SELECT 'Return Value' = #return_value;", pageIndex, entryPageSize, wallId, firstChildSize, userid, constrainedToGroup).ToList();
model.Entries = new List<Entry>();
//THIS FUNCTION MAP entryviews to POCO classes
model.Entries = ControllerUtility.ConvertQueryResultsToEntryList(entryviews);
//Sixth database call
var user = db.Users.Single(u => u.Id == userid);
model.User = user;
I wonder if this is too much of a burden for the initial page load?
I could use SQL-View to read all data at once, but I guess I would get a too complicated data set to manage.
Another option could be using Ajax to load the additional results after the page loading (with the main data) is completed. For example, I could load TeacherBadges with AJAX after the page is being loaded.
I wonder which strategy is more effective and recommended? Are there specific cases when a particular strategy could be more useful?
Thanks!
It all depends on your scenario - different scenarios have different ways of doing things. There is no single right way of doing things that are similar in nature. What might work for me might not work for you. Ever heard that saying: there are many ways to kill a cat? Well this certainly applies to programming.
I am going to answer based on what I think you are asking. Your questions are very broad and not that specific.
However, I am not sure if this is a recommended approach since it
requires multiple database calls.
Sometimes you need to do one database call to get data, and sometimes you need to do more than one database call to get the data. For example:
User details with addresses: one call for user and one call for addresses
User details: one call
I am using ViewModel for this purpose.
Using view models for your views is a good thing. If you want to read up more on what I had to say about view models then you can go and read an answer that I gave on the topic:
What is ViewModel in MVC?
View models are ideal for when you have data that is coming from multiple datasets. View models can also be used to display data coming from one dataset, for example:
Displaying user details with multiple addresses
Displaying only user details
I read the data in the controller in separate linq statements, and
assign them to the relevant List property of the ViewModel.
I would not always return a list - it all depends on what you need.
If I have a single object to return then I will populate a single object:
User user = userRepository.GetById(userId);
If I have a list of objects to return then I will return a list of objects:
List<User> users = userRepository.GetAll();
It is of no use to return a single object and then to populate a list for this object:
List<User> user = userRepository.GetByUserId(userId).ToList();
Second option could be using SQL-View to read all data with one
database call, and then map them to the entities properly in
controller.
This is similar to your first question, how you return your data on the database level is up to you. It can be stored procedures or views. I personally prefer stored procedures. I have never used views before. Irrespective of what you choose your above mentioned repository methods should still look the same.
Third option could be using Ajax to load the additional results after
the page loading (with the main data) is completed.
You can do this if you want to. I would not do it if it is not really needed. I try to load data on page load. I try to get as much data on the screen before the page is fully loaded. There have been times that I had to go the AJAX route after the page was loaded. After the page was loaded I had to do an AJAX call to load my HTML table.
If you really just need to have data displayed then do just that. You do not need any fancy ways of doing this. Maybe later you need to change on screen data, then AJAX is cool to use.
I wonder which strategy is more effective and recommended? Are there
specific cases when a particular strategy could be more useful?
Let us say you want to display a list of users. We do a database call and return the list to the view. I do not normally use view models if I only return a list:
public class UserController : Controller
{
private IUserRepository userRepository;
private IAddressRepository addressRepository;
public UserController(IUserRepository userRepository, IAddressRepository addressRepository)
{
this.userRepository = userRepository;
this.addressRepository = addressRepository;
}
public ActionResult Index()
{
List<User> users = userRepository.GetAll();
return View(users);
}
}
And your view could look like this:
#model List<YourProject.Models.User>
#if (Model.Count > 0)
{
foreach (var user in Model)
{
<div>#user.Name</div>
}
}
If you need to get a single user's details and a list of addresses, then I will make use of a view model because now I need to display data coming from multiple datasets. So a user view model can look something like this:
public class UserViewModel
{
public UserViewModel()
{
Addresses = new List<Address>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
The your details action method could look like this:
public ActionResult Details(int id)
{
User user = userRepository.GetById(id);
UserViewModel model = new UserViewModel();
model.Name = user.Name;
model.Addresses = addressRepository.GetByUserId(id);
return View(model);
}
And then you need to display the user details and addresses in the view:
#model YourProject.ViewModels.UserViewModel
<div>First Name: #Model.Name</div>
<div>
#if (Model.Addresses.Count > 0)
{
foreach (var address in Model.Address)
{
<div>#address.Line1</div>
<div>#address.Line2</div>
<div>#address.Line3</div>
<div>#address.PostalCode</div>
}
}
</div>
I hope this helps. It might be to broad of an answer but it can guide you on the correct path.
Includes for linked data
For linked data it's simple (you probably know this way):
var users = context.Users.Include(user => user.Settings).ToList();
It queries all users and pre-loads Settings for each user.
Use anonymous class for different data sets
Here is an example:
context.Users.Select(user => new
{
User = user,
Settings = context.Settings
.Where(setting => setting.UserId == user.Id)
.ToList()
}).ToList();
You still kinda need to choose your main query collection (Users in this case), but it's an option. Hope it helps.

linq-to-sql "an attempt has been made to attach or add an entity that is not new"?

I've been getting several errors:
cannot add an entity with a key that is already in use
An attempt has been made to attach or add an entity that is not new, perhaps having been loaded from another datacontext
In case 1, this stems from trying to set the key for an entity versus the entity. In case 2, I'm not attaching an entity but I am doing this:
MyParent.Child = EntityFromOtherDataContext;
I've been using using the pattern of wrap everything with a using datacontext. In my case, I am using this in a web forms scenario, and obviously moving the datacontext object to a class wide member variables solves this.
My questions are thus 2 fold:
How can I get rid of these errors and not have to structure my program in an odd way or pass the datacontext around while keeping the local-wrap pattern? I assume I could make another hit to the database but that seems very inefficient.
Would most people recommend that moving the datacontext to the class wide scope is desirable for web pages?
Linq to SQL is not adapted to disconnected scenarios. You can copy your entity to a DTO having a similar structure as the entity and then pass it around. Then copy the properties back to an entity when it's time to attach it to a new data context. You can also deserialize/reserialize the entity before attaching to a new data context to have a clean state. The first workaround clearly violates the DRY principle whereas the second is just ugly. If you don't want to use any of these solution the only option left is to retrieve the entity you're about to modify by its PK by hitting the DB. That means an extra query before every update. Or use another ORM if that's an option for you. Entity Framework 4 (included with .NET 4) with self-tracking entities is what I'm using currently on a web forms project and everything is great so far.
DataContext is not thread-safe and should only be used with using at the method level, as you already do. You can consider adding a lock to a static data context but that means no concurrent access to the database. Plus you'll get entities accumulated in memory inside the context that will turn into potential problems.
For those that came after me, I'll provide my own take:
The error "an attempt has been made to add or attach an entity that is not new" stems from this operation:
Child.Parent = ParentEntityFromOtherDataContext
We can reload the object using the current datacontext to avoid the problem in this way:
Child.Parent = dc.Entries.Select(t => t).Where(t => t.ID == parentEntry.ID).SingleOrDefault();
Or one could do this
MySubroutine(DataContext previousDataContext)
{
work...
}
Or in a web forms scenario, I am leaning to making the DataContext a class member such as this:
DataContext _dc = new DataContext();
Yes, the datacontext is suppose to represent a unit of work. But, it is a light-weight object and in a web forms scenario where a page is fairly transient, the pattern can be changed from the (using dc = new dc()) to simply using the member variable _dc. I am leaning to this last solution because it will hit the database less and require less code.
But, are there gotchas to even this solution? I'm thinking along the lines of some stale data being cached.
What I usually do is this
public abstract class BaseRepository : IDisposable
{
public BaseRepository():
this(new MyDataContext( ConfigurationManager.ConnectionStrings["myConnection"].ConnectionString))
{
}
public BaseRepository(MyDataContext dataContext)
{
this.DataContext = dataContext;
}
public MyDataContext DataContext {get; set;}
public void Dispose()
{
this.DataContext.Dispose();
}
}
Then imagine I have the following repository
public class EmployeeRepository : BaseRepository
{
public EmployeeRepository():base()
{
}
public EmployeeRepository(MyDataContext dataContext):base(dataContext)
{
}
public Employee SelectById(Guid id)
{
return this.DataContext.Employees.FirstOrDefault(e=>e.Id==id);
}
public void Update(Employee employee)
{
Employee original = this.Select(employee.Id);
if(original!=null)
{
original.Name = employee.Name;
//others
this.DataContext.SubmitChanges();
}
}
}
And in my controllers (I am using asp.net mvc)
public ActionResult Update(Employee employee)
{
using(EmployeeRepository employeeRepository = new EmployeeRepository())
{
if(ModelState.IsValid)
{
employeeRepository.Update(employee);
}
}
//other treatment
}
So the datacontext is properly disposed and I can use it across the same instance of my employee repository
Now imagine that for a specific action I want the employee's company to be loaded (in order to be displyed in my view later), I can do this:
public ActionResult Select(Guid id)
{
using(EmployeeRepository employeeRepository = new EmployeeRepository())
{
//Specifying special load options for this specific action:
DataLoadOptions options = new DataLaodOptions();
options.LoadWith<Employee>(e=>e.Company);
employeeRepository.DataContext.LoadOptions = options;
return View(employeeRepository.SelectById(id));
}
}

Writing Useful Unit Tests

I have a simple page with a Grid that I'm binding a collection of objects to. I also have some simple functionality on the grid to edit and save rows. I would like to write unit tests for this page, but it's not really making sense to me.
For example:
Private Sub LoadGrid()
'Populate Collection
grid.datasource = MyCollection
grid.databind()
end sub
I guess a Sub really doesn't need a unit test, but what if this were a function that returned true when the grid had been loaded. How do you write a unit test for this? What other test should be done on a simple web page like this?
As always, thanks to everyone who give any sort of input.
How do you write a unit test for this?
The first step is actually making your form testable. Have a look at this page for separating UI and BL layers, there are about a bajillion different ways to implement MVC, MVP, and all of its variants, and there's no One True Way™ to do it. So long as your code is sane and consistent, other people will be able to work on your code.
I personally find the following pattern works in most cases for testing UIs:
Create an interface representing your Model.
Create a class for your controller which handles all the updates to the model.
Your view should listen to changes to the model.
So in the end, you end up with something like this (sorry, my VB-fu is rusty, writing this in C# instead):
interface IProductPageModel
{
int CurrentPage { get; set; }
int ItemsPerPage { get; set; }
DataSet ProductDataSet { get; set; }
}
class ProductPageController
{
public readonly IProductPageModel Model;
public ProductPageController(IProductPageModel model)
{
this.Model = model;
}
public void NavigateTo(int page)
{
if (page <= 0)
throw new ArgumentOutOfRangeException("page should be greater than 0");
Model.CurrentPage = page;
Model.ProductDataSet = // some call to retrieve next page of data
}
// ...
}
This is concept code, of course, but you can see how its very easy to unit test. In principle, you could re-use the same controller code in for desktop apps, silverlight, etc since your controller doesn't depend directly on any particular view implementation.
And finally on your form side, you'd implement your page similar to:
public class ProductPage : Page, IProductPageModel
{
ProductPageController controller;
public ProductPage()
{
controller = new ProductPageController(this);
}
public DataSet ProductDataSet
{
get { return (DataSet)myGrid.DataSource; }
set { myGrid.DataSource = value; myGrid.DataBind(); }
}
protected void NavigateButton_OnCommand(object sender, CommandEventArgs e)
{
controller.NavigateTo(Convert.ToInt32(e.CommandArgument));
}
}
Here there's no real distinction between view and model -- they're the same entity. The idea is to make your code-behind as "stupid" as possible, so that as much testable business logic is contained in the controller as possible.
What other test should be done on a
simple webpage like this?
You'd want tests for any sort of form validation, you want to make sure you're throwing exceptions in exceptional cases, ensuring that your controller methods update your model in an expected way, and so on.
Juliet is right.
The line of code where you said
'Populate Collection
that is the testable part. You can do assertions on if the collection is null, if it has items, if it has exactly 42 items. But that would be an integration test.
If you can isolate all the calls to the database (the part that returns a datareader), then return a empty, fake DbDataReader, then you can test everything inbetween the UI and the database.
Tests that spin up a browser and verify that a table is render, similarly is a integration test that will depend on having IIS up and working (and a DB up and working, unless you have a repository you can fake)
If you are just getting started, I would look for all the easy to test code first, such as methods that do have dependencies on the database, then move on the tricker tests that require mocking/stubbing/faking database servers, etc.

ASP.NET MVC: How to transfer more than one object to View method?

I finished NerdDinner tutorial and now I'm playing a bit with project.
Index page shows all upcoming dinners:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
return View(dinners);
}
In DinnerRepository class I have method FindAllDinners and I would like to add to above Index method number of all dinners, something like this:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(dinners, numberOfAllDinners);
}
Of course, this doesn't work. As I'm pretty new to OOP I would need help with this one.
Thanks,
Ile
Create view model:
public class DinnerViewModel
{
public List<Dinner> Dinners { get; set; }
public int NumberOfAllDinners { get; set; }
}
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(new DinnerViewModel { Dinners = dinners, NumberOfAllDinners = numberOfAllDinners } );
}
You need to create a "wrapper" object that contains the objects you wish to pass as public properties of it. For instance, create an object called DinnerViewModel and give it two properties and set these with two properties, one a List called Dinners and one an int called DinnerCount. Then pass the DinnerViewModel to the view and you can then access Model.Dinners and Model.DinnerCount
In your case I would prefer the solution mentioned by LukLed.
In general you could of course also transfer multiple values from your controller to your view using ViewData:
ViewData["dinners"] = dinners;
ViewData["numberOfAllDinners"] = 150;
...
For more information also take a look at this link.
Just simply use dinners.Count property instead.
Remember, you start off using the ViewData inherts in you .aspx filesand returning the same in you return statements. Because of that, I figure that it was an issue with the Inherits attribute on the top of the ASP.NET files. But, if you are getting the error when trying to create or edit a new Dinner when you are on the 'Upcoming Dinners' page (generated from the Details.aspx and the LINQ file that gets all Dinners that are after todays date), go into your 'Controllers' directory, specifically the DinnerController.cs. Then look at the Edit and or Create methods. the answer lies right here. If you put breakpoints on these methods, you should be able to figure it out. If not, continue reading:
Look where it fails, the 'return...' line. Maybe I am the only person who forgot to change this, but my error is the same as people are getting in this page and this os how I fixed it.....the 'return(dinner)' line, in Create and Edit (and any others that you are having issues with), they are using the NerDinner.Model.Dinner / ViewData method. However, if you change it to the ViewModel return method instead, it should fix it, For example: 'return(new DinnerFormViewModel(dinner));', it should work for you. I hope this helps, as it was what my issue was. Just a simple overlook.

ASP.NET MVC Model List in controller

I'm using ajax and trying to pass the Model back, which is a generic List < T >. I tried Model.ToList() but I don't think I'm going about this correctly. I think it is already setup but I just need to find the method or property name to get the List that was posted, which might not work since my Model from the controller contains a couple of lists to pass to different partials and one of these partials, I'm trying to update the partial view. I'm basically trying to sort the list. So using an AJAX to call the controller but I need the list to sort and call the partial again. I hope my explanation wasn't too confusing.
If these are just single values you are passing back, such as int, strings, floats, etc. You can just put the array in the action like so:
public ActionResult MyAction (int id, string[] value)
Or you can use something called a FormCollection
public ActionResult MyAction (int id, FormCollection form) {
string[] values = form.GetValues("value");
Or you can use Request.Form (this is not recommended, because the above two are more recommended
public ActionResult MyAction(int id) {
string[] values = Request.Form.GetValues("value");
Hope this helps. I didn't totally understand what your question was, but it seemed to be dealing with how to get an array of posted values.

Resources