What is the right syntax for this joined EF Linq query - asp.net-mvc

I am trying to get a query that returns everything properly formatted for my ViewModel so I do not have to manually copy everything over from my entity models. I have this Linq query that is giving me an error. :
var query = from i in context.Invoices
join l in context.LineItems on i.InvoiceID equals l.InvoiceID into il
where i.InvoiceID == id
select new InvoiceViewModel()
{
InvoiceID = i.InvoiceID,
CustomerID = i.CustomerID,
InvoiceNote = i.Note,
InvoiceDate = i.InvoiceDate,
Terms = i.Terms,
LineItems = il.ToList<LineItemViewModel>()
};
This is my ViewModel
public class InvoiceViewModel {
public int InvoiceID { get; set; }
public int CustomerID { get; set; }
public string InvoiceNote { get; set; }
public DateTime InvoiceDate { get; set; }
public string Terms { get; set; }
public virtual ICollection<LineItemViewModel> LineItems { get; set; }
}
public class LineItemViewModel {
public int LineItemID { get; set; }
public int InvoiceID { get; set; }
public int Quantity { get; set; }
public string Item { get; set; }
public decimal Amount { get; set; }
public string LineItemNote { get; set; }
}
The error I am getting is (the red squigly is under the il in LineItems = il.ToList())
'IEnumerable<LineItem>' does not contain a definition for 'ToList' and the best extension method overload 'Enumerable.ToList<LineItemViewModel>(IEnumerable<LineItemViewModel>)' requires a receiver of type 'IEnumerable<LineItemViewModel>'
Which I (sorta, kinda, a little) understand. So what is the proper syntax for this?

You need to explicitly initialize your LineItemViewModel instances from the LineItem entities. You might be better off writing this as a correlated subquery rather than a join:
var query =
from i in context.Invoices
where i.InvoiceID == id
select new InvoiceViewModel()
{
InvoiceID = i.InvoiceID,
CustomerID = i.CustomerID,
InvoiceNote = i.Note,
InvoiceDate = i.InvoiceDate,
Terms = i.Terms,
LineItems =
(
from li in context.LineItems
where li.InvoiceID == i.InvoiceID
select new LineItemViewModel
{
LineItemID = li.LineItemID,
InvoiceID = li.InvoiceID,
Quantity = li.Quantity,
Item = li.Item,
Amount = li.Amount,
LineItemNote = li.LineItemNote,
}
).ToList()
};

Related

How to select from order model where order details parameter ? asp.net mvc

