Inner Join on Entity Framework - c#

There is a table name Product:
|ProductID|ProductName|
1 abc
2 xyz
and there is another table Product Status.
The column Result in the below has three values:
Result
Value | Meta
0 Not checked
1 Failed
2 Passed
ProductCheckList
ID(AutoGenerated) | ProductID(Foreign Key) | Stage | Result |
1 1 1 1
2 1 2 2
In this table every product has to go through five different stages. If the product passes all the stages then the product is given quality checked status , If the product fails any on of the stages is is given as quality failed and send back to production.
I have to show a list of all the products with there quality status and depending on its quality state highlight the row with different row color. We are using Entity Framework and I am new to this.
I have thought of making a wrapper class for this.
Public class ProductWrapper
{
public Product product{get;set;}
Public string QualityStatus{get;set;}
public string BgColor {get;set;}
}
I am writing this LINQ query:
UtilitiesEntities context = new UtilitiesEntities();
List<ProductWrapper> wrapperList = new List<ProductWrapper>();
var request = from product in context.Product
join productCheck in context.ProductCheckList
on product.productId equals productCheck .productID
// may be do group by or something i get the result and assign the values.
select new List<ProductWrapper>
{
};
I am not able to write the query and add the where condition to fetch the result a list of wrapper class to pass to my view with the desired result.

If I understood correctly your request, you want something like this:
string goodQualityColor = "Green";
string badQualityColor = "Red";
string notCheckedColor = "Gray";
string notCheckedStatus = "Not Checked";
string failedStatus = "Failed";
string passedStatus = "Passed";
Dictionary<int,string> results= new Dictionary<int,string>();
results.Add(2,goodQualityColor);
results.Add(1,badQualityColor);
results.Add(0,notCheckedColor);
Dictionary<int,string> qualityStatuses = new Dictionary<int,string>();
results.Add(2,passedStatus);
results.Add(1,failedStatus);
results.Add(0,notCheckedStatus);
var request = (from product in context.Product
join productCheck in context.ProductCheckList
on product.productId equals productCheck.productID
select new
{
Product = product,
QualityStatus = productCheck.Result,
Result = productCheck.Result
}).ToList();
var finalResults = (from req in request
select new ProductWrapper
{
Product = req.Product,
QualityStatus = qualityStatuses.FirstOrDefault(s => s.Key == req.QualityStatus).Value,
BgColor = results.FirstOrDefault (s => s.Key == req.Result).Value
}).ToList<ProductWrapper>();

I would create an outer join using the into keyword and then do my statusCheck in the select part.
Could would look something like this :
var data = (from product in context.Product
join productCheck in context.ProductCheckList on product.productId equals productCheck.productID into productChecks
select new ProductWrapper
{
product = product,
passedBool = productChecks.All(pc => pc.Result == 2) && productChecks.Count() == 5
}).ToList();

Related

How do I order by a function in LINQ query?

