Trouble loading a child model in MVC 4 - asp.net-mvc-4

I have an OrderViewModel that includes an instance of a child _DetailsViewModel. The OrderViewModel has order header information and the _DetailsViewModel holds order detail information. Despite being separate Models, both have the same single data source..the Orders table. The reason that the details are in their own View Model is that those fields are reused on a different View, in the same visual arrangement, so I'm putting them in a Partial View to be reused as needed. Here is an idea of my main and child View Models:
public class OrderViewModel
{
public string OrderNum { get; set; }
public string CustonerName{ get; set; }
public double SalesPrice{ get; set; }
public _Details Details { get; set; }
}
public class _DetailsViewModel
{
public string PhoneNum { get; set; }
public string ItemNum { get; set; }
public double Quantity { get; set; }
public string PayMethod{ get; set; }
public string Comments { get; set; }
}
Within my controller I call a service that returns all data from the Orders table and returns a List of Order Entities.
orderService = new OrderService();
var orders = orderService.GetOrderInfo(StoreNum);
From there I use Omu.ValueInjecter to inject the results into the main View Model.
var orderViewModel = orders
.Select(x => new
OrderViewModel().InjectFrom(x)).Cast<OrderViewModel>()
.ToList();
return View(orderViewModel);
I need to also populate the _Details model so that I can pass it to the Partial View from within my main Order View...like below:
#Html.Partial("_OrderDetails", Model._Details)
Is there a way to populate the _Details Model from the single service call that is already populating the main Order Model? Will I have to add the _Details properties to the main Order View and then iterate the Order view to set each field of the corresponding _Details Model manually? Surely I'm missing something.
Thanks...

Move the entities out of your database first, in that manner you only issue one query request:
// apply any filter(s) needed here.
var orderList = orders.ToList();
// then do injecting using the "cached" orders
var orderViewModel = orderList
.Select(x => new OrderViewModel().InjectFrom(x))
.Cast<OrderViewModel>()
.ToList();
// then inject into your details model
var detailsModel = orderList
.Select(x => new _DetailsViewModel().InjectFrom(x))
.Cast<_DetailsViewModel>()
.ToList();
And a small suggestion if I may, remove the underscore for _DetailsViewModel to make the naming standard.
UPDATE:
How do I add the detailsModel to the orderViewModel afterwards to pass
to the Order View?
You just simply set it to the instance of OrderViewModel like so:
orderViewModel.Details = detailsModel ;
Then return orderViewModel to your view and do your thing there:
#Html.Partial("_OrderDetails", Model.Details)

Related

ASP.NET MVC Cannot Implicitly Convert Type System.Collections.Generic.List<string> to 'string'

I'm trying to use EF to retrieve data from SQL and put it into a list of type viewmodel.
But when I try and retrieve the data it gives the following error: Cannot Implicitly Convert Type System.Collections.Generic.List<.string> to 'string'
I'm only trying to retrieve the ID and ProductName columns from the database.
Any help would be appreciated.
Here is my ViewModel:
public class ProductViewModel
{
public int ID { get; set; }
public string Product { get; set; }
}
Here is my controller code:
public ActionResult TableReport()
{
List<ProductViewModel> products = new List<ProductViewModel>();
ProductViewModel product = new ProductViewModel();
product.Product = db.Products.Select(z => z.ProductName).ToList();
product.ID = db.Products.Select(z => z.ProductID).ToList();
return View();
}
#GSerg: Read your code out loud. "Create an empty list of ProductViewModels. Create an empty ProductViewModel. Assign the list of all product names as the product name for that single empty ProductViewModel. Assign the list of all product ids as the id for that single empty ProductViewModel". No wonder it is not working, is it? Instead you should have done var products = db.Products.Select(z => new ProductViewModel() { ID = z.ProductID, Product = z.ProductName}).ToList();
You are putting a list in a string
Change your Viewmodel to
public class ProductViewModel
{
public int ID { get; set; }
public List<string> Product { get; set; }
}
And you are also trying to put a list in an int. That won't work either.

