Linq to Entity | Pass table join to model - asp.net-mvc

Good time of a day!
I have a MVC project with query in controller:
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
orderby n.id descending
select new {
m.imgcrop, m.id,
n.commenterName, n.comment
}).Take(10);
How to pass this query to view model, and the model to view.
Spend all evening to find the examples, but cant. Thanks for help!
UPDATED
Full Model Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace photostorage.Models
{
public class GlobalModel
{
public class PhotoViewModel
{
public photos Photos { get; set; }
public profiles Profile { get; set; }
public IQueryable<comments> Comments { get; set; }
public IQueryable<photos> NextPrev { get; set; }
}
public class UserPhotoList
{
public IQueryable<photos> Photos { get; set; }
public profiles Profile { get; set; }
}
public class UserProfileView
{
public IQueryable<photos> Photos { get; set; }
public profiles Profile { get; set; }
}
public class GetLastComments
{
public IQueryable<photos> uPhoto { get; set; }
public IQueryable<comments> uComments { get; set; }
}
}
}
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using photostorage.Models;
namespace photostorage.Controllers
{
public class HomeController : Controller
{
private photostorageEntities db = new photostorageEntities();
public ActionResult Index()
{
if(Request.IsAuthenticated) {
GlobalModel.GetLastComments model = new GlobalModel.GetLastComments();
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
select new {
m.imgcrop, m.id,
n.commenterName, n.comment
}).Take(10);
return View("Index_Auth", model);
}else{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View("Index");
}
}
public ActionResult About()
{
return View();
}
}
}

In this case you can make a "view model" that will only be used by your view and not by the rest of your application. Something like the following:
public class CommentsViewModel
{
public int MessageId { get; set; }
public string ImageCrop { get; set; }
public string CommenterName { get; set; }
public string Comment { get; set; }
}
Then change your query like so:
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
orderby n.id descending
select new CommentsViewModel {
ImageCrop = m.imgcrop,
MessageId = m.id,
CommenterName = n.commenterName,
Comment = n.comment
}).Take(10).ToList();
Make your view strongly typed to the new class and pass the data to it like so:
View("name_of_your_view", getPhotos);

If you wanted to do this, like you had:
var getPhotos = (from m in db.photos
join n in db.comments on m.id equals n.photoid
where n.ownerName == User.Identity.Name
select new {
m.imgcrop, m.id,
n.commenterName, n.comment
}).Take(10);
You could actually have this without creating a new "CommentsViewModel", but just use what should be the existing tables and models:
var getPhotos = (from m in db.Photos
join n in db.Comments on m.Id equals n.PhotoId
where n.OwnerName == User.Identity.Name
select new {
ImageCrop = m.ImageCrop,
Id = m.Id,
CommenterName = n.CommenterName,
Comment = n.Comment
}).Take(10);
The models would be something like these examples, if you had a foreign key relationship on the Photo.Id to Comments.PhotoId:
public class Photos
{
public int Id { get; set; }
public string ImageCrop { get; set; }
[ForeignKey("PhotoId")]
public virtual Comments Comment { get; set; }
}
public class Comments
{
public int Id { get; set; }
public int PhotoId { get; set; }
public string CommenterName { get; set; }
public string OwnerName { get; set; }
public string Comment { get; set; }
}
Just a note: The models you displayed in your question had none of these columns, yet you were building a query against them. It's best to remember to give a complete picture when asking for help.

Related

Entity Framework SQL not optimal on navigation property

I have simple code first model (generated from db) with 3 entities:
[Table("Note")]
public partial class Note
{
public Note()
{
NoteCompanies = new HashSet<NoteCompany>();
}
public long ID { get; set; }
[Column(TypeName = "text")]
public string Content { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<NoteCompany> NoteCompanies { get; set; }
}
[Table("Company")]
public partial class Company
{
public long ID { get; set; }
[StringLength(150)]
public string Name { get; set; }
}
[Table("NoteCompany")]
public partial class NoteCompany
{
public long ID { get; set; }
public long NoteID { get; set; }
public long CompanyID { get; set; }
public virtual Company Company { get; set; }
}
When i use this Model inside ASP MVC View like:
#model Models.Note
<ul>
#for (var company in Model.NoteCompanies.Select( nc => nc.Company ))
{
#company.Name
}
</ul>
Entity framework fires single select query for each Company. I would expect that Entity would use produce JOIN query like:
SELECT {fields}
FROM
NoteCompany NC
INNER JOIN Company C ON NC.CompanyId = C.Id
WHERE
NC.NoteId = #Param
Is it possible to force EF to produce JOIN query instead of single row SELECT?
Best Regards
IT Man
It will be better to write something like this:
#{
var ids = Model.NoteCompanies.Select(nc => nc.CompanyID).ToList();
for (var company in db.Companies.Where(x => ids.Contains(x.ID)).ToList())
{
#company.Name
}
}
Or try to get NoteCompanies with corresponding Companies at controller via eager loading:
model.NoteCompanies = db.NoteCompanies.Include(x => x.Company).ToList();
return View(model);