I am developing my first MVC application with Entity Framework. I have a table USERS and a table RESTRICTIONS
In my controller I wrote a function that returns the number of common restrictions between two users:
public int common_restrictions(int id1, int id2)
{
MyModel bd = new MyModel();
int count= 0;
var restrictions = from c in bd.RESTRICTIONS where c.ID_USER == id1
select c;
var restrictions2 = from c in bd.RESTRICTIONS where c.ID_USER == id2
select c;
foreach (var prop in restrictions)
{
var nr = restrictions2.Count(p => p.ID_PROP == prop.ID_PROP);
if (nr != 0)
{
count++;
}
}
return count;
}
The function works as it supposed to.
Now in another function in the same controller I want to sort the list of users in descending order of their common restrictions with a specific user (let's say user with the id 12). I got the list of users in a query but I don't know how to sort them after that
var query = from u in bd.USERS where u.ID != 12
select u;
// sort the list??
I've tried
var query = from u in bd.USERS orderby(common_restrictions(u.ID,
12)) select u;
but I get the following error message:
"LINQ to Entities does not recognize the method 'Int32 common_restrictions (Int32, Int32)' method, and this method cannot be translated into a store expression."
You need to do this in two steps if you don't want to include another property to your User object. And I think this following way is easier.
var query = (from u in bd.USERS where u.ID != 12
select u).ToList();
var unSorteUsers = (from u in query
select new
{
User = u,
CR = common_restrictions(u.ID,12)
});
var sortedUsers = (from u in unSorteUsers
orderby u.CR
select new User
{
ID = u.User.ID,
//All other properties.
});

Get list of child records

I have a database that looks like this:
tbl_Seminar
ID
isActive
tbl_SeminarFees
ID
seminar_id -- foreign key
fee_text
I want to get all seminars that are active (isActive ==1) and a list of the fees associated with that seminar. Each Seminar may have n records in tbl_SeminarFees that are its fees. I am able to return a linq structure that returns me a list of objects that look like this {seminar, SeminarFee} but I wanted to create a nested structure that looks like this:
{seminar, List<SeminarFee>}
What should my linq query look like?
here is my linq currently:
var results = from s in context.Seminar
join p in context.SeminarFees on
s.ID equals p.SeminarID
where s.IsActive == 1
select new
{
Seminar = s,
Fees = p
};
How do I change this to get a list of these: {seminar, List<SeminarFee>}
Thanks
UPDATE
#lazyberezovsky gave me a good idea to use a group join and into another variable. But then how do I loop through the result set. Here is what I have now:
foreach (var seminarAndItsFeesObject in results)
{
//do something with the seminar object
//do something with the list of fees
}
This however gives me the following error:
Argument type 'SeminarFees' does not match the
corresponding member type
'System.Collections.Generic.IEnumerable`1[SeminarFees]'
What am I doing wrong?
Thanks
You can use group join which groups inner sequence items based on keys equality (a.k.a. join..into) to get all fees related to seminar:
var results = from s in context.Seminar
join f in context.SeminarFees on
s.ID equals f.SeminarID into fees // here
where s.IsActive == 1
select new
{
Seminar = s,
Fees = fees
};
You can't call ToList() on server side. But you can map results on client later.
BTW You can define navigation property Fees on Seminar object:
public virtual ICollection<SeminarFee> Fees { get; set; }
In this case you will be able load seminars with fees:
var results = context.Seminar.Include(s => s.Fees) // eager loading
.Where(s => s.IsActive == 1);
var results = from s in context.Seminar
join p in context.SeminarFees on s.ID equals p.SeminarID
where s.IsActive == 1
group p by s into grouped
select new {
Seminar = grouped.Key,
Fees = grouped.ToList()
};

More efficient way of loading children of entity objects in linq to entity query

I have a rather complex linq to entity query that I'm performing, in the end, I have a result set. I loop through that result set, build business objects and return that list of business objects. it's pretty quick, the problem is that 2 of the child properties are complex objects with their own child objects. for every business object in my loop, I then have to make 2 DB calls to fill its child object. Those 2 calls slow down the overall process, is there a better way to do this? noob to EF here. (EF 4,SQL Server 2008,c#)
Get a result set:
var newresult = from r in result // result is another complex query
join subedit in
(from sa in context.Security_Access
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx) && g.UserID == userId
select new { user = g.UserID, linkid = sa.LinkID }).Distinct() on new { aid = r.AssetId } equals new { aid = subedit.linkid } into theSubEdit
from subEditAccess in theSubEdit.DefaultIfEmpty()
join subdownload in
(from sa in context.Security_Access
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx|| sa.PrivledgeID == yy) && g.UserID == userId
select new { user = g.UserID, linkid = sa.LinkID }).Distinct() on new { aid = r.AssetId } equals new { aid = subdownload.linkid } into theSubDownload
from subDownloadAccess in theSubDownload.DefaultIfEmpty()
join subView in
(from sa in context.Security_Access
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx|| sa.PrivledgeID == yy|| sa.PrivledgeID == 101) && g.UserID == userId
select new { user = g.UserID, linkid = sa.LinkID }).Distinct() on new { aid = r.AssetId } equals new { aid = subView.linkid } into theSubView
from subViewAccess in theSubView.DefaultIfEmpty()
select new { r, EditAccess = (int?)subEditAccess.user, DownloadAccess = (int?)subDownloadAccess.user, ViewAccess = (int?)subViewAccess.user };
I then loop through that result set:
foreach (var asset in newresult)
{
// and build a new business object, set its properties
BoAsset boAsset = new BoAsset();
boAsset.HasEditRights = (asset.EditAccess > 0);
boAsset.HasDownloadRights = (asset.DownloadAccess > 0);
boAsset.HasViewRights = (asset.ViewAccess > 0);
boAsset.Description = asset.r.Description;
boAsset.DetailedDescription = asset.r.DetailedDescription;
boAsset.Keywords = asset.r.Keywords;
boAsset.Notes = asset.r.Notes;
boAsset.Photographer = asset.r.Photographer;
boAsset.PhotographerEmail = asset.r.PhotographerEmail;
boAsset.Notes = asset.r.Notes;
boAsset.Author = asset.r.Author;
// these 2 properties i've commented out are
// complex objects/entities, setting them the way I am
// requires me to call 2 separate methods which make 2 DB trips
// per business object.
//boAsset.Domains = GetAssetDomains(asset.r.AssetId);
//boAsset.DomainEntries = GetAssetCustomDomains(asset.r.AssetId);
myListofObjects.Add(boAsset);
}
return myListofObjects;
Is there a better way?
Just add this .Include("Domains").Include("DomainEntries") to your Linq in in context.Security_Access That should get rows from those tables all in one go.
So your "inner" queries would look like:
from sa in context.Security_Access.Include("Domains").Include("DomainEntries")
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx) && g.UserID == userId
select new { ...
Here is the documentation from MS: http://msdn.microsoft.com/en-us/library/bb738708.aspx
If you want to improve your performance use compile queries !
You can check the example here.
static readonly Func<AdventureWorksEntities, Decimal,
IQueryable<SalesOrderHeader>> s_compiledQuery2 =
CompiledQuery.Compile<AdventureWorksEntities, Decimal, IQueryable<SalesOrderHeader>>((ctx, total) =>
from order in ctx.SalesOrderHeaders.Include("Orders") where order.TotalDue >= total select order);
MSDN
AND
You can Introduce Include suppose to select all the employees along with their departments . If you have a navigational property, you won't need a join at all. You can use Include like this:
List<Employee> employeesWithDepartments = CreateObjectSet<Employee>().
Include(e => e.Department).
ToList();

Linq to SQL joining two tables and populate GridView

I have two tables in my database, Building and Town. They look like this:
Building:
buildingid
buildingname
Town:
id
userid
buildingid
In Town there is one entry for each building a user has.
What i want is to populate a GridView for a user with a given userid. This GridView should include the buildingname and the number of buildings.
Building. I have tried this:
var buildings = (from Town in dc.Towns
join Building in dc.Buildings
on Town.buildingid equals Building.buildingid
select Building.buildingname);
gvBuildings.DataSource = buildings;
gvBuildings.DataBind();
But I don't know how to get the numbers for each building.
I have now been working on this for a while and a couple of your answers work. I have used this code:
var buildings = dc.Towns
.Where(t => t.userid == userid)
.GroupJoin(dc.Buildings,
t => t.buildingid,
b => b.buildingid,
(Town, Buildings) => new
{
BuildningName = Buildings.First().buildingname,
Count = Buildings.Count()
});
gvBuildings.DataSource = buildings.ToList();
gvBuildings.DataBind();
When i run this code my GridView ends up looking like this:
I need the buildings to be shown in groups, grouped by the buildingname. I have tried all of the suggestions but i cant get it to work.
Try grouping:
var buildings = dc.Towns
.Where(t => t.UserId == userId)
.GroupJoin(dc.Buildings,
t => t.BuildingId,
b => b.BuildingId,
(town, buildings) => new
{
BuildingName = buildings.First().BuildingName,
Count = buildings.Count
});
Keep in mind that when binding to a control you must supply a collection of type (or implementing) IList. This can be accomplished by calling ToList() on the buildings collection:
gvBuildings.DataSource = buildings.ToList();
gvBuildings.DataBind();
check linq differed execution
and than try the blow code might work for you
var buildings =
(from j in dc.Town
join i in dc.Buildings
on j.buildingId equals i.buildingId
where j.Userid = varUSerid
group new {i, j}
by new
{ i.BuildingID }
into
g
select new {
BuildingName = g.First<k=>k.BuildingName)
, count = g.Count() } ).ToList();
gvBuildings.DataSource = buildings;
gvBuildings.DataBind();
var buildings = (from Town in dc.Towns
join Building in dc.Buildings
on Town.buildingid equals Building.buildingid
into results
from r in results.DefaultIfEmpty()
group Town by new
{
r.BuildingId
} into groupedResults
where Town.UserID == parameteruserId
select new
{
BuildingName = Building.buildingname,
BuildingCount = groupedResults.Count()
});
Try this.. it should work.. i have a similar requirement..
manDbDataContext db = new DbDataContext();
var estimatedTotal = ( from est in db.AssignmentEstimatedMaterials
where est.assignment_id == Convert.ToInt32(Label_assignmentId.Text)
join materialdetail in db.Materials on est.material_id equals materialdetail.material_id
select new { est.qty,est.total_amount, materialdetail.material_name}).ToList();
GridView_estiamte_material.DataSource = estimatedTotal;
GridView_estiamte_material.DataBind();
Note, you should select individual data and it works.

