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.
Related
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()
}
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,...})
Below is my LINQ Query, that im using to select ITEMS:
protected void Page_Load(object sender, EventArgs e)
{
whatsmydiscountEntities ctx = new whatsmydiscountEntities();
int IdRelationshipItems = Convert.ToInt32(Request.QueryString["IdRelationshipItems"]);
int IdProductService = Convert.ToInt32(Request.QueryString["IdProductService"]);
var Items = (from es in ctx.State
join ie in ctx.ItemsStates on es.StateId equals ie.StateId
join i in ctx.Items on ie.IdItem equals i.IdItem
join iir in ctx.ItemsRelationshipItems on i.IdItem equals iir.IdItem
join ir in ctx.RelationshipItems on iir.IdRelationshipItems equals ir.IdRelationshipItems
join ips in ctx.ItemsProductsServices on i.IdItem equals ips.IdItem
join ps in ctx.ProductsServices on ips.IdProductService equals ps.IdProductService
where iir.IdRelationshipItems == IdRelationshipItems
&& ips.IdProductService == IdProductService
&& ir.Active == 1
&& i.Active == 1
select new
{
ItemName = i.Name,
StateSigla = es.Sigla,
ProductServiceName = ps.Ttitle,
RelationshipItemName = ir.Name,
RelationshipItemImage = ir.Image,
RelationshipItemActive = ir.Active,
ItemSite = i.Site,
ItemDescription = i.Description,
ItemAddress = i.Address,
Iteminformationdiscount = i.information_discount,
ItemLogo = i.Logo,
ItemActive = i.Active,
StateId = ie.StateId,
IdRelationshipItems = iir.IdRelationshipItems,
IdProductService = ips.IdProductService
}).ToList();
}
As you can see, the result will be 1 row for each state, if the user passes the IdRelationshipItems and the IdProductService.
Instead of 1 row for each state with the same information, I'd like to show only 1 row and all the states separated by commas. What do I need to change to do this?
I had to solve this problem today. I have a view model that looks like this:
public class IndexViewModel
{
public string Search { get; set; }
public IPagedList<MembershipUser> Users { get; set; }
public IEnumerable<string> Roles { get; set; }
public bool IsRolesEnabled { get; set; }
public IDictionary<string,string> Tags { get; set; }
}
I needed to return a unique list of users and all of the Tags that are assigned to them as a comma separated string.
The data is stored like this:
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", tag1
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", tag2
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", tag3
And I needed output that looked something like this:
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", "tag1, tag2, tag3"
I ended up creating a List of UserId's like this (I'm using the MembershipProvider which exposes the UserId as ProviderUserKey):
var userIdList = users.Select(usr => (Guid) usr.ProviderUserKey).ToList();
The object "users" is a MembershipUser object. I then call a function in my service passing the List in like this:
Tags = _usersTagsService.FindAllTagsByUser(userIdList)
And my service function looks like this:
public IDictionary<string, string> FindAllTagsByUser(IList<Guid> users)
{
var query = (from ut in _db.UsersTags
join tagList in _db.Tags on ut.TagId equals tagList.Id
where users.Contains(ut.UserId)
select new {ut.UserId, tagList.Label}).ToList();
var result = (from q in query
group q by q.UserId
into g
select new {g.Key, Tags = string.Join(", ", g.Select(tg => tg.Label))});
return result.ToDictionary(x=>x.Key.ToString(),y=>y.Tags);
}
I'm pretty sure these two linq statements can probably be combined into one but I find it easier to read this way. Still only hits the database once.
Things to watch out for:
I was originally passing just IList users into the function FindAllTagsByUser and linq couldn't infer the type and therefore wouldn't let me use the .Contains extension method. Kept saying that linq to entities didn't support Contains.
You need to do a .ToList() on the first query to materialize it or you will get trouble from linq to entity when you try to use string.Join to create the comma separated list.
Good luck
dnash