I'm building a very simple CRUD web-application (ASP.NET MVC) for tennisplayers and the tournaments they can participate.
On a specific page I want to show all tournaments in the database with a title at the top saying 'All Tournaments' with between brackets the amount of records in the database.
My cshtml would look like this:
#model System.Collections.Generic.IEnumerable<TMS.BL.Domain.Tournament>
#{
ViewBag.Title = "All Tournaments";
Layout = "_Layout";
}
<h3>All Tournaments (#Model.Count())</h3>
#if (!#Model.Any())
{
<p>No tournaments were found...</p>
}
else
{
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Starts</th>
<th scope="col">Ends</th>
<th scope="col">Org. Club</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
#foreach (var t in Model)
{
<tr>
<td>#t.Name</td>
<td>#t.StartDate.ToString("ddd, dd/MM/yyyy")</td>
<td>#t.EndDate.ToString("ddd, dd/MM/yyyy")</td>
<td>#t.OrganizingClub.Name (#t.OrganizingClub.Province - #t.OrganizingClub.Town)</td>
<td>
<a asp-controller="Tournament" asp-action="Details" asp-route-id="#t.Id" class="btn btn-primary btn-sm">Details</a>
</td>
</tr>
}
</tbody>
</table>
}
}
The controller for this page is the TournamentController. This controller is using a Manager object which contains the dbcontext. The GetAllTournamentsWithOrgClubAndParticipants() method returns an IEnumerable of tournament objects (with an include of the club and pariticpants, but for my question this is of no importance).
public class TournamentController : Controller
{
private IManager _mgr;
public TournamentController(IManager manager)
{
_mgr = manager;
}
public IActionResult Index()
{
return View(_mgr.GetAllTournamentsWithOrgClubAndParticipants());
}
When I load the page, I see that the same query is fired 3 times. once for the #Model.Count() in the title of the webpage, once for the #Model.Any() to determine wheter or not to show the table and once in the foreach loop. Now I know this is because of defered excecution and I could resolve this by adding a ToList() behind the GetAllTournamentsWithOrgClubAndParticipants() in the controller class, but I often hear NOT TO USE the ToList() method because of the way you are loading everything into memory doing this. To my feeling for this case it's still better than excecuting the same query 3 times in a row or am I wrong? Any other way I could resolve this?
Thank you very much!
By returning IEnumerable you are telling the caller that it will get something that can be enumerated over, it does not set the underlying type. So for instance if your manager/repository method returns:
var result = context.Tournaments.Include(t => t.OrganizingClub).Include(t => t.Participants);
return result;
then what is sent back is effectively an EF Query that can be enumerated over. Your Razor code-behind will effectively execute it each time to get the Count, Any, and then to iterate over with your foreach. This should execute 3 slightly different queries. The first being a SELECT COUNT(*) FROM... the second being a IF EXISTS SELECT TOP (1) FROM... then the SELECT t.Id, t.Name, ... FROM with the same filters and joins.
Changing this to:
var result = context.Tournaments.Include(t => t.OrganizingClub).Include(t => t.Participants).ToList();
Will load the details into memory once, then the Count and Any will just be in-memory operations. This in itself for this example is not bad, but it is worth understanding the potential consequences of operations like this. When you're dealing with a data volume that will be manageable on a single screen (I.e. 10's of records, not 1000's+) then there is essentially no harm in returning a materialized list of data. However, as systems grow, design decisions based on smaller data sets can come back to bite you in the butt. For instance, if you want to introduce pagination for results. You'll want the population to the view to be running a query that ultimately loads and returns just one page of data, not loading all rows to send one page to the view.
Even with dealing with smaller sets of data it is worthwhile to understand and utilize EF's projection capability using Select to populate view models. In your example you will be loading all data from the Tournament, OrganizationClub, and Participants even though your view only needs a handful of fields. This can also open the door for unexpected future performance hits because you are serializing entities. If we later add another navigation property or collection to a Tournament, Club etc. and even if this view doesn't need that additional property/collection, simply sending a Tournament to the view could result in the serializer "touching" that navigation property and triggering a lazy load. (Extra queries) All the sudden a new requirement for one area of the application has a performance impact in many other areas you didn't even touch.
Looking at the View code:
<td>#t.Name</td>
<td>#t.StartDate.ToString("ddd, dd/MM/yyyy")</td>
<td>#t.EndDate.ToString("ddd, dd/MM/yyyy")</td>
<td>#t.OrganizingClub.Name (#t.OrganizingClub.Province - #t.OrganizingClub.Town)</td>
<td>
<a asp-controller="Tournament" asp-action="Details" asp-route-id="#t.Id" class="btn btn-primary btn-sm">Details</a>
</td>
We need a Tournament Name, Startdate, EndDate, Organizing Club Name, Province, and Town, plus the Tournament ID.
This can be simplified down to a minimal view model called for example TournamentSummaryViewModel:
[Serializable]
public class TournamentSummaryViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string OrganizingClubName { get; set; }
public string ClubProvince { get; set; }
public string ClubTown { get; set; }
}
then to project this:
var result = context.Tournaments
.Select( t => new TournamentSummaryViewModel
{
Id = t.Id,
Name = t.Name,
StartDate = t.StartDate,
EndDate = t.EndDate,
OrganizingClubName = t.OrganizingClub.Name,
ClubProvice = t.OrganizingClub.Province,
ClubTown = t.OrganizingClub.Town
}).ToList();
The advantages of this is that it minimizes the query to just the columns the view will need. This avoids surprises as the data model changes/grows over time as we don't serialize entities so there are no lazy load risks. When using projection with Select (Or Automapper's ProjectTo) you don't even need to worry about eager loading /w Include. It also reduces the payload size to the view, and helps hide the overall domain structure of your application from people peeking and even tampering with the data being sent via browser debugging tools.
In the above example we just issue the ToList() call on the assumption that the data volume will be reasonable, but we could just as easily let it return the IQueryable for the Razor code to interact with. You can map fields to flatten data (such as adding details from the OrganizingClub) or consolidate data such as if you want ParticpantCount = t.Paricipants.Count(), or nest additional view models Selected from related data. The key is to avoid embedding the entities themselves in the returned ViewModel. (As they can form a ticking time bomb.)
Related
Translate code in Laravel to asp.net mvc. I am passing a subset of the model and summarizing the fields by year. My EF model has 50+ fields.
This is my Laravel route:
Route::get('/metrics', 'MetricsController#index');
My Laravel Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
class MetricsController extends Controller
{
public function index()
{
$metrics = DB::select('select year(date_id) as Year, Sum(Revenue) from metrics Group By year(date_id)');
return ($metrics);
}
}
I would like to display the model data in a #foreach loop in the view? The dump in the Laravel view displays the data in the following way.
[{"Year":2009,"Sum(Revenue)":"61302670.65"},
{"Year":2011,"Sum(Revenue)":"68397989.00"},
{"Year":2012,"Sum(Revenue)":"69245803.00"},
{"Year":2013,"Sum(Revenue)":"67184051.00"},
{"Year":2014,"Sum(Revenue)":"33445434.00"}]
I will keep it simple, sticking to the fact that you need to show the data in the View. For that, I will assume that you already have your DbContext, Models and data loading parts ready.
Model
I will create a sample model just for the sake of example. But it could, obviously, be more advanced.
public class Result
{
public string Year { get; set; }
public decimal SumRevenue { get; set; }
}
Action Method (in the Controller)
The most important here is the fact that you are loading a "List of" model as result of your query. As you are using EF, you could load it directly from your DbContext or probably use a repository pattern. Since we don't know, I am just making a simple method call so just imagine the DB logic is in there :)
public ActionResult Index()
{
// Here is where you load your data. I am using this line just as example.
List<Result> resultList = RandomLibrary.LoadData();
return View(resultList)
}
View
Remember to prepare your View to receive the List you got in the Action Method.
#model List<Result>
#foreach(var item in Model)
{
<p>
<strong>Year: </strong>#item.Year
<br />
<strong>Sum of revenue: </strong>#item.SumRevenue
</p>
}
This is an oversimplification of the process. You can see it in more details in the official documentation. Or just let us know if you will need a more detailed explanation as your case might be a bit different.
I have an asp.net-mvc site and i am trying to understand the recommended practice around transactions and lazy loading.
When doing research around implementating nhibernate second level cache, one rule of thumb is around wrapping everything in a transaction (see here and here). I am trying to reconcile this point with how lazy loading works because they seems to be at odds given deferred query execution.
Lets say i have a query that looks like this . .
I have two entities:
Project
Owner
Here is a query in a transaction
public CacheTestViewModel GetCacheTestViewModel()
{
var vm = new CacheTestViewModel();
var session = Repository.Session;
using (var tx = session.BeginTransaction())
{
vm.Projects = Repository.Session.Query<Project>().Cacheable()
tx.Commit();
}
return vm;
}
but in my view I have the following code:
<% foreach (var project in Model.Projects {%>
<% = project.Owner %>
<%} %>
so that queries the Owner object when required due to lazy loading but when reviewing nhibernate profiler, it appears that these queries are happening AFTER the transaction commit is done so wanted to see if that breaks the principle in the first place. It seems like this will always be an issue with any lazy loading (either Fetch().Select() or Fetch().Select().Batch()
What is the recommended practice here?
You should create ViewModels that represent everything that the View needs to be rendered. You should not be issuing database queries from the view if at all possible. To summarize the link above here:
It increase the time that the connection to the database have to be
open. The recommendation is to keep that open only for the duration of
the action, not throughout the lifetime of the request.
It make it
that much harder to understand what are the data requirements for a
particular action is.
When writing views, you shouldn't be bothered
with thinking about persistence, or the number of queries that you
views are generating.
The views are often the most changeable parts in
the application, and having the application issue queries from the
views may result in significant changes to the way the application
data access pattern between revisions.
Most often, queries from the
views result from lazy loading, Select N+1 or similar bad practices.
We strongly recommend that you'll avoid generating queries in the
view, instead, perform all your queries in the action, and provide in
memory access only to the view for them to render themselves.
(Emphasis on the last point mine).
The practical implications of this are that you should not be lazy loading anything in a view. So what should you do instead? This is where the ViewModel layer comes in. You should be fully populating a thin ViewModel with the information you need and then rendering the view with that.
Furthermore, ViewModels shouldn't even contain classes mapped with NHibernate (this appears to be what you're doing in your example).
With all this in mind I would change your current code:
public class CacheTestViewModel
{
public List<ProjectViewModel> Projects { get; set; }
}
public class ProjectViewModel
{
public string Owner { get; set; }
/* etc. */
}
.. And then your code to populate those ViewModels:
public CacheTestViewModel GetCacheTestViewModel()
{
var vm = new CacheTestViewModel();
var session = Repository.Session;
using (var tx = session.BeginTransaction())
{
var projects = Repository.Session.Query<Project>().Cacheable();
foreach (var project in project)
{
vm.Projects.Add(new ProjectViewModel { Owner = project.Owner.Name });
}
tx.Commit();
}
return vm;
}
Now you might be thinking "gee, that's a lot of code to map domain entities to ViewModels. I can see that getting out of hand." You'd be right about that. There's a library called AutoMapper that you can use to semi-automate your mappings.
The recommended practice is to have a view model that is separate from your domain model, and to have one view model per view. For a Projects view, there would be a corresponding ProjectsViewModel class that might look like:
public class ProjectsViewModel
{
public IList<ProjectViewModel> Projects { get; set; }
public class ProjectViewModel
{
public int ProjectId { get; set; }
public string Title { get; set; }
public string OwnerName { get; set; }
}
}
The ProjectsViewModel class would be fully populated in the transaction scope so that no lazy loading is necessary.
Somewhere I had to use a linq statement for select a result set from my Model that Controller returned in Index ActionResult.
for doing this, I googled "how to use linq in view razor" and I get the result and my application worked properly, but I see some recommendation that say "Don't use Linq in view".
Why we shouldn't use it however it's possible?
And if I don't want using it what's the solution?
This is my query :
#using System.Linq
#{var lst = (from x in item.Showtimes select x.ShowtimeDate).Distinct();}
#foreach (var showTimeItem in lst)
{
<option value="#showTimeItem">#showTimeItem</option>
}
UPDATE
This is my controller Index
public ActionResult Index()
{
MelliConcert.Models.MelliConcertEntities db = new Models.MelliConcertEntities();
var listOfConcerts = (from x in db.Concert
orderby x.ID ascending
select x).Take(15).ToList();
return View(listOfConcerts);
}
And i use it in the view like this :
#model IEnumerable<MelliConcert.Models.Concert>
#foreach (var item in Model)
{
#if (item.OpenedForFirst == true)
{
//...
}
//Some Codes
#using System.Linq
#{var lst = (from x in item.Showtimes select x.ShowtimeDate).Distinct();}
#foreach (var showTimeItem in lst)
{
<option value="#showTimeItem">#showTimeItem</option>
}
}
My linq statement placed in this loop.
What should i do?
There's nothing inherently wrong with using LINQ in a view per se. The problem isn't that you're using LINQ, the problem is that you're doing this:
#{var lst = (from x in item.Showtimes select x.ShowtimeDate).Distinct();}
Any time you have to declare and set a variable inside your view, that's probably an indication that you need to modify your model. Your view model should have a property on it for this purpose. Something like this:
public IEnumerable<SomeType> DistinctShowtimes
{
get
{
return (from x in item.Showtimes select x.ShowtimeDate).Distinct();
}
}
Then the point about LINQ in the view becomes moot, as all you'd need is this:
#foreach (var showTimeItem in Model.DistinctShowtimes)
{
<option value="#showTimeItem">#showTimeItem</option>
}
UPDATE (in response to your updated question):
Now the problem (albeit slightly less of one) is this:
#model IEnumerable<MelliConcert.Models.Concert>
While this works fine, it's limiting. And what you're experiencing is that limitation. You're asking yourself, "How do I return more than one thing to the view?" The answer is to create a custom view model for that view. Right now your view is binding to an enumeration of Concert objects. Which is fine, if that's all it needs. But it turns out that's not all it needs. It has some custom logic that requires a little more. So, you create a custom view model. Something like this:
public class ConcertsViewModel
{
public IEnumerable<Concert> Concerts { get; set; }
// other properties, methods, anything
}
Then in your controller action, you return one of those instead:
public ActionResult Index()
{
using(var db = new Models.MelliConcertEntities())
{
var concertsModel = new ConcertsModel();
concertsModel.Concerts = (from x in db.Concert
orderby x.ID ascending
select x).Take(15).ToList();
return View(concertsModel);
}
}
(Note also the use of the using statement, which should always be employed when making use of IDisposable resources.)
So now your view is still getting the list of Concert objects, but it's packaged in a custom view model onto which you can add any more functionality you need for that view. Next, in your view, change the model declaration:
#model MelliConcert.Models.ConcertsViewModel
(This assumes you put it in the Models namespace. Depending on the scale of your application, you might want to break out view models into their own namespace. I don't often use the Models namespace in the actual application for core business objects, so our projects are likely structured very differently. This should get you going, but you'll want to make sure you keep your concerns cleanly separated.)
Then in the view code you can reference what you need from that object. So if you need to access the list, instead of just calling something like #foreach (var item in model) you would call #foreach (var item in model.Concerts).
I think in this case the argument would be to do the maximum amount of processing on your model before returning it to the view.
So why not return distinct showtimes to the view and then just loop through them?
The only problem with your current set up is it may undermine the spirit of MVC which was to separate concerns. The view shouldn't be applying any logic that needs to be tested (or as little as possible). By keeping logic in the models and controllers you make unit testing easier and the views are simpler for a developer to read.
EDIT
Hey #samangholami, you can return multiple objects to a view using a class. Create a class called "PAGENAMEViewModel" or something similar and create a property for every value you want to return. For example:
public class MovieViewModel
{
public string MovieName { get; set; }
public IEnumerable<string> Actors { get; set; }
public IEnumerable<ShowTimeRecord> Showtimes { get; set; }
public class ShowTimeRecord
{
public string TheaterName { get; set; }
public string TheaterAddress { get; set; }
public DateTime ShowtimeDate{ get; set; }
}
}
Then return your complex model like so:
public ActionResult Index()
{
MovieViewModel model = myMovieHelper.GetMovieData();
return View(model);
}
Besides the possible separation of concerns problem you also might have a performance problem that can be more difficult to diagnose.
If your LINQ query is somehow connected to the database via some ORM or something like that by looping through the results you might create a N+1 problematic scenario.
Moving it off the view might not prevent it, but at least puts it into somewhere that is more visible.
I believe that's because it's not respecting the separation of concerns principle, which is a fundamental concept of MVC. By performing that query in the view, you're taking business logic to it.
It has nothing to do with Linq itself. You could, for example, have a Linq statement to perform an OrderBy. That's ok because it's not business logic, it's a proper view operation (to order data into a table, let's say).
I am trying to use an EditorTemplate to display a child collection in a table in the parent’s view. The problem I have run into is that this only seems to work if the template is named exactly the same as child’s class. When I attempt to use a template with a slightly different name, and pass that name as the templateName argument to EditorFor,I get a runtime error. I was hoping I could use different child EditorTemplates for different purposes with the same child collection.
Here is an abbreviated example:
Models:
public class Customer
{
int id { get; set; }
public string name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int id { get; set; }
public DateTime orderdate { get; set; }
public decimal amount { get; set; }
public Customer customer { get; set; }
}
Customer controller Index() method:
public ActionResult Index()
{
Customer customer = new Customer() {id = 1, name = "Acme Corp.", Orders = new List<Order>()};
customer.Orders.Add(new Order() {id = 1, orderdate = DateTime.Now, amount = 100M});
customer.Orders.Add(new Order() { id = 2, orderdate = DateTime.Now, amount = 200M });
return View(customer);
}
Customer Index.cshtml view:
#model TemplateTest.Customer
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Customer</title>
</head>
<body>
<div>
#Html.EditorFor(Model=>Model.name)
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Order Date</th>
<th>Amount</th>
</tr>
</thead>
#Html.EditorFor(Model=>Model.Orders)
</table>
</div>
</body>
</html>
Order.cshmtl template in Views/Shared/EditorTemplates (added “color” to verify I am using this template):
#model TemplateTest.Order
<tr>
<td>#Html.DisplayFor(Model=>Model.id)</td>
<td style="color:blue">#Html.EditorFor(Model=>Model.orderdate)</td>
<td>#Html.EditorFor(Model=>Model.amount)</td>
</tr>
This works fine. But if I rename the EditorTemplate to “OrderList.cshtml” and change the child EditorFor line to
#Html.EditorFor(Model=>Model.Orders, "OrderList")
when I run it again I get this exception:
“The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[TemplateTest.Order]', but this dictionary requires a model item of type 'TemplateTest.Order'.”
Any idea why the EditorFor doesn’t use the template “OrderList” I specified in the “templateName" argument? Otherwise, what is that argument for?
TL;DR > Named templates don't work with collections, use a foreach loop to work around it - See below for extensive details about why, and an example.
You said:
Any idea why the EditorFor doesn’t use the template “OrderList” I
specified in the “templateName" argument? Otherwise, what is that
argument for?
EditorFor is actually using the the template OrderList that you specified -- but you've stumbled on something that very confusing. Some research turned up a lot of hints but I found the real nuts-and-bolts details in this post: Problem with MVC EditorFor named template
In short, what is happening is that the default case which works:#Html.EditorFor(Model=>Model.Orders) is actually calling an MVC default template in the interim by convention, but this is not obvious at all.
Try thinking of it this way:
In the working version you are passing in a type List<Order> with the reference to Model.Orders (MANY orders) but the template is specified with a model of Order (single, NOT MANY).
Interesting. Why does that even work? At first glance it seems like it should not work. But it does work because of what happens behind the scenes.
Paraphrased from above mentioned post:
When you use #Html.EditorFor(c => c.Orders) MVC convention chooses
the default template for IEnumerable. This template is part of the MVC framework, and what it does is generate Html.EditorFor() for
each item in the enumeration. That template then generates the
appropriate editor template for each item in the list individually
- in your case they're all instances of Order, so, the Order template is used for each item.
That's the magic, and it is handy, but because this happens by convention and is basically hidden from us, it is the source of the confusion in my opinion.
Now when you try to do the same thing but using a named template by explicitly setting your EditorFor to use a particular editor template OrderList, you end up with that editor template being passed the whole enumeration -- and this is the source of the error you posted.
In other words the failing case manages to skip over the 'magic' part of the working case and that it is why it fails. But, semantically it looks good and sound, right? There's the confusion.
Working case:
your call default MVC template your template
#Html.EditorFor( Model => Model.Orders) IEnumerable template Order template
Failing case:
your call your template
#Html.EditorFor(Model=>Model.Orders, "OrderList") OrderList template ERROR!!!
There's a number of ways to make the error go away, but many of them are problematic because they cause the HTML controls be rendered in a way that prevents you from being able to address the individual controls by index on POST. Uhhg. (Note: the working case does render the HTML correctly as expected)
To get the HTML controls rendered properly, it seems that you must use a regular for loop (not a foreach) and pass each of the individual Order objects to the custom template (which I've called OrderEditorTemplateDefault).
#for (int i = 0; i < Model.Orders.Count ; i++)
{
#Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateDefault")
}
Part of your question indicated:
I was hoping I could use different child EditorTemplates for different
purposes with the same child collection.
You could do that by introducing a condition inside the loop and choosing the alternate template there (either for the entire list or on an Order-by-Order basis, just depends on how you write the condition)
#for (int i = 0; i < Model.Orders.Count ; i++) {
if (someCondition) {
#Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateDefault")
} else {
#Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateALTERNATE")
}
}
Sorry so verbose. Hope that helps.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I am trying to use ASP.NET MVC without EF and possibly without LINQ. To start with I created just one table/model/controller/view on basic ASP.net mvc template in VS2010(Just index page and Index method is implemented). I am currently going with try/error as all tutorials on the web are based on EF and I have an embedded c background.
My questions are:
Is the architecture correct?
I saw repository pattern is used in some open source projects such as nearforums. What's its advantage and how does it fit into this, does it replace DAL?
Should I pass datatables to view or objects representing the datatable?
Can I pass more than one model to razor page? How can I list 2 different tables for example?
Is it better to put all DAL code in one file?
DAL:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Configuration;
using MySql.Data.MySqlClient;
namespace MvcApplication1.Models
{
public class DAL
{
public DAL()
{ }
public DataTable getDataTable(string tableName)
{
using (MySqlConnection conn = new MySqlConnection(ConfigurationManager.ConnectionStrings["MySQLConn"].ConnectionString))
{
using (MySqlCommand command = conn.CreateCommand())
{
command.CommandText = "select * from " + tableName;
command.CommandType = CommandType.Text;
conn.Open();
var table = new DataTable();
table.Load(command.ExecuteReader());
return table;
}
}
}
}
}
Product.cs class that represents Product table in the code:
namespace MvcApplication1.Models
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Producer { get; set; }
public int UnitPrice { get; set; }
}
}
ProductController.cs
namespace MvcApplication1.Controllers
{
public class ProductController : Controller
{
//
// GET: /Product/
DAL dal = new DAL();
public ActionResult Index()
{
DataTable dt = dal.getDataTable("Product");
Product[] products = new Product[dt.Rows.Count];
for (int i = 0; i < dt.Rows.Count; i++)
{
products[i] = new Product();
products[i].ProductId = (int)dt.Rows[i]["ProductId"];
products[i].Name = (string)dt.Rows[i]["Name"];
products[i].Producer = (string)dt.Rows[i]["Producer"];
products[i].UnitPrice = Decimal.ToInt32((decimal)dt.Rows[i]["UnitPrice"]);
}
return View(products);
}
........
........
}
Index.cshtml:
#model IEnumerable<MvcApplication1.Models.Product>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProductId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Producer)
</td>
<td>
#Html.DisplayFor(modelItem => item.UnitPrice)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ProductId }) |
#Html.ActionLink("Details", "Details", new { id=item.ProductId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ProductId })
</td>
<br>
</tr>
}
I saw repository pattern is used in some open source projects such as nearforums. What's its advantage and how does it fit into this, does it replace DAL?
The benefits of the repository pattern are that it allows you to switch out your data access logic/ORM. For example, if you wanted to switch from Entity Framework to NHibernate, you just update your registrations inside you IoC container and like magic your code is using NHibernate with 0 knowledge of you ever making the switch. It allows your code to be agnostic of the underlying persistence framework. For all it knows, results code be coming from a file, web service call, or an in memory list. You'll also get reuse in the event that you had to make the same query from another component. Another gain is unit testing. With your current implementation, you cannot unit test your controller as it's going to connect directly to your database every time, thus by definition making it an integration test. You will certainly want to leverage an ORM, or you'll be writing the same code over and over again until it makes you cry. Setting up/tearing down connections, type casting all your primitives to native .NET types, etc. Dealing with native ADO.NET is a thing of the past nowadays. Make your life easier and use an ORM. At the same time, with great power comes great responsibility. ORMs can be a nightmare if you don't validate the queries that are being generated and executed against your database. If not used properly, they'll generate performance problems. You'll be able to leverage LINQ for most of your queries, but sometimes you'll have to get dirty funky native and go with raw SQL. You ORM will still be able to handle both scenarios.
Should I pass datatables to view or objects representing the datatable?
You certainly don't want to pass datatables. You want to pass view models or entities to your view. The view should know nothing about the database. All you do is give it data, and it displays in on the screen not ever knowing the source of that data. You want to be careful with passing entities directly to your view if you're using something like NHibernate, because you run into problems associated with lazy loading and N + 1 queries. View models are typically slimmed down versions of your entity. For example, if you had an entity with 4 properties but the view only needed to consume 2/4 of those properties, you'd make a separate view model with 2 properties and use a mapping library like Automapper to map from your entity to view model.
Can I pass more than one model to razor page? How can I list 2 different tables for example?
This is quite simple. You make a top level object and give it 2 properties to represent the entities you want to access.
public class MyViewModel
{
public Entity1 FirstObject { get; set; }
public Entity2 SecondObject { get; set; }
}
Then you'd make a strongly typed view and render FirstObject and SecondObject using the strongly typed view helpers you used in your razor view posted in your question. #Html.TextBoxFor(), etc.
Lastly, your controller would accept MyViewModel as an argument and populate FirstObject and SecondObject based on the form inputs rendered in your view.
Is it better to put all DAL code in one file?
You want to put related DAL code in the same file. Things are typically mapped out by table but that all depends on the relationships between your objects (what's your aggregate root for example). What you don't want to do is blindly implement one repository per table. You'll just end up with a really procedural and anemic data model that results in stitching together object graphs and relationships via multiple calls to the database. I'd recommend looking into Domain Driven Design to get a good idea on how to tackle this problem.
I've just scratched the surface here. You'll basically need to pick up the latest patterns and practices if you want to do this stuff properly. It's a different movement all together. Not like the good old days of monolithic code behind files, view state, and DataGrid controls. There are some really good books out there like:
Brownfield Application Development
Domain Driven Design (Eric Evans)
Clean Coder (Robert C Martin)
Refactoring: Improving the Design of Existing Code (Martin Fowler)
Patterns of Enterprise Application Architecture (Martin Fowler)
I'm not saying read all these books verbatim, but I'd strongly recommend the first 2. I'm sure the community will have even more to say about these development practices, but this is my perspective on it all. Thanks and good luck.