Join two Model classes result and display it on View-MVC

I want to combine below two classes result using Linq and display the result on a view?
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
[Table("ProductMaster")]
public partial class ProductMaster
{
public ProductMaster()
{
ProductDetails = new HashSet<ProductDetail>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductId { get; set; }
[StringLength(50)]
public string ProductName { get; set; }
public virtual ICollection<ProductDetail> ProductDetails { get; set; }
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class ProductDetail
{
public int? Price { get; set; }
[StringLength(50)]
public string ManufactureBy { get; set; }
public int? ProductId { get; set; }
[Key]
public int ProdDetailsId { get; set; }
public virtual ProductMaster ProductMaster { get; set; }
}
Join two tables data using LINQ Query.
public ActionResult BindProductMasterData()
{
Model1 db = new Model1();
var varResult = from pm in db.ProductMasters join pd in db.ProductDetails on pm.ProductId equals pd.ProductId select new { pm.ProductId, pm.ProductName, pd.Price, pd.ManufactureBy };
return View(varResult.ToList());
}
Display below table columns data in View
ProductId , ProductName, Price, ManufactureBy
Create a view model for this view and assign your LINQ join query result to a list of that and pass that to the view.
public class ProductVm
{
public int Id { set;get;}
public string Name { set;get;}
public List<ProductDetailVm> Details { set;get;}
}
public class ProductDetailVm
{
public int Id { set;get;}
public int? Price { get; set; }
public string ManufactureBy { get; set; }
}
and in your action method, project the result of your LINQ query to a list of ProductVm objects.
public ActionResult BindProductMasterData()
{
Model1 db = new Model1();
var varResult = db.ProductMasters
.Select( f =>
new ProductVm
{
Id = f.ProductId,
Name = f.ProductName,
Details = f.ProductDetails.Select(g => new ProductDetailVm
{
Id = g.ProdDetailsId ,
Price = g.Price,
ManufactureBy = g.ManufactureBy
}
).ToList()
}).ToList();
return View(varResult);
}
Now make sure your view is strongly typed to a list of our ProductVm view model
#model List<ProductVm>
#foreach(var p in Model)
{
<h4>#p.ProductName</p>
<p>Details</p>
#foreach(var d in p.Details)
{
<p>#d.ManufacturedBy</p>
<p>#d.Price</p>
}
}

How to get newly generated id on the MVC model

I have the model that has multiple entities. For example I have an order that have CustomerId to foreign key. In the model I have some fields from the order and some fields from the customer entity. When I am saving new record I would like to save customer first, get newly generated identity value and put it to the order table. How to achieve that? I can do that by getting MAX(ID) from the customer table, however I am pretty sure that there is better way to handle that.
This is my controller method:
public ActionResult Create(OrderModels model, FormCollection form)
{
try
{
Customer customer = new Customer() { FirstName = model.FirstName, MiddleName = model.MiddleName, SecondName = model.SecondName, Email = model.Email, PhoneNbr = model.PhoneNbr };
int orderSource = Int32.Parse(form["OrderSourceList"]);
int paymentType = Int32.Parse(form["PaymentTypeList"]);
string warehouseGuid = form["Warehouses"];
ProductLogic productLogic = new ProductLogic();
Product product = productLogic.GetProductByArticle(model.Article);
DateTime now = DateTime.Now;
SalesOrder salesOrder = new SalesOrder()
{
OrderNbr = model.OrderNbr,
OrderSourceId = orderSource,
NpWarehouseRef = new Guid(warehouseGuid),
TTN = model.TTN,
OrderStatusId = 1,
PaymentTypeId = paymentType,
OrderDate = DateTime.Now
};
using (AccountingRepository repository = new AccountingRepository())
{
repository.AddOrUpdate<Customer>(customer);
repository.AddOrUpdate<SalesOrder>(salesOrder);
}
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
AccountingRepository has dispose method
public void Dispose()
{
if (_context != null)
{
_context.SaveChanges();
_context.Dispose();
}
}
My Order class:
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("SalesOrder", Schema = "dbo")]
public class SalesOrder
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
...
public int? CustomerId { get; set; }
...
public virtual Customer Customer { get; set; }
internal class Config : EntityTypeConfiguration<SalesOrder>
{
public Config()
{
HasRequired(r => r.Customer)
.WithMany(r => r.SalesOrder)
.HasForeignKey(r => r.CustomerId);
}
}
}
}
My Customer class:
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("Customer", Schema = "dbo")]
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string SecondName { get; set; }
public string PhoneNbr { get; set; }
public string Email { get; set; }
public virtual HashSet<SalesOrder> SalesOrder { get; set; }
internal class Config : EntityTypeConfiguration<Customer>
{
public Config()
{
}
}
}
}
Two ways:
Save the customer entity, and Entity Framework will back fill the PK property with it's ID. You can then use this for the order entity.
db.Customers.Add(customer);
db.SaveChanges();
order.CustomerId = customer.Id; // has a value now
Just associate the customer with the order via a navigation property. When you save everything Entity Framework works out which relationships need to be saved first and then fills in the appropriate FK ids in the related entities.
order.Customer = customer;
db.Orders.Add(order);
db.SaveChanges(); // customer is saved first and the id is set for order.CustomerId automatically