Fixed JSON reference loop errors not I'm getting too much data

I added this line to resolve circular errors in my JSON:
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
But now, I find that some of my controllers return too much information.
I found this similiar question, but reading through it, I couldn't really figure out how to apply it to my code:
Avoiding Circular referencing providing too much data
For example, this simple controller returns data that I don't want:
[HttpGet("{id}")]
public async Task<ActionResult<BookList>> GetBookList(string id)
{
var bookList = await _context.BookList.FindAsync(id);
return bookList;
}
Here is the model for that data:
public partial class BookList
{
public BookList()
{
BookLinks = new HashSet<BookLinks>();
}
public string BookId { get; set; }
public Guid LibraryId { get; set; }
public string BookTitle { get; set; }
public string BookText { get; set; }
public byte? BookType { get; set; }
public virtual LibraryList Library { get; set; }
public virtual ICollection<BookLinks> BookLinks { get; set; }
}
}
So when I hit the controller above, I am getting all the unwanted data for BookLinks in addition to the data from BookList.
I just want the data from BookList based on a particular BookId.
I was under the impression, that if I wanted all data returned(including the BookLinks data), I'd have to do something like this:
var bookList = await _context.BookList
.Include(i => i.BookLinks)
.Where(b => b.BookId == id)
.ToListAsync();
That said, is there way to limit or exclude data that I don't want?
Thanks!
Your navigation props are virtual, so I'm assuming you have lazy-loading enabled. When the serializer walks the object, it will trigger the get for each of these properties, which will in turn issue queries one by one to backfill the data, which will then get serialized as well. Assuming that it doesn't encounter a circular reference, it will continue to walk down the related entities, loading and serializing each relationship.
This is a perfect illustration of why you should never serialize entities. Entities are for working with the database. They are not for and should not be used for returning responses, rendering views, etc.
Instead, create a view model/DTO/whatever you want to call it. Map your entity class(es) on to that, and then return the view model instead. That way, you can control exactly what the response is.

Posting DropDownList value from View to Controller in MVC4

