There are lots of EF master-detail questions, I've looked through most of them. I can't find any that address the syntax approach I'm using which include transformation of the EF query results into a new business model, but I'm sure there is a way.
Here is the business model to emit from the DAL (header properties not shown)
public class Order : OrderHeader, IOrder
{
public virtual List<OrderDetail> OrderDetails { get; set; } = new List<OrderDetail>();
}
Here is the query reduced to show the problem I'm facing which is loading the collection of OrderDetails at the same time of pulling the orders. I could run another query after loading the headers but that sounds very slow and clunky.
public async Task<List<Business.Models.Order>> GetSimplifyOrders()
{
var query = (from n in _context.SalesOrderHeaders
join c in _context.Customers on n.CustomerId equals c.CustomerId
join d in _context.SalesOrderDetails on n.SalesOrderId equals d.SalesOrderId
select new Business.Models.Order
{
SalesOrderNumber = n.SalesOrderNumber,
Customer = new Business.Models.Customer()
{ CompanyName = c.CompanyName,
FullName = $"{c.FirstName} {c.LastName}",
EmailAddress = c.EmailAddress},
**OrderDetails = ??**
}).AsNoTracking()
.ToList<Business.Models.Order>();
return query;
}
Can someone help me with the specific syntax to load the details inline as shown above?
Rather than doing a join at the top level, you can use another internal LINQ query to populate a collection inside of your model.
from n in _context.SalesOrderHeaders
join c in _context.Customers on n.CustomerId equals c.CustomerId
select new Business.Models.Order
{
SalesOrderNumber = n.SalesOrderNumber,
Customer = new Business.Models.Customer()
{
CompanyName = c.CompanyName,
FullName = $"{c.FirstName} {c.LastName}",
EmailAddress = c.EmailAddress
},
OrderDetails =
(from d in _context.SalesOrderDetails
where n.SalesOrderId == d.SalesOrderId
select new Business.Models.OrderDetail()
{
...
})
.ToList()
}
Related
I have the following LINQ query that I want to return as a model with related joins etc all in place.
var query = (from wi in context.WorkItems
join mi in context.MaintenanceItems
on new { Id = wi.MaintenanceItemID }
equals new { Id = mi.MaintenanceItemID }
join p in context.Properties
on new { Id = mi.PropertyID }
equals new { Id = p.PropertyID }
join l in context.Locations
on new { Id = p.LocationID }
equals new { Id = l.LocationID }
join c in context.Categories
on new { Id = mi.CategoryID }
equals new { Id = c.CategoryID }
join sc in context.SubCategories
on new { Id = mi.SubCategoryID }
equals new { Id = sc.SubCategoryID }
join pl in context.PickLists
on new { Id = wi.PriorityFlag, V = "Priority" }
equals new { Id = pl.ValueKey, V = pl.ValueType }
where wi.AssignedTo1 == username
orderby wi.PriorityFlag descending, wi.PriorityIndex, wi.WorkItemID
select new {
wi.WorkItemID,
wi.PriorityFlag,
wi.PriorityIndex,
wi.CompletionDateTime,
l.LocationName,
p.PropertyFullName ,
wi.AssignedTo1,
wi.AssignedTo2,
wi.AssignedTo3,
c.CategoryName,
sc.SubCategoryName,
PriorityDesc = pl.ValueTitle
});
My model consists of a WorkItem entity which the query can return multiples of, with then related entities filling in the descriptive values for all the foreign keys etc.
Obviously using the query I have created an anonymous type, but I am not sure how to turn it back into the fully fleshed out known types I need?
I have created an anonymous type, but I am not sure how to turn it back into the fully fleshed out known types I need?
Instead of projecting selected elements into a new type, use Eager Loading to Include() the full related entities in your result.
Also instead of projecting individual scalar properties in your anonymous type, you can project whole entities.
I have 3 tables:
1. Tbl_Model,
2. Tbl_ModelImg,
3. Tbl_Category
I want to know how to fetch the records from these 3 tables using categoryId.
The single model may have multiple images, but I want to show all the products of that category with their images. The problem is I only want a single image in that view. So when a user clicks on that particular model, the details of that model and all its images will show on the next view.
The following query is working fine but it displays all the images with their model name. Means If a model has 4 images than on category details page it displays 4 items with the same name and different images.
Here is the model class :
public class showdata
{
public Tbl_ModelImg tmi { get; set; }
public Tbl_Model tm { get; set; }
public Tbl_SubCategory tblsubcategory { get; set; }
}
public ActionResult Categorydetails(string sid)
{
var sId = Int64.Parse(new
StandardModule().Decrypt(HttpUtility.UrlDecode(sid.ToString())));
try
{
var query = (from c in db.Tbl_Model
join o in db.Tbl_ModelImg
on c.Model_Id equals o.Model_Id
join d in db.Tbl_SubCategory on c.SubCategory_Id
equals d.Id
where c.SubCategory_Id == sId
select new showdata()
{
tm = c,
tmi = o,
tblsubcategory = d
}).OrderByDescending(d => d.tm.Id).ToList();
return View(query);
}
Replace the second line of the LINQ query with
join o in db.Tbl_ModelImg.GroupBy(m => m.Model_Id).Select(m => m.First())
The GroupBy() will group the items in Tbl_ModelImg by Model_Id and then the Select() will cut the contents of the resulting dataset down to just the first item in each group.
Once the where clause restricts your results to just those that items that join to the correct SubCategory_Id then you have what you need.
var query = (from c in db.Tbl_Model
join o in db.Tbl_ModelImg.GroupBy(m => m.Model_Id).Select(m
=> m.FirstOrDefault())
on c.Model_Id equals o.Model_Id
join d in db.Tbl_SubCategory on c.SubCategory_Id equals d.Id
where c.SubCategory_Id == sId
select new showdata()
{
tm = c,
tmi = o,
tblsubcategory = d
}).OrderByDescending(d => d.tm.Id).ToList();
I have a function to get data from my database with join on it. I want to take data from different tables, how can I achieve this? I want to take "libelle_motif" which is from the table "motif_deplacement"
My function right now:
public static List<personne> getPersonne_Deplacement(int numDeplacement)
{
List<personne> desP = new List<personne>();
var query = (from Per in db.personne.ToList()
join Dep in db.deplacement_personne.ToList() on Per.num_personne equals Dep.num_personne
join Mot in db.motif_deplacement.ToList() on Dep.id_motif equals Mot.id_motif
where Dep.id_deplacement == numDeplacement
select new personne
{
nom_personne = Per.nom_personne,
num_personne = Per.num_personne,
ref_personne = Per.ref_personne,
libelle_motif = Mot.libelle_motif,
});
desP = query.ToList();
return desP;
}
And this is how my database looks like :
You will have to create a new class which will act as the model with the properties you want. Construct one of those in your select based on the included relationships.
Also it much easier to manage this query if you model your relationships in your EF entities as opposed to writing out join statements in every query.
Also notice that I removed all the calls to ToList. Your previous code was materializing all entities from each table and then joining and filtering in memory which is extremely inefficient.
public static List<SomeModel> getPersonne_Deplacement(int numDeplacement)
{
var query = from Per in db.personne
join Dep in db.deplacement_personne on Per.num_personne equals Dep.num_personne
join Mot in db.motif_deplacement on Dep.id_motif equals Mot.id_motif
where Dep.id_deplacement == numDeplacement
select new SomeModel
{
nom_personne = Per.nom_personne,
num_personne = Per.num_personne,
ref_personne = Per.ref_personne,
libelle_motif = Mot.libelle_motif,
};
return query.ToList();
}
SomeModel.cs
public class SomeModel
{
public string nom_personne {get;set;}
public string num_personne {get;set;}
public string ref_personne {get;set;}
public string libelle_motif {get;set;}
// add additional properties as needed
}
I don't know if my answer is related to your question. But why don't you use .Include()
Ex:
var result = db.personne.Include(x => x.deplacement_personne).ThenInclude(x => x.motif_deplacement)
It will give you this structure:
obj personne { nom_personne, ... , obj deplacement_personne { ... , obj motif_deplacement }
You can also select any cols with .Select() , or filter it with .Where().
I have generated Entity from database using VS. Now when I want to join two tables from my database and show the result using datagrid view.
my LINQ is :
result = (from lang in my_entity.LANGUAGE
join c in my_entity.COUNTRY
on lang.COUNTRY_ID equals c.ID
select lang).ToList<LANGUAGE>();
dgvresult.DataSource = result;
dgvresult.Refresh();
All the columns from Language tables are shown but none are shown from Country table.
I want few columns from language and few columns from country table to be shown in datagrid view.
How do I do it. Any learning links are also appreciated.
Also I want to learn DataGrid View in detail. If any one can suggest good books or materials , please do.
You can create a new anonymous type and then bind to it, like so:
result = (from lang in my_entity.LANGUAGE
join c in my_entity.COUNTRY
on lang.COUNTRY_ID equals c.ID
select new
{
lang.Col1,
land.col2,
c.Col1,
c.Col2
}).ToList();
dgvresult.DataSource = result;
dgvresult.Refresh();
Or you can create a view model and simply select the values into it:
public class LangCountryModel
{
public string LangCol1 { get; set; }
public string LangCol2 { get; set; }
public string CountryCol1 { get; set; }
public string CountryCol2 { get; set; }
}
result = (from lang in my_entity.LANGUAGE
join c in my_entity.COUNTRY
on lang.COUNTRY_ID equals c.ID
select new LangCountryModel
{
LangCol1 = lang.Col1,
LangCol2 = land.col2,
CountryCol1 = c.Col1,
CountryCol2 = c.Col2
}).ToList();
var result = (from ob1 in lista1
join ob2 in lista2
on ob1.Id equals ob2.Id
select new
{
ob1.Age,
ob2.CarName
}).ToList();
dgvresult.DataSource = result;
dgvresult.Refresh();
Like #pingoo answer with a small edit:
You can do it without using the Join LINQ keyword by using the navigation property COUNTRY of the source table LANGUAGE like this:
result = (from lang in my_entity.LANGUAGE
select new {
lang.Col1,
lang.col2,
lang.COUNTRY.COUNTRY_ID
}).ToList();
dgvresult.DataSource = result; dgvresult.Refresh();`
(...).ToList<LANGUAGE>()
.Select(p=>new{p.LanguageProp,p.COUNTRY.COUNTRYProp,...})
I'm trying to recover and display data from multiple table. o i've done a mutliple join like that :
var prod = from product in context.PRODUCT
join islocated in context.ISAUDITEDIN on product.PRODUCT_ID equals islocated.PRODUCT_ID
join location in context.LOCATION on islocated.LOCATION_ID equals location.LOCATION_ID
orderby location.LOCATION_ID
group new
{
Location_ID = location.LOCATION_ID,
Product_ID = product.PRODUCT_ID
} by location.LOCATION_ID into locat
select new {
Location_ID = locat.Key,
Product = locat
};
where context is the OData that result to a call from a web service. That link is working : when I do a simple select, I'm able to recover the data.
Then, I want to display the data that are in the result. So i've created a Dictionnary :
Dictionary<string,List<ProductModel>> dict = new Dictionary<string,List<ProductModel>>();
foreach (var locat in prod) {
List<ProdctModel> products = new List<ProductModel>();
foreach (var p in locat.Product)
{
products.Add(new ProductModel(p.Location_ID, p.Product_ID));
}
dict.Add(locat.Location_ID.ToString(), products);
}
Where ProductModel is a simple class like that:
public class ProdctModel
{
public int locationID{get; set;}
public int productID{get; set;}
public ProdctModel(int location_ID, int product_ID)
{
this.locationID = location_ID;
this.productID = product_ID;
}
}
But when i'm running that, i've got the following :
“the method join is not supported”
Ligne 131 : Dictionary<string,List<ProdctModel>> dict = new Dictionary<string,List<ProdctModel>>();
Ligne 132 : foreach (var locat in prod) {
How to solve that ? I've seen a couple of things by using method .Expand() instead of join but I dunno how to use it : could you help me ?
Thanks!
I never used the odata with linq, but im assuming you can't join different remote resources. Try to get the objects to memory first, and then do the join:
var products = context.PRODUCT.ToList();
var islocateds = context.ISAUDITEDIN.ToList();
var locations = context.LOCATION.ToList();
var prod = from product in products
join islocated in islocateds on product.PRODUCT_ID equals islocated.PRODUCT_ID
join location in locations on islocated.LOCATION_ID equals location.LOCATION_ID
orderby location.LOCATION_ID
group new
{
Location_ID = location.LOCATION_ID,
Product_ID = product.PRODUCT_ID
} by location.LOCATION_ID into locat
select new {
Location_ID = locat.Key,
Product = locat
};
Remember that this will download all 3 tables.
EDIT
Also, the reason you are getting the exception when creating dictionary, is that the "prod" varialble is an IEnumerable- it executes the request only when you do "foreach" or something similiar.