I have simple view model, which is populating itself but the problem is that a webpage contains a lot of select lists and for every one select list I am calling database procedure for getting data list.
Is it possible, for performance, to execute database calls asynchronous or in parallel?
I have this kind of code :
// controller
public ActionResult Index()
{
model = new SampleViewModel();
model.Populate(database);
return View(model);
}
// view model
public class SampleViewModel
{
public SampleViewModel(DbContext db)
{
_list1 = context.Db.SqlQuery<SelectList1>("SELECT Id, Value FROM dbo.Table1").ToList();
_list2 = context.Db.SqlQuery<SelectList2>("SELECT Id, Value FROM dbo.Table2").ToList();
_list3 = context.Db.SqlQuery<SelectList3>("SELECT Id, Value FROM dbo.Table3").ToList();
_list4 = context.Db.SqlQuery<SelectList4>("SELECT Id, Value FROM dbo.Table4").ToList();
_list5 = context.Db.SqlQuery<SelectList5>("SELECT Id, Value FROM dbo.Table5").ToList();
}
private readonly List<SelectList1> _list1;
public int SelectedList1Id { get; set; }
public IEnumerable<SelectListItem> List1 { get { return new SelectList(_list1, "Id", "Value");} }
-//- _list2
-//- _list3
-//- _list4
-//- _list5
}
As you can see, _list3 is waiting for _list2 and _list2 is waiting for _list1 and this can slow request a lot. The reason why view model is populating itself is because in real scenario these select lists are related to each other and the model contains information about selected Ids and with these Ids I can rebuild the select lists for example if model validation failed.
Any idea? Can I use some async await approach and will it help me in this case against SQL Server 2008 ?
You may use Task Parallel Libraries Paralle.Invoke method to execute many tasks in parallel.
Parallel.Invoke(() =>{
// Execute some code here
}, () =>
{
// Execute some other code here
});
I personally do not pass a concrete DbContext object to my view model. View models should be simple POCO. It should not have any knowledge of your data access technology. So my personal preference is keeping the data access code seperate from my view model. So i never read values from database in a view model constructor with a concrete object like you did.
Assuming you have a simply POCO view model like this
public class CreateViewModel
{
public List<SelectListItem> States {set;get;}
public List<SelectListItem> UserTypes {set;get;}
}
In your GET action, you can use Parallel.Invoke to load the 2 properties data.
var vm = new CreateViewModel();
Parallel.Invoke(() =>{
vm.States = db.States.Select(s=>new SelectListItem { Value=s.Id.ToString(),
Text=s.Name }).ToList();
}, () =>
{
vm.UserTypes= db.UserTypes.Select(s=>new SelectListItem { Value=s.Id.ToString(),
Text=s.Name }).ToList();
});
return View(vm);
Caching
If these are frequently accessed items for your dropdown, I suggest you cache this data instead of querying the db table every time. You may consider using the default MemoryCache.
Related
I posted the question earlier, but didn't receive any correct responses, hence posting again with some edits. I have a function that accepts two parameters, IDs and Dates. When I had put breakpoints, I was able to see the Ids and the Dates selected on the page as parameter values. However, after hitting the process button, nothing happens, meaning this data isn't getting saved to the DB.
Model Classes:
public class Hello{
public string ID{ get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? Date{ get; set; }
}
Controller Class:
[HttpGet]
public ActionResult Selection(string ids, string dates)
{
model = new Hello();
ExtensionDB db = new ExtensionDB();
string[] IDS = ids.Split(',');
string[] DATES = dates.Split(',');
List<Hello> list = new List<Hello>();
for (int i = 0; i < IDS.Length; i++)
{
if (IDS[i] != null && IDS[i] != "")
{
Hello item = new Hello { ID = IDS[i], Date = DateTime.Parse(DATES[i]) };
list.Add(item);
}
}
if (ModelState.IsValid)
{
foreach (var row in db.Table1)
{
foreach (var row2 in db.Table2)
{
if (row.UID== row2.CID) // UID and CID are Foreign keys that join these two tables
{
foreach (var item in list)
{
if (row.UID == Convert.ToInt32(item.ID))
{
row2.ReportedDate = item.Date;
}
db.SaveChanges();
}
}
}
}
ViewBag.Message = "Success";
return View(model);
}
else
{
ViewBag.Message = "Failed";
return View(model);
}
}
I will add the view class if needed, however the problem is here.. You can also refer to it here: Saving changes to the DB MVC
Your code does not attempt to update anything. Start with confirming what the data you are passing to this POST call contains, and what you want to do with it. It looks like what you are trying to do is update the dates for a number of records. Looking at your previous post (no need to re-post another question with the same code) there are a few things..
First: Structure the data you want to pass to the POST call into a collection of simple objects containing an id and a date. For example:
{
id = rid,
date = date
}
and add those to the collection named something like "updateData" rather than two separate arrays of IDs and dates. Then in the server-side code, declare a simple view model class:
public class UpdateDateViewModel
{
public int Id { get; set; }
public DateTime Date { get; set; }
}
In the ajax call instead of:
data: { ids: ids, dates: dates },
you'll want something like:
data: { updates: updateData },
where updateData is your collection of id + date pairs.
and use that view model in your method:
public ActionResult Process(IList updates)
Provided that request data is sent as Json, ASP.Net should translate that data automatically for you, though you may need to configure ASP.Net to translate the camelCase vs PascalCase. Worst case, to test, you can use camelCase property names ("id" and "date")
Now when it comes to updating the data: Server side, please get in the habit of using meaningful variable names, not "c", "i", etc. It makes code a lot easier to understand.
public ActionResult Process(IList<UpdateDateViewModel> updates)
{
using (db = new DB())
{
//rp = new RequestProcess(); - Assuming RequestProcess is an Entity?
//var c = rp.getStuff(); - No idea what this getStuff() method does...
foreach(var update in updates)
{
var request = db.RequestProcesses.Find(update.Id);
if (request != null)
request.RequestDate = update.Date; // If we find a matching request, update it's date.
else
{ // Doesn't exist, create it and add it to the DbSet.(table)
var request = new RequestProcess { Id = update.Id, RequestDate = update.Date };
db.RequestProcesses.Add(request);
}
db.SaveChanges();
}
}
}
Now this is a very bare bones guess at what you may be trying to do. Ideally though, updates should be completely separate from adds in the sense that an update should only deal with existing records. If it comes across an ID that it cannot find it should throw an error, ignore, and/or return a status to the user that something wasn't right. Creating new entries should be a separate call and ensure that records are properly initialized with their required fields.
Your original code looked to be taking a list of IDs, but then creating a new entity and calling that "getStuff" method that didn't have the DbContext, or any of the values from the POST call, but then attempting to copy values from that entity into the string parameters that you passed (which would overwrite the Json string) None of that would have updated an entity which would never have updated your data.
Take it slow and follow the examples before attempting to adapt them to your ideas. It will be a lot more constructive and less frustrating then writing a bunch of code that doesn't really make much sense, then wondering why it doesn't work. Your original code has probably a dozen or more problems and inefficiencies. Simply pasting it up on Stack will get a lot of confusing comments based on these problems which don't really help with the first issue you want to solve. Strip it back to the minimum, start with getting the data you need to the server in a meaningful way, then from that, attempt to use that data to update your entities.
This question already has answers here:
Where to convert business model to view model?
(3 answers)
Closed 5 years ago.
I'm using MVC 4 with the repository pattern and unit testing also. I have a typical controller that has simple CRUD functionality. I've separated my View Models from my DTOs and I would like to know the best way to convert between the 2:
Models:
I have Admin.Models.Product which is my view model and AdminAssembly.Models.Product which is my DTO.
Controller:
//repo that handles product operations
AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> db;
//default constructor
public ProductController() { db = new AdminAssembly.Repositories.EntityRepo<AdminAssembly.Models.Product>(new AdminAssembly.Models.EntitiesContext()); }
//unit testing constructor
public ProductController(AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> context) { db = context; }
//
// POST: /Product/Create
[HttpPost]
public ActionResult Create(Admin.Models.Product product) {
if (ModelState.IsValid) {
//COMPILE-ERROR: how to convert to DTO?
db.Add(product);
}
return View();
}
//
// GET: /Product/Edit/5
public ActionResult Edit(int id) {
//COMPILE-ERROR: how to convert to view model?
Admin.Models.Product product = db.GetAll().Where(p => p.ID == id);
return View(product);
}
How do I convert between the 2?
Do I reference my DTO assembly in my view model and do something like: (won't this break my unit testing?)
//convert to AdminAssembly.Models.Product
db.Add(product.ToDTO());
//convert back to Admin.Models.Product via constructor
Admin.Models.Product product = Admin.Models.new Product(db.GetAll().Where(p => p.ID == id));
Do I need some sort of object conversion black box?
Converter.ToViewProduct(product);
Some sort of interface?
or something else?
Update 1:
public static class Product {
public static Admin.Models.Product ToView(AdminAssembly.Models.Product dto) {
Admin.Models.Product viewProduct = new Admin.Models.Product();
//straight copy
viewProduct.Property1 = dto.Property1;
viewProduct.Property2 = dto.Property2;
return viewProduct;
}
public static AdminAssembly.Models.Product ToDTO(Admin.Models.Product viewModel) {
AdminAssembly.Models.Product dtoProduct = new AdminAssembly.Models.Product();
//straight copy
dtoProduct.Property1 = viewModel.Property1;
dtoProduct.Property2 = viewModel.Property2;
//perhaps a bit of wizza-majig
dtoProduct.Property1 = viewModel.Property1 + viewModel.Property2;
return dtoProduct;
}
}
The long-hand response
[HttpPost]
public ActionResult Create(Admin.Models.Product product)
{
if (ModelState.IsValid)
{
//COMPILE-ERROR: how to convert to DTO?
var dtoProduct = new AdminAssembly.Models.Product();
dtoProduct.Property1 = product.Property1;
dtoProduct.Property2 = product.Property2;
//...and so on
db.Add(dtoProduct);
}
return View();
}
While this looks verbose and tedious (and it is) it has to happen eventually, somewhere.
You can hide this mapping either in another class or extension method, or you can use a third party like AutoMapper, as Charlino points out.
As a side note, having two classes with the same name in two different namespaces will eventually get confusing (if not for you, then for the next person who has to maintain your code.) Implement friendlier and more descriptive names wherever possible. For example, put all your view models in a folder called ViewModels, not Models. And append all your view models with ViewModel, or VM. It's also a good convention, imo, to name your view models based on the view that they are for, not so much the domain model that they will be mapped to, as not all view models will map directly to a domain model. Sometimes you'll want parts of more than one domain model, for a single view, and that will blow up your naming convention.
So in this particular case I would suggest changing Admin.Models to Admin.ViewModels and then rename the view model version of Product to CreateViewModel. Your code will be much more readable and will not be littered with namespaces throughout your methods.
All of that would result in a method that would look more like this:
[HttpPost]
public ActionResult Create(CreateViewModel viewModel)
{
if (ModelState.IsValid)
{
var product = new Product();
product.Property1 = viewModel.Property1;
product.Property2 = viewModel.Property2;
//...and so on
db.Add(product);
}
return View();
}
Check out a library called AutoMapper.
From their wiki:
What is AutoMapper?
AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. This type of code is rather dreary and boring to write, so why not invent a tool to do it for us?
If you dont want to use AutoMapper you may use extensions, as suggested by #Forty-Two. If the number of things to map is no very great, I would go with this approach, just because then, AutoMapper == YAGNI
public static class Extensions
{
public static ViewModel ToViewModel(this Model )
{
var vm = new ViewModel()
{
//map
};
return vm;
}
public static Model ToModel(this ViewModel viewModel)
{
var model = new Model()
{
//map
};
return model;
}
}
Similar to your code in UPDATE, but using extensions instead.
I have a Controller which returns a ViewModel to a View and it works just fine. I want to migrate to a Repository pattern but am having trouble getting the correct syntax in the repository. I have created the repository and the interface to it.
public interface IShippingRepository
{
IQueryable<ShippingCommHdr> All { get; }
IQueryable<ShippingCommHdr> AllIncluding(params Expression<Func<ShippingCommHdr, object>>[] includeProperties);
void InsertOrUpdate(ShippingCommHdr shippingcommhdr);
void Delete(int id);
void Save();
}
Here is the code form my Controller that I want to move to the repository:
public ViewResult ShippingSummary()
{
CPLinkEntities context = new CPLinkEntities();
var shipments =
from h in context.ShippingCommHdrs
where (h.CompletedDate == null)
join
e in context.vHr_Employees on h.CreatedBy equals e.ID
join
s in context.Shippers on h.ShipperID equals s.ShipperID
join
r in context.vAaiomsSites on h.ShipToSiteID equals r.SiteID
join
c in context.vHr_Employees on h.CreatedBy equals c.ID
join
p in context.vHr_Employees on h.FromSitePOC equals p.ID
select new
{
h.ID,
ShippedToSite = r.SiteName,
h.DateShipped,
h.EstDeliveryDate,
h.TrackingNo,
h.HeaderComments,
h.ShippingCommLI.Count,
s.Shipper,
CreatedBy = c.LastName,
FromSitePoc = p.LastName
};
var model = new List<ShippingSummaryVM>();
foreach (var h in shipments)
{
var viewModel = new ShippingSummaryVM
{
ID = h.ID,
ShippedToSite = h.ShippedToSite,
DateShipped = h.DateShipped,
EstDeliveryDate = h.EstDeliveryDate,
TrackingNo = h.TrackingNo,
FromSitePOC = h.FromSitePoc,
Shipper = h.Shipper,
HeaderComments = h.HeaderComments,
NumOrders = h.Count,
CreatedBy = h.CreatedBy,
};
model.Add(viewModel);
}
return View(model);
}
If I could get this one Controller/Repository to work, I can then migrate all the others over fairly quickly. thanks for any assistance
I'd start by adding a method definition to the repository interface for the query you need to execute. The repository can give this query a meaningful name:
public interface IShippingRepository
{
IQueryable<Shipment> GetShipments()
// ...
}
In the controller you'll need an instance of the repository. You can inject it into a constructor, or create one in a constructor, but either way the repository will need to talk to the CPLinkEntities context behind the scenes. You'll need to pass a context into the repository for the repository to work with.
public class SomeController : Controller
{
IShippingRepository _shippingRepository;
public SomeController()
{
_shippingRepository = new ShippingRepository(new CPLinkEntities());
}
public ViewResult ShippingSummary()
{
var shipments = _shippingRepository.GetShipments();
// ....
}
}
A concrete repository definition might look like the following.
public class ShippingRepository : IShippingRepository
{
CPLinkEntities _entities;
ShippingRepository (CPLinkEntities entities)
{
_entites = entities;
}
public IQueryable<Shipment> GetShipments()
{
return from ship in _entities.Ships join ... join ... select
}
}
Your controller method basically has 2 responsibilities
Run a Query
Map the results of the query into a view model
You can put that query into a repository, and then you could use an auto-mapper tool like AutoMapper or ValueInjecter to help you map the results of your query to a view model.
Your resulting controller method would simply call the repository to get a list of CPLinkEntities. Your controller method could then take those entities and then call the automapper to give you a list of ShippingSummaryVM's. I've left some implementation details, but this should give you a high level understanding of how to achieve what you are asking.
Option A: Have a stronger domain model. Your repository would responsible for loading root level domain objects and you let the underlying OR/M handle object traversal. Your controller would call a method on shipment to find shipments that are not yet completed. You'd get back a shipment object and could traverse to the related entities to get site name and other details you need for your VM
Option B: Have repositories that return all for each entity, and then do the join in a business or service layer. Entity Framework won't load all even if you say ShippingRepository.All. It only loads at the last responsible moment (when you need a materialized result). So you could have a business method that joins the "All" on each entity and filters based on completed date then returns back the result.
Option A is better but might be a lot more work.
There is a field in our database which really ought to be a boolean, but for some reason the original developers made it a CHAR which will either be set to "1" or "0".
[Column("CHARGEABLE")]
[StringLength(1)]
private string Chargeable { get; set; }
I want my model to represent this field as a boolean so I figured I could add a property to my model to wrap it:
[NotMapped]
public bool ChargeableTrue
{
get
{
return Chargeable == "1" ? true : false;
}
set
{
Chargeable = value ? "1" : "0";
}
}
Now on my View I just display the EditorFor ( ChargeableTrue ), but when I click save it doesn't actually update it.
I think what is happening is that when the model is being updated, it's still attempting to get the value of 'Chargeable' from the View, even though I haven't displayed it there. And since there is no input field, it just gets null and ends up saving that to the database.
if (ModelState.IsValid)
{
db.Entry(call).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
What is one expected to do in this situation?
Based on KMan's answer, here's the extended version just in case you're not familiar with creating view models.
The idea is that your domain object is not really what you want to be updating exactly from your views. Instead, you create a go-between that can also include view-specific items (like a list of objects to populate a drop-down).
public class MyViewModel {
public bool Chargeable { get; set; }
}
Now you can do this:
#* In view *#
Html.EditorFor(m => m.Chargeable)
// In controller
public ActionResult Save(MyViewModel model) {
if (ModelState.IsValid) {
var domainObject = new MyObject() {
Chargeable = model.Chargeable ? "1" : "0"
};
// the rest of your code using domainObject
}
}
I'd consider just creating an overload of your domain object's constructor that accepts your view model to keep the mapping in one place. I typically use a tool like AutoMapper to map objects or manual extension methods.
A view model typically contains a sub-set of your domain object's properties, but can contain all of them or more properties like lists, visbility states, etc. They come in incredibly useful and I've never done a MVC project where I haven't used them.
Use a view model and make your mapping on the controller.
Hy Guys.
I have 2 entities: "Interactions" and "Orders", which has many "Interactions".
On View, I wanna a different class (like <tr class=#cssClass>) for the Orders which has interactions unread. So I created a ViewModel.
My ViewModel:
public class OrderViewModel
{
public Order Order{get;set;}
public bool isRead{get;set;}
}
My controller (I'm working with repositories):
var orders = orderRepo.All();
var viewModel = orders.Select(order=> new orderViewModel
{
Order = order, '<------------'
isRead = order.Interactions.Any(x => x.Read == true),
});
return View(viewModel);
If I take out the Order = order, It works fine. But with this code I'm getting this error:
QueryException was unhandled by user code. Could not instantiate: Models.OrderViewModel
InnerException: When casting from a number, the value must be a number less than infinity
InnerException: Make sure the source type is convertible to the destination type.
If, instead of use the objects I use their properties, it works fine:
ViewModel:
public int IdPedido { get; set; }
public string CategoryOrder { get; set; }
public string ModuleOrder { get; set; }
Controller:
var viewModel = orders.Select( order => new OrderViewModel{
CategoryOrder=Order.Category,
ModuleOrder=Order.Module,
...});
What am I missing? I'm not understanding what should be convertible. Or isn't possible to use Objects on ViewModel, only Properties?
If it's a usable information, I'm using the Nhibernate ORM.
Tks guys!
EDIT
Based on #elspiko question, I realized that Order and Orders didn't have the same type. So I set orders: IEnumerable<Order> orders = pedido.Repo.All(); And now it's working.
I thought it would work before editing. Why was it considered different types?
Your code doesn't look right, it should be:
var viewModel = orders.Select(order=>new OrderViewModel
{
Order = order, '<------------'
But i'm assuming thats just a typo?
It might have something to do with lazy loading, if you put a debugger on the select statement, what type does order have? Is it some form of proxy object?