I have Order model And OrderDetails model like that:
public class Order
{
public int ID { get; set; }
public int ClientID { get; set; }
public int ShippingID { get; set; }
public int SharepointID { get; set; }
public int CallStatuID { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
// public int OrderStatusID { get; set; }
public decimal ShippingCost { get; set; }
public string purchasetype { get; set; }
public string Notes { get; set; }
public decimal Total { get; set; }
public virtual List<OrderDetail> orderdetails { get; set; }
public virtual Client Client { get; set; }
public virtual Shipping shipping { get; set; }
public virtual Sharepoint sharepoint { get; set; }
public virtual CallStatu callStatu { get; set; }
}
and
public class OrderDetail
{
public int ID { get; set; }
public int OrderID { get; set; }
public int ItemID { get; set; }
public int SizeID { get; set; }
public decimal Price { get; set; }
public int orderStatuID { get; set; }
public int Quantity { get; set; }
public decimal Total { get; set; }
public virtual Order order { get; set; }
public virtual Item item { get; set; }
public virtual Size size { get; set; }
public virtual OrderStatu orderStatu { get; set; }
}
i want to select Client name, Governorate, Total of the Order and Items count
So i wrote this code:
public JsonResult ShareSearchDetails(SharepointANVM ANVM)
{
var SHR = from O in db.Orders
where O.SharepointID == ANVM.Id
join OD in db.OrderDetails
on O.ID equals OD.OrderID
where OD.orderStatuID == ANVM.NameOfStutes
select new
{
ClientName = O.Client.RealName,
Governorate = O.Client.Region.RegionName,
Total = O.Total,
Items = O.orderdetails.Count(),
Orderstatu = OD.orderStatu.NameOfStutes
};
return Json(SHR.ToList(), JsonRequestBehavior.AllowGet);
}
the problem is that the result is right, but its ordering by OrderDetails model.
that's the code get all rows from sub model (orderdetails) but i want it come ordering by Main model (Orders)
the result:The wrong result
but the correct Result should be like that :
The correct result
Please help
You could use Distinct to get not duplicated rows, like the following code:
var SHR = (from O in db.Orders
join OD in db.OrderDetails on O.ID equals OD.OrderID
where O.SharepointID == ANVM.Id && OD.orderStatuID == ANVM.NameOfStutes
select new
{
ClientName = O.Client.RealName,
Governorate = O.Client.Region.RegionName,
Total = O.Total,
Items = O.orderdetails.Count(),
Orderstatu = OD.orderStatu.NameOfStutes
})
.Distinct()
.ToList();
Note that, you don't need to use where twice.
I hope you find this helpful.
You can use grouping in your linq just like in SQL :
var SHR = from O in db.Orders
join OD in db.OrderDetails on O.ID equals OD.OrderID
where OD.orderStatuID == ANVM.NameOfStutes && O.SharepointID == ANVM.Id
group new { O, OD } by new {
ClientName = O.Client.RealName,
Governorate = O.Client.Region.RegionName,
Total= O.Total,
Orderstatu = OD.orderStatu.NameOfStutes
} into grp
select new
{
grp.ClientName,
grp.Governorate,
grp.Total,
grp.Orderstatu,
Items = grp.Count()
};

Linq filtering entity's collection ends up in LINQ to Entities casting error

ASPNET MVC5 web app
In getting Products, whose relation with other entities is described by following models, I need to filter only those Products.Category.CategoryTrans whose language_id equals the culture param.
Please note I need to pass the result as an IQueryable to subsequent paging and sorting methods, already implemented.
Models:
public partial class Product
{
public int? ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsDeleted { get; set; }
public bool IsApproved { get; set; }
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
[NotMapped]
public virtual CategoryTrans CategoryTrans { get; set; }
}
public partial class Category
{
public int ID { get; set; }
public string Name { get; set; }
public int? ParentID { get; set; }
public bool IsDeleted { get; set; }
public virtual ICollection<Product> Products { get; set; }
public virtual ICollection<CategoryTrans> CategoryTrans { get; set; }
}
public class ISO_Languages
{
public int ID { get; set; }
public string code { get; set; }
public bool IsEnabled { get; set; }
public string name_en { get; set; }
public string name_fr { get; set; }
public string name_it { get; set; }
public string name_de { get; set; }
public string name_es { get; set; }
}
public class CategoryTrans
{
[Key, Column(Order = 1)]
public int category_id { get; set; }
[Key, Column(Order = 2)]
public int language_id { get; set; }
[ForeignKey("category_id")]
public virtual Category categoryId { get; set; }
[ForeignKey("language_id")]
public virtual ISO_Languages languageId { get; set; }
public string name { get; set; }
}
Following query returns ALL CategoryTrans in p.Category.CategoryTrans, that means any category translation
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db, string userid, string culture)
{
var query = (from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture
select p);
return query;
}
What I am trying to do is filtering, for every product, the single category translation, depending on culture input parameter.
Something like:
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db, string userid, string culture)
{
var query = from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture
select new Product
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = p.Category.CategoryTrans.Where(b => b.language_id.Equals(l.ID)).SingleOrDefault()
};
return query;
}
but getting error:
The entity or complex type 'xyz.DAL.Product' cannot be constructed in a LINQ to Entities query.
Looking for this specific error I now tried projecting onto a DTO:
public class ProductDTO
{
public int? ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual CategoryTrans CategoryTrans { get; set; }
}
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db, string userid, string culture)
{
var query = from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture
select new ProductDTO
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = p.Category.CategoryTrans.Where(b => b.language_id.Equals(l.ID)).FirstOrDefault()
};
return query.Cast<Product>();
}
And that is now working as expected, returning only required translation to CategoryTrans.
So the query works now BUT casting ProductDTO to needed Product returns:
Unable to cast the type 'xyz.Controllers.ProductDTO' to type 'xyz.Models.Product'. LINQ to Entities only supports casting EDM primitive or enumeration types.
I cannot find a solution to this exception.
Even if you are able to cast ProductDTO to Product model, these objects will not be tracked automatically by EF.
One possible solution may be to first select the Products, then iterate over them to assign the required property.
In your first (failed) query, I believe teh select can be replaced with :
select new Product
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = ct
};
which may work.
Otherwise, you cannot Cast<> from one object type to another unless they have a base/derived relationship. Otherwise, you'll need to convert them:
in Product DTO, add:
replace :
return query.Cast<Product>();
with:
return query.Select(p=> new Product
{
ID = p.ID,
Name = p.Name,
Description = p.Description,
CategoryTrans = p.CategoryTrans
};
UPDATE:
Ok, let's try something different. Drop the select from the query, and just use the p object:
public static IQueryable<Product> ActiveProductsPerUser(BaseContext db,
string userid, string culture)
{
var query = from p in db.Products
join ct in db.CategoryTrans
on p.CategoryID equals ct.category_id
join l in db.ISO_Languages
on ct.language_id equals l.ID
where l.code.Substring(0, 2) == culture;
return query;
}
that will leave the CategoryTrans property null, but it will get you through this and the other manipulations of the query. When you get to the end, and are actually doing things with the result, then pull out the p.Category.CategoryTrans.Where(b => b.language_id.Equals(l.ID)).FirstOrDefault() to get the CategoryTrans
I could not find an answer to the
The entity or complex type 'xyz.DAL.Product' cannot be constructed in a LINQ to Entities query.
error. I eventually solved the problem by adding a
[NotMapped]
public virtual string LocalizedCategoryName { get; set; }
to Product model Class, in charge for displaying localized category name, and moving the filtering to the ViewModel, setting two nested foreach loops, returning a new List of fully localized products:
private List<Product> _LocalizedProductList = new List<Product>();
public List<Product> LocalizedProductList
{
get
{
HttpUtilities HttpHelper = new HttpUtilities();
string culture = HttpHelper.getFullCulture();
int IsoCode = GenericUtilities.getIsoID(culture, db);
List<Product> localized = new List<Product>();
foreach (Product p in _LocalizedProductList)
{
foreach (CategoryTrans c in p.Category.CategoryTrans)
{
if (c.language_id.Equals(IsoCode))
{
Product x = new Product
{
ID = p.ID,
LocalizedCategoryName = c.name,
DateCreated = p.DateCreated,
DateExpire = p.DateExpire,
DateLastModified = p.DateLastModified,
Name = p.Name,
Description = p.Description,
IsApproved = p.IsApproved,
IsDeleted = p.IsDeleted,
ProductImages = p.ProductImages,
User = p.User
};
localized.Add(x);
};
}
}
return localized;
}
set { _LocalizedProductList = value; }
}
No idea if it's the best or the only way to do it, but works as intended. I can now stay with the simple query and pass the returned IQueryable of Product onto the sorting and paging async method or whatever. Whenever I am done, the result is being assigned to ViewModel.LocalizedProductList, where the getter takes care of the final filtering. Thanks to wonderbell for the suggestion that one possible solution could be to first select the Products, then iterate over them to assign the required property. That's why upvoted his/her post even it cannot be considered as a complete (or even partial) solution to the question.

2 models with one to many relationship - How to list the count of items in the first model

I had a tough time coming up with a good title. I am working on an MVC application using asp.net and Entity Framework. I have 2 models, Categories and Products. There can be many products in each category. I have a page that lists out each category and each product with the associated fields for each. I want to add a column(field) to the Category list that lists how many products are in that category. I have no idea how to go about doing this. I am guessing I will need to add an additional variable to the Categories model called "productCount" or something like that.
Category model:
public class CategoryModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Category Name")]
[MaxLength(50)]
public String categoryName { get; set; }
[DefaultValue(true)]
[Display(Name = "Active?")]
public bool isActive { get; set; }
}
Product model:
public class ProductModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Index("ItemNumber", 1, IsUnique = true)]
[Display(Name = "Item #")]
public int itemNumber { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Product")]
[MaxLength(50)]
public String product { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Description")]
[MaxLength(500)]
public String description { get; set; }
[DefaultValue(true)]
[Display(Name = "Active?")]
public bool active { get; set; }
[Display(Name = "Image Name")]
public String imageName { get; set; }
[Display(Name = "PDF Name")]
public String PDFName { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual CategoryModel Category { get; set; }
public IEnumerable<SelectListItem> CategoryList { get; set; }
public static IEnumerable<SelectListItem> getCategories()
{
using (var db = new ProductContext())
{
List<SelectListItem> list = new List<SelectListItem>();
var x = db.Categories.ToList();
foreach (var y in x)
{
list.Add(new SelectListItem { Value = y.ID.ToString(), Text = y.categoryName });
}
return list;
}
}
}
Admin Model:
public class AdminModel
{
public IEnumerable<CategoryModel> Categories { get; set; }
public IEnumerable<ProductModel> Products { get; set; }
public RegisterViewModel RegisterUsers { get; set; }
}
And here is the Controller method that lists the categories:
public ActionResult ControlPanel()
{
ViewBag.Message = TempData["Message"] == null ? "" : TempData["Message"].ToString();
//using (var db = new )
using (var db = new ProductContext())
{
var categories = from c in db.Categories
select c;
categories = categories.OrderByDescending(c => c.isActive);
var model = new AdminModel
{
Categories = categories.ToList(),
Products = db.Products.ToList()
};
return View(model);
}
}
For a single categoryID you can just use Linq's Count():
int specificCategoryID = 15;
return db.Products.Where(w => w.CategoryID == specificCategoryID).Count();
For a list of key/value pairs of categoryIDs and the count of products, something like this:
var products = db.Products.AsEnumerable();
var productCount = products.GroupBy(p => p.CategoryID,
(k, v) => new { CategoryID = k, ProductCount = v.Count() });
I would recommend using a proper viewmodel and making sure that it only uses one query so you dont run into a select n+1 issue.
Edit:
Assuming you always have a list of all categories and products you can just do the calculation in the Admin ViewModel on the items you already pulled from the DB:
public class AdminModel
{
public IEnumerable<CategoryModel> Categories { get; set; }
public IEnumerable<ProductModel> Products { get; set; }
public RegisterViewModel RegisterUsers { get; set; }
public int GetProductCountForCategoryID(int categoryID)
{
return this.Products
.Count(w => w.CategoryID == categoryID);
}
}
Then in the view, just pass in whatever category ID like this:
#Model.GetProductCountForCategoryID(categoryID);

How to use (left/right) outer join for this LINQ that join 3 entities?

Using MVC 3, EF4.1:
Building a quiz screen, I am joining three entities: Steps, Questions, Responses
Each Step can have many questions, each question can have one or no responses
My issue is when I have no answers in my query, it returns steps with no questions. How do I incorporate an outer join (left/right) into this LINQ?
var steps = from s in db.Steps
join r in db.Responses.Where(x => x.ReviewID == id)
on s.StepID equals r.Question.StepID into g
orderby s.StepOrder
select new Quiz
{
StepID = s.StepID,
Title = s.Title,
Results = from x in g
orderby x.Question.DisplayOrder
select new Result
{
QuestionID = x.Question.QuestionID,
DisplayOrder = x.Question.DisplayOrder,
Choices = x.Question.Choices,
ControlType = x.Question.ControlType,
QuestionText = x.Question.QuestionText,
AnswerValue = x.AnswerValue
}
};
Question Model:
public class Question
{
public Question()
{
this.Responses = new List<Response>();
}
public int QuestionID { get; set; }
public string QuestionText { get; set; }
public Nullable<bool> Required { get; set; }
public int DisplayOrder { get; set; }
public int StepID { get; set; }
public Nullable<int> DataType { get; set; }
public Nullable<int> ControlType { get; set; }
public string Choices { get; set; }
public Nullable<int> MaxLength { get; set; }
public virtual ICollection<Response> Responses { get; set; }
public virtual Step Step { get; set; }
public string NumberedQuestion
{
get { return String.Format("{0}. {1}", DisplayOrder, QuestionText); }
}
}
Response:
public class Response
{
public int ResponseID { get; set; }
public int UserID { get; set; }
public int QuestionID { get; set; }
public string AnswerValue { get; set; }
public int ReviewID { get; set; }
public virtual Question Question { get; set; }
}
Steps:
public Step()
{
this.Questions = new List<Question>();
}
public int StepID { get; set; }
public int ReviewID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int StepOrder { get; set; }
public virtual ICollection<Question> Questions { get; set; }
The issue is that you're getting the question from the responses, so you're only getting questions that have responses. If you get the questions for each step and then the response for each question, it should work.
var steps = from s in db.Steps
orderby s.StepOrder
select new Quiz
{
StepID = s.StepID,
Title = s.Title,
Results = from question in s.Questions
orderby question.DisplayOrder
select new Result
{
QuestionID = question.QuestionID,
DisplayOrder = question.DisplayOrder,
Choices = question.Choices,
ControlType = question.ControlType,
QuestionText = question.QuestionText,
AnswerValue = question.Responses
.Where(r => r.ReviewID == id)
.Select(r => r.AnswerValue)
.FirstOrDefault()
}
};
Not very pretty... but does what it should ;)
var result =
from step in db.Steps
let questions =
db.Questions.Where(item => item.StepID == step.StepID)
let responses =
db.Responses.Where(item =>
item.ReviewID == id
&& questions.Any(q => q.QuestionID == item.QuestionID))
select new Quiz
{
StepID = step.StepID,
Title = step.Title,
Results =
from question in questions.OrderBy(item => item.DisplayOrder)
select new Result
{
QuestionID = question.QuestionID,
DisplayOrder = question.DisplayOrder,
Choices = question.Choices,
ControlType = question.ControlType,
QuestionText = question.QuestionText,
AnswerValue =
responses.Any(item => item.QuestionID == question.QuestionID)
? responses.First(item =>
item.QuestionID == question.QuestionID).AnswerValue
: null
}
};

Get data from two tables (view and list) with LinQ and return into one view (code MVC-C#)

I have two tables: tour and hotel I want to execute query with join and get the result in the View.
How to view data from two tables as figure below?
enter link description here
in the Controller I have this code :
public ActionResult DetailView(string strID)
{
var id_tour = (from data1 in _db.Tours
join dataview2 in _db.TypeOfCosts on data1.ID_TourCost equals dataview2.ID_TourCost
where (data1.ID_Tour == strID) && (data1.ID_TourCost == dataview2.ID_TourCost)
select new
{
data1.TourName,
data1.ID_Tour,
data1.DepartureDay,
data1.DeparturePosition,
data1.AvailableRoom,
dataview2.AdultCost,
dataview2.ChildrenCost,
dataview2.BabyCost,
}).FirstOrDefault();
var view_tour = new DetailModels(id_tour.TourName, id_tour.ID_Tour, Convert.ToDateTime(id_tour.DepartureDay), id_tour.DeparturePosition,
Convert.ToInt32(id_tour.AvailableRoom),
Convert.ToInt32(id_tour.AdultCost), Convert.ToInt32(id_tour.ChildrenCost), Convert.ToInt32(id_tour.BabyCost));
return View(view_tour);
}
[HttpPost]
public ActionResult DetailView(DetailModels model)
{
var id_hotel = from data2 in _db.Tours
join dataview3 in _db.TourPrograms on data2.ID_Tour equals dataview3.ID_Tour
join dataview4 in _db.Programs on dataview3.ID_TourProgram equals dataview4.ID_TourProgram
join dataview5 in _db.Hotels on dataview4.ID_Hotel equals dataview5.ID_Hotel
where (data2.ID_Tour == dataview3.ID_Tour) &&
(dataview3.ID_TourProgram == dataview4.ID_TourProgram) && (dataview4.ID_Hotel == dataview5.ID_Hotel)
select new
{
dataview5.HotelName,
dataview5.HotelAddress,
dataview5.HotelPhoneNumber,
};
// chuyền dữ liệu vào như thế nào
return RedirectToAction("DetailView", "Tourpackage");
}
in the Model I have this code:
enter code here public class DetailModels
{
public string TourName { get; set; }
public string ID_Tour { get; set; }
public DateTime DepartureDay { get; set; }
public string DeparturePosition { get; set; }
public int AvailableRoom { get; set; }
public string HotelName { get; set; }
public string HotelAddress { get; set; }
public int HotelPhoneNumber { get; set; }
public int AdultCost { get; set; }
public int ChildrenCost { get; set; }
public int BabyCost { get; set; }
public DetailModels(string tourname, string idtour, DateTime dapartureday, string departureposition, int availableroom,
int adultcost, int childrencost, int babycost)
{
this.TourName = tourname; this.ID_Tour = idtour; this.DepartureDay = dapartureday; this.DeparturePosition = departureposition;
this.AvailableRoom = availableroom;
this.AdultCost = adultcost; this.ChildrenCost = childrencost; this.BabyCost = babycost;
}
hope to the help of everyone............thanks
When You are using MVC i strongly recommend You to use Entity Framework. If You never use it check this out: http://www.pluralsight-training.net/microsoft/Courses/TableOfContents?courseName=aspdotnet-mvc3-intro - very good video-tutorial.
I assume that Tour-Hotel relation is typical many-to-many. Mapped by EF class Tour will have property Hotels and vice versa. If You pass for example Tour to view #Model.Hotels give You collection of related hotels. And BTW do some refactoring code,please:)

Resources