I'm working on a simple project which is based on the popular ContosoUniveristy tutorial. I want to extend some of the functionalities present in this tutorial.
I have created a table named School where I keep each schools properties like address, phone number, courses, students and so on. Later I added a foreignKey property named SchoolID to student, course and few other tables.
I have SchoolIndex page with basic layout view where all the schools are listed and user can click one to go to the details page. This details page has a _DitLayout layout with additional menu on the left where one can find links to appropriate informations e.g. contact, courses, students (like the ones stored in School table). _DitLayout is shared by all the contact, courses and students views.
Here I have a problem. When I click school on the SchoolIndex page I want the links in the menu on the left to point to this particular school properties. To do that I would have to somehow pass SchoolID to the layout page (not a good idea?). The other way is to somehow store the SchoolID when running trough views, controllers and actions. So that in the controller i could write
public class SchoolController : Controller
{
private SchoolContext db = new SchoolContext();
public ActionResult Contact()
{
int ID = //here I pass SchoolID;
// I fetch school from the database
SchoolModel school = db.School.Find(ID);
//and I map the properties to the ViewModel
ContactViewModel contact = new ContactViewModel();
contact.Address = school.Address;
contact.Phone = school.Phone;
//etc.
return View(contact);
}
}
Question is: How can I pass or store SchoolID between controllers? YES I need to pass ID not only between actions but also between controllers.
I thought that maybe i could store this ID in the cookie. Is it a good idea? Is there a better way to do it?
If you want to store data, without keeping it inside the URL (or HTTP request), then you would have to save it either in Session or Cookie.
Personally I would modify the MVC routing to incorporate the school id.
eg: /{schoolId}/{controller}/{action}.
This way schoolId will be available in any action, regardless of the controller.
You can use Session object or TempData to pass data between controllers.
public class SchoolController : Controller
{
private SchoolContext db = new SchoolContext();
public ActionResult Contact()
{
int ID = TempData["SchoolID"];
// I fetch school from the database
SchoolModel school = db.School.Find(ID);
//and I map the properties to the ViewModel
ContactViewModel contact = new ContactViewModel();
contact.Address = school.Address;
contact.Phone = school.Phone;
//etc.
return View(contact);
}
}
public class OtherController : Controller
{
public ActionResult School(int id)
{
TempData["SchoolID"] = id;
return RedirectToAction("/School/Contact");
}
}
Related
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.
I develop websites using web-forms, now I have a project where I am using MVC3 Framework with Rzor. My question is about some basic design patterns in MVC. I have a Webpage, where on left side i will Pull Categories from SQL Table, In Center I will Query another Sql Table, and few more all over the page.
So my question is...whats the best way to bring data into one webpage, all these queries are totally independant, do I need to create new MODEL for every Query? or there is a better way of doing it?
in WebForms I used user controls, where every user control had its own design & Sql Queries. I have heard about using Partial Views in MVC, but i am not sure, I guess i am having hard time understanding how to bring data into one webpage using different queries & show output on webpage.
Thanks
You should create a ViewModel. Look at Update below
This is a model that represents your page. The elements you want to show in your view should exist in your ViewModel. You will populate the ViewModel in your controller and display them on the page.
I've written an example of a shopping site page, with categories on the left and Products in the centre. Both entities would exist in different tables.
Example:
class MainPageViewModel
{
//this data is from a different table.
//and goes on the left of the page
public string Categories {get; set;}
//this data is also from a different table.
//and goes on the center of the page
public List<Products> Products {get; set;}
}
In your controller:
public class HomeController : Controller
{
// GET: /Home/
public ActionResult Index()
{
MainPageViewModel vm = new MainPageViewModel();
vm.Categories = GetCategories();
//use the GetProducts() to get your products and add them.
vm.Products.Add(...);
return View(vm); //pass it into the page
}
string[] GetCategories()
{
DataTable data = GetDataFromQuery("SELECT * FROM Categories WHERE..");
//convert the data into a string[] and return it..
}
//maybe it has to return something else instead of string[]?
string[] GetProducts()
{
DataTable data = GetDataFromQuery("SELECT * FROM Products WHERE..");
//convert the data into a string[] and return it..
}
DataTable GetDataFromQuery(string query)
{
SqlDataAdapter adap =
new SqlDataAdapter(query, "<your connection string>");
DataTable data = new DataTable();
adap.Fill(data);
return data;
}
}
Then in your view you display it appropriately:
#model MainPageViewModel
#{ ViewBag.Title = "MainPage"; }
<div id="left-bar">
<ul>
#foreach (var category in Model.Categories)
{
<li>#category</li>
}
</ul>
</div>
<div id="center-content">
<ul>
#foreach (var product in Model.Products)
{
<li>#product.Name</li>
<li>#product.Price..</li>
.....
}
</ul>
</div>
Update
This is about your comment where you mentioned that your database tables and columns change regularly.
I can't say for sure but maybe you shouldn't be making tables like that everyday, maybe there is a better database design you could have, or maybe an RDBMS isn't the right thing for you and you should look into a NoSql database (like MongoDB )
Nevertheless if you continue with the above code I suggest putting this into a data layer class of its own.
Also take a look at Dapper it's a very thin data access layer that just gets objects from the database with sql queries or stored procedures. (Just exactly what you need) It's made and used by stackoverflow.
I have a web solution (in VS2010) with two sub-projects:
Domain which holds the Model classes (mapped to database tables via Entity Framework) and Services which (besides other stuff) are responsible for CRUD operations
WebUI which references the Domain project
For the first pages I've created I have used the Model classes from the Domain project directly as Model in my strongly typed Views because the classes were small and I wanted to display and modify all properties.
Now I have a page which should only work with a small part of all properties of the corresponding Domain Model. I retrieve those properties by using a projection of the query result in my Service class. But I need to project into a type - and here come my questions about the solutions I can think of:
I introduce ViewModels which live in the WebUI project and expose IQueryables and the EF data context from the service to the WebUI project. Then I could directly project into those ViewModels.
If I don't want to expose IQueryables and the EF data context I put the ViewModel classes in the Domain project, then I can return the ViewModels directly as result of the queries and projections from the Service classes.
In addition to the ViewModels in the WebUI project I introduce Data transfer objects which move the data from the queries in the Service classes to the ViewModels.
Solution 1 and 2 look like the same amount of work and I am inclined to prefer solution 2 to keep all the database concerns in a separate project. But somehow it sounds wrong to have View-Models in the Domain project.
Solution 3 sounds like a lot more work since I have more classes to create and to care about the Model-DTO-ViewModel mapping. I also don't understand what would be the difference between the DTOs and the ViewModels. Aren't the ViewModels exactly the collection of the selected properties of my Model class which I want to display? Wouldn't they contain the same members as the DTOs? Why would I want to differentiate between ViewModels and DTO?
Which of these three solutions is preferable and what are the benefits and downsides? Are there other options?
Thank you for feedback in advance!
Edit (because I had perhaps a too long wall of text and have been asked for code)
Example: I have a Customer Entity ...
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public City { get; set; }
// ... and many more properties
}
... and want to create a View which only shows (and perhaps allows to edit) the Name of customers in a list. In a Service class I extract the data I need for the View via a projection:
public class CustomerService
{
public List<SomeClass1> GetCustomerNameList()
{
using (var dbContext = new MyDbContext())
{
return dbContext.Customers
.Select(c => new SomeClass1
{
ID = c.ID,
Name = c.Name
})
.ToList();
}
}
}
Then there is a CustomerController with an action method. How should this look like?
Either this way (a) ...
public ActionResult Index()
{
List<SomeClass1> list = _service.GetCustomerNameList();
return View(list);
}
... or better this way (b):
public ActionResult Index()
{
List<SomeClass1> list = _service.GetCustomerNameList();
List<SomeClass2> newList = CreateNewList(list);
return View(newList);
}
With respect to option 3 above I'd say: SomeClass1 (lives in Domain project) is a DTO and SomeClass2 (lives in WebUI project) is a ViewModel.
I am wondering if it ever makes sense to distinguish the two classes. Why wouldn't I always choose option (a) for the controller action (because it's easier)? Are there reasons to introduce the ViewModel (SomeClass2) in addition to the DTO (SomeClass1)?
I would solve your problem by using an auto-mapping tool (like AutoMapper) to do the mapping for you. In cases where the mapping is easy (for example if all properties from one class should be mapped to properties with the same name on another class) AutoMapper will be able to do all the hook-up work for you, and you'll have to give a couple of lines of code to note that there should be a map between the two at all.
That way, you can have your entities in Domain, and a couple of view model classes in your WebUI, and somewhere (preferrably in WebUI or a sub namespace of the same) define maps between them. Your view models will in effect be DTOs, but you won't have to worry much about the conversion process between the domain and your DTO classes.
Note: I would strongly recommend against giving your Domain entities straight to the views of your MVC web UI. You don't want EF to "stick around" all the way to the front-end layer, in case you later want to use something other than EF.
introduce ViewModels which live in the
WebUI project and expose IQueryables
and the EF data context from the
service to the WebUI project. Then I
could directly project into those
ViewModels.
The trouble with this is you soon run into problems using EF trying to 'flatten' models. I encountered something similar when I had a CommentViewModel class that looked like this:
public class CommentViewModel
{
public string Content { get; set; }
public string DateCreated { get; set; }
}
The following EF4 query projection to the CommentViewModel wouldn't work as the couldn't translate the ToString() method into SQL:
var comments = from c in DbSet where c.PostId == postId
select new CommentViewModel()
{
Content = c.Content,
DateCreated = c.DateCreated.ToShortTimeString()
};
Using something like Automapper is a good choice, especially if you have a lot of conversions to make. However, you can also create your own converters that basically convert your domain model to your view model. In my case I created my own extension methods to convert my Comment domain model to my CommentViewModel like this:
public static class ViewModelConverters
{
public static CommentViewModel ToCommentViewModel(this Comment comment)
{
return new CommentViewModel()
{
Content = comment.Content,
DateCreated = comment.DateCreated.ToShortDateString()
};
}
public static IEnumerable<CommentViewModel> ToCommentViewModelList(this IEnumerable<Comment> comments)
{
List<CommentViewModel> commentModels = new List<CommentViewModel>(comments.Count());
foreach (var c in comments)
{
commentModels.Add(c.ToCommentViewModel());
}
return commentModels;
}
}
Basically what I do is perform a standard EF query to bring back a domain model and then use the extension methods to convert the results to a view model. For example, the following methods illustrate the usage:
public Comment GetComment(int commentId)
{
return CommentRepository.GetById(commentId);
}
public CommentViewModel GetCommentViewModel(int commentId)
{
return CommentRepository.GetById(commentId).ToCommentViewModel();
}
public IEnumerable<Comment> GetCommentsForPost(int postId)
{
return CommentRepository.GetCommentsForPost(postId);
}
public IEnumerable<CommentViewModel> GetCommentViewModelsForPost(int postId)
{
return CommentRepository.GetCommentsForPost(postId).ToCommentViewModelList();
}
Talking about Models, ViewModels and DTOs is confusing, personally I don't like to use these terms. I prefer to talk about Domain Entities, Domain Services, Operation Input/Result (aka DTOs). All of these types live in the Domain layer. Operations is the behavior of Entities and Services. Unless you are building a pure CRUD application the presentation layer only deals with Input/Result types, not Entities. You don't need additional ViewModel types, these are the ViewModels (in other words, the Model of the View). The View is there to translate the Operation Results to HTML, but the same Result could be serialized as XML or JSON. What you use as ViewModel is part of the domain, not the presentation layer.
I'm new to ASP.NET MVC and want to create a small order management tool. My database contains the tables Orders and Articles (and a few other ones), and I generated an EF Model from my database, so I can use the full power of the EF mappings (e.g. db.Orders.Articles)
My two main relations which I'm concerned about are Orders and Articles.
An order can have many articles
An article can only belong to one order.
I've created an OrdersController with an Create action to create an order:
//
// GET: /Orders/Create
public ActionResult Create()
{
Order order = new Order()
{
// filling some order columns, e.g. date
};
Article article = new Article()
{
// ... article columns
};
order.Articles.Add(article);
return View(order);
}
//
// POST: /Orders/Create
[HttpPost]
public ActionResult Create(Order order)
{
// i know i should care more about error handling, but now ommit it
db.Orders.AddObject(order);
db.SaveChanges();
return RedirectToAction("index");
}
So I'm directly binding an EF Object to a view (read somewhere not to do that and use a view model instead, but don't really know what that view model should look like)
My view contains the Order form as well as the article form (because i want to create a order and articles at the same time and not seperate). I used these greate EditorFor Methods to do that.
And now to my problem: If i hit the submit button, the app crashes as soon as it comes to the HttpPost Create Method (when mapping the order) with this error message:
Error Message: The EntityCollection
has already been initialized. The
InitializeRelatedCollection method
should only be called to initialize a
new EntityCollection during
deserialization of an object graph.
If i hit continue in VS2010, it will complete saving the order - so my question is how to solve this problem in a reliable way.
Thanks in advance and sorry for that long story :)
I solved my problem now by using a separate ViewModel like #Yakimych advised me. However I did not copy all the attributes from the EF models, instead I just refer to them. My ViewModel looks like this:
public class NewOrderViewModel {
public Order { get; set; }
public List<Article> { get; set; }
}
I'm creating a shopping basket in ASP.NET using session variables to pass the data from a shopping.aspx page to basket.aspx, currently I have the pages passing the primary key of the product with a gridview on the basket.aspx used to display the data from the database.
However this only works for one item at a time, how can I extended the session variable so multiple products can be added, as well as quantities etc?
You can use your own object, eg. Basket which can have one or more properties.
Object should be market as Serializable.
For example:
[Serializable]
public class Basket
{
public List<BasketItem> Items {get;set;}
public int UserId {get;set;}
}
[Serializable]
public class BasketItem
{
//...
}
You can put (almost) any object into the session, not just strings. So you could use a List<string> for a list of keys, or even a List<Product>.
EDIT
So in the first page you would get
var bookids = new List<string>();
// collect all book IDs into the 'bookids' list
Session["bookIDs"] = bookids;
and in the second page:
var bookids = Session["bookIDs"] as List<string>;
// use all IDs