How do I manually populate ViewModel (Not using AutoMapper!)

I know there are a lot of posts on the subject but I cannot find one that helps me do what I want. I know that I will eventually be using Automapper but before I start playing with it, I want to learn how to do things manually. I want to create a ViewModel, populate it with values from my entities by way of a repository and send it to my View. As simple as this sounds, I am stuggling to get it done. I'm using MVC 3, EF 4.3, Database First. I have auto-generated my classes. I'm posting the relevant entities (abbreviated/renamed for this post) and classes, here is what I have so far:
Aggregate Entity: Shipping Header
using System;
using System.Collections.Generic;
namespace My.Models
{
public partial class ShippingHdr
{
public ShippingHdr()
{
this.ShippingLI = new HashSet<ShippingLI>();
}
public int ID { get; set; }
public int ShipToSiteID { get; set; }
public Nullable<System.DateTime> DateShipped { get; set; }
public Nullable<System.DateTime> EstDeliveryDate { get; set; }
public string FromSitePOC { get; set; }
public Nullable<int> ShipperID { get; set; }
public string TrackingNo { get; set; }
public string Comments { get; set;}
public virtual Shippers Shippers { get; set; }
public virtual ICollection<ShippingLI> ShippingLI { get; set; }
}
}
Here is my ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace My.Models.ViewModels
{
public class ShippingHeaderSummaryVM
{
public int ID { get; set; }
public string Site { get; set; }
public Nullable<System.DateTime> DateShipped { get; set; }
public Nullable<System.DateTime> EstDeliveryDate { get; set; }
public string TrackingNo { get; set; }
public string HeaderComments { get; set; }
public string Shipper { get; set; }
public int NumOrders { get; set; }
public string Site { get; set; }
}
}
Here is a query I got to return the items I want to use to populate my Viewmodel with. I believe the best place for this is in a Repository. I verified it returns the data I want using LinqPad (hence the missing reference to my dbContxt). I just don't know how to get the values from the query to the ViewModel:
var shipments = from h in c.ShippingHdrs
where (h.ShippingLI.Count > 1)
join
e in c.vHr_Employees on h.CreatedBy equals e.ID
join
s in c.Shippers on h.ShipperID equals s.ShipperID
join
r in vAaiomsSites on h.ShipToSiteID equals r.SiteID
select new
{
h.ID,
r.Site,
h.EstDeliveryDate,
h.DateShipped,
h.TrackingNumber,
h.HeaderComments,
e.LastName,
h.ShippingLI.Count,
s.Shipper
};
So what I want to do, again without using Automapper, is to populate the ViewModel with all of the rows from the ShippingHdr entity and pass it to my view.
Here are the filelds that need to be mapped:
ShippingHeaderSummaryVM mapped from shipments
ID = h.ID
Site = r.Site
DateShipped = h.DateShipped
EstDeliveryDate = h.EstDeliveryDate
TrackingNo = h.TrackingNumber
FromSitePOC = e.LastName
NumOrders = h.ShippingLI.Count
Shipper = s.Shipper
HeaderComments = h.HeaderComments
I am stuck here.
How do I populate the ViewModel from the query?
How then do I then call that action from my controller?
I hope I have given enough information, any help would be appreciated.
In order to populate a list of shipments based on your view model object you would need to create a mapping method to map from your collection of shipments from your database to a collection of shipments based on your view model:
var model = new List<ShippingHeaderSummaryVM>();
foreach(var h in shipments)
{
var viewModel = new ShippingHeaderSummaryVM
{
ID = h.ID
Site = r.Site
DateShipped = h.DateShipped
EstDeliveryDate = h.EstDeliveryDate
TrackingNo = h.TrackingNumber
FromSitePOC = e.LastName
NumOrders = h.ShippingLI.Count
Shipper = s.Shipper
HeaderComments = h.HeaderComments
}
model.Add(viewModel);
}
return model;
As a side note, this becomes a one liner after you have AutoMapper up and running:
var model = Mapper.Map<IEnumerable<ShippingHdr>, IEnumerable<ShippingHeaderSummaryVM>>(shipments);
While, learning how to do things manually is great. Manually mapping models doesn't really benefit you in any way or form. Go with AutoMapper.
You can also use Linq to do something like this...
shipments.Select(h => new ShippingHeaderSummaryVM(){
ID = h.ID,
Site = r.Site,
DateShipped = h.DateShipped,
EstDeliveryDate = h.EstDeliveryDate,
TrackingNo = h.TrackingNumber,
FromSitePOC = e.LastName,
NumOrders = h.ShippingLI.Count,
Shipper = s.Shipper,
HeaderComments = h.HeaderComments
});
Note that while mapping view models is great for passing to a view, always do it manually when reading from a view model to update your database.
Edit: Thanks for the typo correction:-)

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