I'm following an MVC 5 tutorial: http://www.c-sharpcorner.com/article/dashboard-application-with-asp-net-mvc-5-and-jquery/
but the Author left out 1 of the features - the middle panel - "Orders" (which is the orders for all customers). It has a View Details link but no code and partial view is displayed when clicked.
So I'm trying to do create that partial view but having trouble with writing the Linq To Entities.
I am trying for just a partial view that is like a header/multi detail:
CustomerName CustomerImage
OrderDate
Quantity ProductType, ProductName, ProductImage
OrderDate
Quantity ProductType, ProductName, ProductImage
CustomerName CustomerImage
OrderDate
Quantity ProductType, ProductName, ProductImage
Here is the ViewModels I created to represent the above:
public class OrderDetailsViewModel
{
public int Quantity { get; set; }
public string ProductType { get; set; }
public string ProductName { get; set; }
public string ProductImage { get; set; }
}
public class CustomerOrdersViewModel
{
public string CustomerName { get; set; }
public string CustomerImage { get; set; }
public DateTime OrderDate { get; set; }
public ICollection<OrderDetailsViewModel> OrderDetailsViewModel{ get;
set; }
}
Here is the DbContext and the models that the Author created:
DbContext:
public class DashboardContext : DbContext
{
// Constructor - inherits the base constructor.
public DashboardContext() : base("DashboardOrder")
{
}
public IDbSet<Customer> CustomerSet { get; set; }
public IDbSet<Order> OrderSet { get; set; }
public IDbSet<Product> ProductSet { get; set; }
public IDbSet<OrderDetails> OrderDetailSet { get; set; }
}
Customer:
public class Customer : IEntity
{
public Customer()
{
Orders = new List<Order>();
}
public int ID { get; set; }
public string CustomerName { get; set; }
public string CustomerEmail { get; set; }
public string CustomerPhone { get; set; }
public string CustomerCountry { get; set; }
public string CustomerImage { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
IEntity:
public interface IEntity
{
int ID { get; set; }
}
Order:
public class Order : IEntity
{
public Order()
{
OrderDetail = new List<OrderDetails>();
}
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<OrderDetails> OrderDetail { get; set; }
}
OrderDetails:
public class OrderDetails : IEntity
{
public int ID { get; set; }
public int Quatity { get; set; }
public virtual Order Order { get; set; }
public virtual Product Product { get; set; }
}
Product:
public class Product : IEntity
{
public Product()
{
OrderDetails = new List<OrderDetails>();
}
// Auto-implemented properties.
public int ID { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public int UnitsInStock { get; set; }
public string ProductImage { get; set; }
public string ProductType { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
}
Here is the Action Method in the DashboardController I was attempting to write.
I believe I want to read the Order Model which in turn has reference to the Customer and a list of Orders which in turn has a reference to the list of OrderDetail which has reference to the Product.
But I'm having a hard time with the "Linq to SQL" to get the data in the lists in the models to create my ViewModel to flatten it out and pass to my partial view as a list.
public ActionResult GetCustomerOrdersDetails()
{
List<CustomerOrdersViewModel> customerOrders = null;
using (DashboardContext _context = new DashboardContext())
{
// Using LINQ TO SQL and deferred execution via the "ToList".
customerOrders = (from o in _context.OrderSet
select new CustomerOrdersViewModel
{
CustomerName = o.Customer.CustomerName,
CustomerImage = o.Customer.CustomerImage,
OrderDate = o.OrderDate,
-- Here I need to process the list of Orders which in turn has a reference to the list of OrderDetail which has
reference to the Product.
ProductType = ?,
ProductName = ?,
ProductImage = ?,
Quantity = ?,
}).ToList();
}
return PartialView("~/Views/Dashboard/GetCustomerOrdersDetails.cshtml", customerOrders);
}
You first need a .GroupBy() clause to group the records by CustomerName, CustomerImage and OrderDate. Then because OrderDetail is a collection proeprty, you need a .SelectMany() to 'flatten that collection before projecting the result to your OrderDetailsViewModel model.
List<CustomerOrdersViewModel> model = db.OrderSet
.GroupBy(x => new { Name = x.Customer.CustomerName, Image = x.Customer.CustomerImage, Date = x.Date })
.Select(x => new CustomerOrdersViewModel
{
CustomerName = x.Key.Name,
CustomerImage = x.Key.Image,
OrderDate = x.Key.Date,
OrderDetailsViewModel = x.SelectMany(y => y.OrderDetail).Select(y => new OrderDetailsViewModel
{
ProductName = y.Product.ProductName,
Quantity = y.Quantity,
ProductType = y.Product.ProductType,
ProductImage = y.Product.ProductImage
}).ToList()
}).ToList();
return PartialView(model);
Then in the view you can use nested loops to display the details of each order
#model IEnumerable<CustomerOrdersViewModel>
....
#foreach(var order in Model)
{
.... // display details of customer name, date etc
#order.CustomerName
foreach(var item in order.OrderDetailsViewModel)
{
.... // display details of product, quantity etc for each order
#item.ProductName
Related
I want to retrieve all players from db for one club where they are in M:M relationship using PlayerClubs join table. My code is working but really doesn't want that approach for example, first I am retrieving all players from db
var players = await _context.PlayerClubs.Where(pc => pc.ClubId == id).Select(p => p.Player).ToListAsync();
then I retrieve the club based on id which I receive from controller
var club = await _context.Clubs.Where(z => z.Id == id).FirstOrDefaultAsync();
and lastly populate ClubViewModel with this data
return new ClubViewModel()
{
Players = players,
Club = club,
};
Now I want to populate this new ClubViewModel with just one db call i.e. one query using linq expression.
Things that I have tried
var query = (from c in _context.Clubs
join pc in _context.PlayerClubs on c.Id equals pc.ClubId
join player in _context.Players on pc.PlayerId equals player.Id
where c.Id == id
select new ClubViewModel
{
Players = player,
Club = c,
}).ToListAsync();
but I got stuck.
PlayerClubs table
Club
public class Club
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[EnumDataType(typeof(Gender))]
public Gender GenderType { get; set; }
public int SeasonId { get; set; }
public virtual Season Season { get; set; }
[Required]
public string YearOfEstablishment { get; set; }
[Required]
public string YearOfEntryIntoLeague { get; set; }
public string Note { get; set; }
[ForeignKey("League")]
public int LeagueId { get; set; }
public virtual League League { get; set; }
public virtual ICollection<PlayerClub> PlayerClubs { get; set; }
public virtual ICollection<CoachClub> CoachClubs { get; set; }
}
Player
public class Player
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime Birth { get; set; }
public int LicenseNumber { get; set; }
public string Note { get; set; }
public virtual List<string> Clubs { get; set; }
public virtual List<Club> Klubovi { get; set; }
public virtual List<string> ClubNames { get; set; }
[StringLength(13, ErrorMessage = "Матичниот број не може да биде подолг од 13 цифри")]
public string Embg { get; set; }
public virtual ICollection<PlayerClub> PlayerClubs { get; set; }
public Player()
{
Clubs = new List<string>();
ClubNames = new List<string>();
Klubovi = new List<Club>();
}
}
PlayerClub
public class PlayerClub
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Club")]
public int ClubId { get; set; }
[ForeignKey("Player")]
public int PlayerId { get; set; }
public virtual Club Club { get; set; }
public virtual Player Player { get; set; }
}
ClubViewModel
public class ClubViewModel : Club
{
public Club Club { get; set; }
public List<Player> Players { get; set; }
public ClubViewModel()
{
Players = new List<Player>();
}
}
Your query:
var query =
from c in _context.Clubs
where c.Id == id
select new ClubViewModel
{
Players = c.PlayerClubs.Select(pc => pc.Player).ToList(),
Club = c,
};
var result = await query.ToListAsync();
My entities:
public class Product : Base.BaseEntity
{
public Product()
{
this.Colors = new HashSet<Color>();
}
[StringLength(50)]
public string ProductName { get; set; }
public int CategoryID { get; set; }
[StringLength(100)]
public string StockFinishes { get; set; }
[StringLength(50)]
public string Guarantee { get; set; }
[StringLength(50)]
public string Installation { get; set; }
public virtual ProductEntity.Category OwnerCategory { get; set; }
public virtual IList<VariationEntity.Variation> Variations { get; set; }
public virtual ICollection<ProductEntity.Color> Colors { get; set; }
}
public class Color : Base.BaseEntity
{
public Color()
{
this.Products = new HashSet<Product>();
}
[StringLength(50)]
public string ColorName { get; set; }
public virtual ICollection<ProductEntity.Product> Products { get; set; }
}
My controller:
[HttpPost]
public ActionResult NewProduct(Models.DTO.ProductDTO.ProductVM productmodel)
{
if (ModelState.IsValid)
{
DATA.Models.ORM.Entity.ProductEntity.Product productentity = new DATA.Models.ORM.Entity.ProductEntity.Product();
productentity.ProductName = productmodel.ProductName;
productentity.CreatedBy = User.UserId;
productentity.CategoryID = productmodel.CategoryID;
productentity.StockFinishes = productmodel.StockFinishes;
productentity.Guarantee = productmodel.Guarantee;
productentity.Installation = productmodel.Installation;
rpproduct.Insert(productentity);
rpproduct.SaveChanges();
if (productmodel.SelectedColors != null)
{
foreach (var colorId in productmodel.SelectedColors)
{
DATA.Models.ORM.Entity.ProductEntity.Color color = rpcolor.FirstOrDefault(x => x.Id == colorId);
productentity.Colors.Add(color);
}
db.SaveChanges();
}
return RedirectToAction("ProductList");
}
else
{
ViewBag.Error = "An error occurred while adding a new product";
return View();
}
}
There is no error, but product colors are not inserted into the database. I can't do it with repository.
How can I add product colors with repository or without repository?
Sorry for bad English :(
I prefer to manage many to many relations myself not Entity Framework.
In my view you need another table to store colors of products with columns ColorId and ProductId. Then there should be a DbSet on your DbContext.
After that you can save a new entity ProductColors which stores ColorId and ProductId. Your entities Color and Product can have a reference to this table, not to Color and Product table.
public class ProductColor : Base.BaseEntity
{
public ProductColor()
{
}
public int ColorId { get; set; }
public virtual Color Color { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
I am trying to insert data into a sql server temporary table based on a value picked from a dropdownlist. In my model I have 4 classes:
public class Customer
{
[Key]
public int CustID { get; set; }
public string CustName { get; set; }
public virtual ICollection<Sale> Sales { get; set; }
}
}
public class Product
{
[Key]
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Sale> Sales { get; set; }
}
}
public class Sale
{
[Key]
public int RecordID { get; set; }
public int ProductID { get; set; }
public int CustID { get; set; }
public int InvoiceNo { get; set; }
public int Quantity { get; set; }
public virtual Customer Customer { get; set; }
public virtual Product Product { get; set; }
}
public class TempTable
{
[Key]
public int recordID { get; set; }
public string CustName { get; set; }
public string PoductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public int InvoiceNo { get; set; }
}
In my SaleController I have the following action:
public ActionResult OldInvoice(Sale sale)
{
ViewBag.InvoiceNo = new SelectList(db.Sales, "RecordID", "InvoiceNo", sale.RecordID);
return View();
}
[HttpPost]
public ActionResult OldInvoice(TempTable t, int InvoiceNoToSearch = 0)
{
var query = (from s in db.Sales
join c in db.Customers on s.CustID equals c.CustID
join p in db.Products on s.ProductID equals p.ProductID
where s.InvoiceNo.Equals(s.InvoiceNo == InvoiceNoToSearch)
select new
{
s.InvoiceNo,
s.Quantity,
c.CustName,
p.ProductName,
p.Price
}).ToList();
foreach (var temp in query)
{
t.CustName = temp.CustName;
t.InvoiceNo = temp.InvoiceNo;
t.PoductName = temp.ProductName;
t.Price = temp.Price;
t.Quantity = temp.Quantity;
db.TempTables.Add(t);
db.SaveChanges();
}
return RedirectToAction("OldInvoice");
}
and finally my view is:
#model IEnumerable<UsingDropDownListValueInQuery.Models.Sale>
#{
ViewBag.Title = "OldInvoice";
}
<h2>OldInvoice</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.DropDownList("InvoiceNo")
<p>
<input type="submit" value="Save" />
</p>
}
I get the following error:
Unable to cast the type 'System.Boolean' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
If I enter in the Where Statement an Existing InvoiceNo it does the job but not if I try to pick the value from the dropdownlist
The problem seems to be that you're never actually sending the value back. In your view, you've created a select named "InvoiceNo", but in your post action, you don't have any parameters that can accept that value. The closest and probably the one you intend to bind to is InvoiceNoToSearch, but if that's the variable you want to use, your view needs to match:
#Html.DropDownList("InvoiceNoToSearch")
Of course, that means your SelectList in ViewBag no longer binds automatically as the options for that select, but that can be fixed by just explicitly specifying it:
#Html.DropDownList("InvoiceNoToSearch", (SelectList)ViewBag.InvoiceNo)
I'm displaying the data from a table called gigs, however it contains a couple of foreign keys to tables 'Bands' and 'Venues' so when using this code in my controller,
string user = User.Identity.GetUserId();
var yourgigs = (from g in dbg.gigs
from v in dbg.Venues
from b in dbg.Bands
from ga in g.gigsaccasses
where (ga.Id == user &&
v.venueid == g.venueid &&
b.bandid == g.bandid)
select g);
return View(yourgigs);
it's displaying bandid and venueid in the view which are meaningless integers. How would I replace those with what I suppose would be b.bandname, v.venuename and also add v.address1 and v.city? The SQL statement that does this is
SELECT bands.bandname, venues.venuename, venues.address1, venues.city, gigs.whatdate, gigs.starttime
FROM gigs INNER JOIN
bands ON gigs.bandid = bands.bandid INNER JOIN
gigsaccass ON gigs.gigid = gigsaccass.gigid INNER JOIN
dbo.AspNetUsers ON gigsaccass.Id = dbo.AspNetUsers.Id INNER JOIN
venues ON gigs.venueid = venues.venueid
WHERE dbo.AspNetUsers.Id = //some user//
I did try using anonymous types as such:
var yourgigs = (from g in dbg.gigs
from v in dbg.Venues
from b in dbg.Bands
from ga in g.gigsaccasses
where (ga.Id == user &&
v.venueid == g.venueid &&
b.bandid == g.bandid
select new
{
bandname = b.bandname,
venuename = v.venuename,
address1 = v.address1,
city = v.city,
whatdate = g.whatdate,
starttime = g.starttime
});
But this then threw an error:
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType76[System.String,System.String,System.String,System.String,System.DateTime,System.TimeSpan]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[OnStageTonight_MVC.Models2.gigs]'.
The View is expecting type 'gigs'
#model IEnumerable<OnStageTonight_MVC.Models2.gigs>
#{
ViewBag.Title = "Gigs";
}
<h2>Gigs</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.venueid)
</th>
<th>
#Html.DisplayNameFor(model => model.bandid)
</th>
What am I missing?
EDIT:
I should add that I do have a model, but I'm assuming this is what is at fault.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace OnStageTonight_MVC.Models2
{
[Table("gigs")]
public partial class gigs
{
public gigs()
{
this.gigsaccasses = new HashSet<gigsaccass>();
}
[Key]
public int gigid { get; set; }
public int venueid { get; set; }
public int bandid { get; set; }
[Display(Name="Date")]
public System.DateTime whatdate { get; set; }
[Display(Name="Starts at")]
public System.TimeSpan starttime { get; set; }
public virtual ICollection<gigsaccass> gigsaccasses { get; set; }
}
[Table("gigsaccass")]
public partial class gigsaccass
{
[Key]
public int gigaccassid { get; set; }
public int gigid { get; set; }
public string Id { get; set; }
public virtual gigs gig { get; set; }
public virtual AspNetUsers AspNetUser { get; set; }
}
[Table("dbo.AspNetUsers")]
public class AspNetUsers
{
[Key]
public string Id { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public Nullable<System.DateTime> LockoutEndDateUtc { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public string UserName { get; set; }
public string YourName { get; set; }
public List<gigsaccass> gigsaccasses { get; set; }
}
[Table("venues")]
public partial class venues
{
[Key]
public int venueid { get; set; }
[Required]
[Display(Name = "Venue")]
public string venuename { get; set; }
[Required]
[Display(Name = "Address")]
public string address1 { get; set; }
[Required]
[Display(Name = "City")]
public string city { get; set; }
public List<gigs> venuegigs { get; set; }
}
[Table("bands")]
public class bands
{
[Key]
public int bandid { get; set; }
[Required]
[Display(Name = "Name")]
public string bandname { get; set; }
public List<gigs> bandgigs { get; set; }
}
public partial class gigscontext : DbContext
{
public gigscontext()
: base("DefaultConnection")
{
}
public DbSet<gigs> gigs { get; set; }
public DbSet<gigsaccass> gigsaccass { get; set; }
public DbSet<AspNetUsers> AspNetUsers { get; set; }
public DbSet<venues> Venues { get; set; }
public DbSet<bands> Bands { get; set; }
}
}
You can't use anonymous classes here. Your view needs to know how to work with model, it needs type information, but: "The type name is generated by the compiler and is not available at the source code level"
You can't pass such objects around. http://www.codeproject.com/Articles/15624/Inside-C-Anonymous-Methods#5
You need to create class that represent row in your data set and return list of populated objects.
As less preferred alternative you can use dynamic: https://msdn.microsoft.com/en-us/library/dd264736.aspx
The M in MVC stands for model, and what you want is one of the 3 main tenants of MVC.
You want an object that encapsulates all the information to be displayed on the view.
It is considered best practice to create a model for the view. This is an additional layer which separates the storage of the item (your entity model) from it's presentation.
var yourgigs = (from g in dbg.gigs
from v in dbg.Venues
from b in dbg.Bands
from ga in g.gigsaccasses
where (ga.Id == user &&
v.venueid == g.venueid &&
b.bandid == g.bandid
select new GigViewModel
{
bandname = b.bandname,
venuename = v.venuename,
address1 = v.address1,
city = v.city,
whatdate = g.whatdate,
starttime = g.starttime
});
public class GigViewModel
{
public string bandname { get; set; }
public string venuename { get; set; }
public string address1 { get; set; }
public string city { get; set; }
public DateTime whatdate { get; set; }
public Timespan starttime { get; set; }
}
When persisting data from your view models, use AutoMapper or something similar to copy properties with matching names between your view models and your entity models.
Use the new view model in your view:
#model IEnumerable<OnStageTonight_MVC.Models2.GigModelView>
I'm using Code First and LINQ to SQL in an ASP.NET MVC4 project. In the below query I'm trying to populate PatientView.Appointments.ScheduledBy, but it's returning null. I've tried adding .Include("Appointments.ScheduledBy"), but Appointments.ScheduledBy continues to return null.
How can I modify the LINQ to SQL expression to get ScheduledBy populated?
Here's my LINQ to SQL (Id is the action's parameter)
var q = from p in context.Patients.Include("Appointments.ScheduledBy")
where p.Id == Id
select new PatientView
{
Patient = p,
Appointments = p.Appointments.OrderByDescending(a => a.ScheduledFor)
};
PatientView pv = q.Single();
PatientView is the view model for the view. The Appointments property does get populated, but the Appointments' ScheduledBy property is null.
public class PatientView
{
public Patient Patient { get; set; }
public IEnumerable<Appointment> Appointments { get; set; }
}
public class Patient
{
public int Id { get; set; }
public string Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Appointment> Appointments { get; set; }
}
public class Appointment
{
public int Id { get; set; }
public Patient Patient { get; set; }
public Employee ScheduledBy { get; set; }
public DateTime ScheduledFor { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}