I have a MVC4 application and although I have get parameters for my DropDownList from the database, I encounter some kind of problems while posting the DropDownList value to the database. There is lots of samples for different approach, but I would like to apply a method without using an extra approach i.e. Ajax, Javascript, etc. On the other hand, I have run into "FormCollection" to pass data, but I am not sure if FormCollection is the best way in this scene. Here are some part of the view, controller and model I use:
View:
#using (Html.BeginForm("Add", "Product", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<p>Product Type : #Html.DropDownListFor(m => m.SelectedLookupId, new SelectList(Model.Lookups.Where(x => x.LookupType == "Product Type"), "LookupID", "LookupValue"), "--- Select ---") </p>
Controller:
[HttpPost]
public ActionResult Add(Product product)
{
if (ModelState.IsValid)
{
product.ProductType = // ??? Cannot get the SelectedLookupId
...
repository.SaveProduct (product);
TempData["message"] = string.Format("{0} has been saved", product.Name);
return View("Completed");
}
else
{
//there is something wrong with the data values
return View(product);
}
}
ViewModel:
public class ProductViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Lookup> Lookups { get; set; } //Lookup for Product Types
public int SelectedLookupId { get; set; }
public Product Product { get; set; }
}
Thanks in advance for your helps.
Your action method should be receiving the view model, not the Product itself, like so:
[HttpPost]
public ActionResult Add(ProductViewModel productViewModel)
Unless I'm confused. But I assume the view snippet you posted above is from the Add view and that view's model is of type ProductViewModel. In your action method you are returning the Add view when the model state is invalid however you are passing a Product to that view. Again I may be confused because this should give you a runtime error that the types don't match.
Thanks for reply. Actually by using ViewModel rather than View, I have managed to solve the problem. On the other hand, after some research, I have applied another effective method in order to populate Dropdownlist without needing ViewModel. Furthermore with this example, I could use multiple foreign keys on the same Lookup table as shown below. Here is an an Applicant entity having 3 foreign keys and Lookup entity related to these keys. What I wanted to achieve with this example is exactly to use a Lookup table for only several Dropdownlist parameters i.e. Gender, Yes/No, Status,... due to no needing to create a table for the several parameters (these parameters are distinguished LookupType property on Lookup table). Here is the full example (I have shorted unrelated properties for brevity) below:
Applicant Entity:
public class Applicant
{
[Key]
public int ApplicantID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public int? HasDoneAnyProject { get; set; }
public int? IsInterestedAnyProgramme { get; set; }
public int? InterestedProgrammeId { get; set; }
public virtual Lookup PrimaryLookup { get; set; }
public virtual Lookup SecondaryLookup { get; set; }
public virtual Lookup TertiaryLookup { get; set; }
}
Lookup Entity:
public class Lookup
{
[Key]
public int LookupID { get; set; }
public string LookupType { get; set; }
public string LookupValue { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public virtual ICollection<Applicant> PrimaryLookupFor { get; set; }
public virtual ICollection<Applicant> SecondaryLookupFor { get; set; }
public virtual ICollection<Applicant> TertiaryLookupFor { get; set; }
}
DbContext:
public class EFDbContext : DbContext
{
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Lookup> Lookups { get; set; }
//for using "Multiple foreign keys within same table using Fluent API"
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.PrimaryLookup)
.WithMany(a => a.PrimaryLookupFor)
.HasForeignKey(b => b.HasDoneAnyProject)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.SecondaryLookup)
.WithMany(a => a.SecondaryLookupFor)
.HasForeignKey(b => b.IsInterestedAnyProgramme)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.TertiaryLookup)
.WithMany(a => a.TertiaryLookupFor)
.HasForeignKey(b => b.InterestedProgrammeId)
.WillCascadeOnDelete(false);
}
}
Controller:
private void PopulateLookupsDropDownList(string lookupType, string foreignKey, object selectedLookups = null)
{
var lookupsQuery = repository.Lookups
.Select(x => x)
.Where(x => x.LookupType == lookupType)
.OrderBy(x => x.ParentLookupID).ToList();
ViewData[foreignKey] = new SelectList(lookupsQuery, "LookupID", "LookupValue", selectedLookups);
}
and for calling the Method for each of three Dropdownlist:
PopulateLookupsDropDownList("YesNo", "HasDoneAnyProject", applicant.HasDoneAnyProject);
PopulateLookupsDropDownList("YesNo", "IsInterestedAnyProgramme", applicant.IsInterestedAnyProgramme);
PopulateLookupsDropDownList("Programme", "InterestedProgrammeId", applicant.InterestedProgrammeId);
View: : Populating each of three Dropdownlist from the same Lookup table with different LookupType parameter:
<label>Has done any project before?</label>
#Html.DropDownList("HasDoneAnyProject", "---- Select ----")
<label>Are you interested in any programme?</label>
#Html.DropDownList("IsInterestedAnyProgramme", "---- Select ----")
<label>Interested programme name?</label>
#Html.DropDownList("InterestedProgrammeId", "---- Select ----")
I hope this approach will be useful for those who want to populate Dropdownlists from the same Lookup table. On the other hand, it is not only suitable for this, also can be used for populating Dropdownlists from different tables.
Regards.

Enumerable objects in ViewBag in MVC4