Linq self-join query

I need some help with Linq self-join.
I have the following classs:
public class Owner
{
public string OwnerId {get;set;}
public string Name {get;set;}
public string Area {get;set;}
public string City {get;set;}
public string Sex {get;set;}
public List<Dog> dog {get;set;}
}
And table....
ID OwnerId OwnerName TypeId TypeName TypeValue TypeCodeId
1 1 John 1 Area United States 440
2 1 John 2 City Los-Angeles 221
3 1 John 3 Sex Female 122
4 2 Mary 1 Area Mexico 321
4 2 Mary 2 City Cancun 345
............................................................
I need to parse results of table1 into list of owners the fastest way possible.
Note: Type can be null, but I still need to show that owner (So, I assume left join should work).
Here's What I do. (owners is a webservice class that contains table1 results)
public IEnumerable<Owner> GetOwners() {
return (from owner in owners
join area in owners into owner_area
from oa in owner_area.DefaultIfEmpty()
join City in owners into owner_city
from oc in owner_city.DefaultIfEmpty()
join sex in owners into owner_sex
from os in owner_sex.DefaultIfEmpty()
where oa.TypeId == 1 && oc.TypeId ==2 && os.TypeId ==3
select new Owner() {OwnerId = owner.OwnerId,
Name = owner.Name,
Area = oa.TypeValue,
City = oc.TypeValue,
Sex = os.TypeValue}).Distinct();
}
This query has several issues:
It returns multiple results and distinct does not work as expected
I've tried to use GroupBy but it says that cannot implicitly convert Owner into IEnumerable <int, Owner>
It is super slow
How can I get distinct record with self join and improve performance?
Thanks
UPDATE:
Thank you guys for your ansewers, testing now, but I figured out that I forgot to supply one more thing. I've added a new column called TypeCodeId to the table layout(see above)
User can filter values based on their selection. So, I have TypeCodeId + TypeValue dictionaries for Area, City and Sex. All of those parameters are optional (If the user didn't select any, I just show them all records.
So, Assume that the user has selected filter Area: Unites States and filter City: Los Angeles
them my query would look like this:
Select Projects where Area equals United States(440) and City equals Los Angeles(221)
If Only Area:Mexico was selected then my query would read something like this:
Select Projects where Area equals Mexico(321)
I'm not sure how to do optional where clauses with what you've provided in the examples.
Since the table isn't normalized we need to get the distict users from the objects/table. This can be accomplished with:
owners.Select(o => new { o.OwnerId, o.OwnerName }).Distinct()
And then we need to join the "types" with two matching values, one for the ownerId and another for the specific type.
var ownerQuery =
from o in owners.Select(o => new { o.OwnerId, o.OwnerName }).Distinct()
join area in owners on new { o.OwnerId, TypeId = 1 } equals new { area.OwnerId, area.TypeId } into areas
from area in areas.DefaultIfEmpty()
join city in owners on new { o.OwnerId, TypeId = 2 } equals new { city.OwnerId, city.TypeId } into cities
from city in cities.DefaultIfEmpty()
join sex in owners on new { o.OwnerId, TypeId = 3 } equals new { sex.OwnerId, sex.TypeId } into sexes
from sex in sexes.DefaultIfEmpty()
select new
{
owner = o,
Area = (area != null) ? area.TypeValue : null,
City = (city != null) ? city.TypeValue : null,
Sex = (sex != null) ? sex.TypeValue : null,
};
You may need to change the projection in the above example.
For best performance if think this is the way to do it.
public IEnumerable<Owner> GetOwners(IEnumerable<Tuple<int, int>> filter)
{
var q = (from o in owners
join f in filter on
new {o.TypeId, o.TypeCodeId} equals
new {TypeId = f.Item1, TypeCodeId = f.Item2}
select o).ToList();
var dic = q.ToDictionary (o => new {o.OwnerId, o.TypeId}, o => o.TypeValue);
foreach (var o in q.Select(o => new { o.OwnerId, o.OwnerName }).Distinct())
{
var owner = new Owner()
{
OwnerId = o.OwnerId,
Name = o.OwnerName
};
string lookup;
if(dic.TryGetValue(new {o.OwnerId, TypeId = 1}, out lookup))
owner.Area = lookup;
if(dic.TryGetValue(new {o.OwnerId, TypeId = 2}, out lookup))
owner.City = lookup;
if(dic.TryGetValue(new {o.OwnerId, TypeId = 3}, out lookup))
owner.Sex = lookup;
yield return owner;
}
}
To get even a little more performance you can write an IEqualityComparer class that only compares int OwnerId and send it into the Distinct function

Categories