I have the below model say F:
public partial class F
{
[Key, Display(Name = "Id")]
public int FId { get; set; }
public int RId { get; set; }
public int FTId { get; set; }
public string C { get; set; }
public string U { get; set; }
public string D { get; set; }
[ScaffoldColumn(false)]
public System.DateTimeOffset Created { get; set; }
}
In the controller I have to read all the records of 'F' from database and assign those to an enumerable list of records.
For ex:
ViewBag.Cs = enumerable C column items (textbox)
ViewBag.Us= enumerable U column items (textbox)
ViewBag.FTIDs = enumerable FTId column items (this has to be a dropdown)
In my I have to show
#Html.Textbox(Cs);
#Html.Dropdown(FTIDs);
I gave only textbox and dropdows as an example, there could be many other controls like datetimes, checkboxes etc.,
I should be able to written each column as list a in viewbag and show it in MVC View.
Can somebody advise if this achievable and how?
Many thanks...
Don't use a viewbag for something like this - instead strongly bind your view to a model. Only use a view bag if you have something really small to pass.. anything complex you should always use a strongly typed view model, you get intellisence and its must cleaner for unit testing
View Model:
Public class MyViewModel
{
public List<F> MyListOfFObjects { get; set; }
}
Now when you create your view you can bind it to this view model in the popup or if you don't want to recreate it simply add a reference to it at the top of your view like so:
#model <your project>.<folder view model is in>.<view model name>
for example
#model AdventureWorks.Models.EmployeeViewModel.
In your controller you simply create this view model and pass it to your view such as:
public ActionResult Index()
{
MyViewModel vm = new MyViewModel();
// Initialize your view model
// Get all the F objects from the database and populate the list
return View(vm); // now your view will have the view model
}
Now in the view you can iterate through this view model
#foreach(var fObject in Model)
{
#Html.TextBoxFor(m => m.fId)
#Html.TextBoxFor(m => m.rID)
}
Here is a link to a list of the different #Html helpers that you can use btw
http://qkview.com/techbrij/aspnet-mvc-4
Reference for strongly binded views:
http://www.asp.net/mvc/tutorials/views/dynamic-v-strongly-typed-views

Nested LINQ IQueryable and WCF WebApi

I have a method like this that works as expected.
[WebGet]
public IQueryable<BuildJobModel> GetCustomers()
{
var context = new MyDataContext(); // ADO.NET Entity Data Model
var query = from c in context.Customers
select new CustomerModel {
Id = c.Id,
Name = c.Name
};
return query;
}
But when I try to create a more complex query like this, it doesn't work.
[WebGet]
public IQueryable<BuildJobModel> GetCustomers()
{
var context = new MyDataContext(); // ADO.NET Entity Data Model
var query = from c in context.Customers
select new CustomerModel {
CustomerId = c.CustomerId,
Name = c.Name,
Orders = from o in c.Orders
select new OrderModel {
OrderId = o.OrderId,
Details = o.Details
}
};
return query;
}
The Models look like this:
public class CustomerModel
{
public int CustomerId { get; set; }
public string Name { get; set; }
public IEnumerable<OrderModel> Orders { get; set; }
}
public class OrderModel
{
public int OrderId { get; set; }
public string Details { get; set; }
}
The Exception:
Cannot serialize member Proj.CustomerModel.Logs of type System.Collections.Generic.IEnumerable`1[[Proj.OrderModel, Proj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
My Goals:
I want to be able to expose an IQueryable interface.
I want to return nested data.
I want to use my own Models, not the ado.net entities
I want to hit the database with as few queries as possible (one would be best).
I would strongly recommend not exposing IQueryable as a return type of a WCF service.
WCF was designed to return data not queries
You lose control over the nature of the query: someone might potentially use this query in a way that is resource-intensive
Ideally, if you want to return collections use array or a generic list.
With respect to your list of goals:
Can you explain this? I don't see what IQueryable interface has to do with nested data.
You can still return arrays or lists of your models
You have better control over performance if you execute query locally and return results instead
Update: Have a look at WCF Data Services - that might be able to do what you want.
In case you're trying to return JSON: the build in JsonFormatter is not able to (de)serialize interfaces. You should try the JSON.NET Formatter from WebApiContrib.
I think you just need to use an array rather than IEnumerable, like so...
public class CustomerModel
{
public int CustomerId { get; set; }
public string Name { get; set; }
public OrderModel[] Orders { get; set; }
}
public class OrderModel
{
public int OrderId { get; set; }
public string Details { get; set; }
}
If you're serializing this to json/xml I suspect the built-in serializers don't know what to do with IEnumerable.