Get properties from second (Right) joined entity with Group by - c#

please I'm working on a ASP.NET MVC project with Entity Framework.
I try to write a Linq Query that give me some data, this Query will join two entities and grouping by data, so the issue is when I try to get the properties of second joined entity I don't see them in Intellisense, I need these properties to select them.
What I try :
var R = (from N in SCHOOL_DB_Context.Con.NT_CTR join S in SCHOOL_DB_Context.Con.STGs on N.STG_nt equals S.CD_STG where N.CTR_nt==CTR group N by N.STG_nt into G select new NT_CTR_Anal { /*Here where I want to select some properties from second entity*/ } )
So please any help about this issue ?

Take a look at tis fragment:
group N by N.STG_nt into G
The part between by and into is the key(s), and similar to SQL there you have access to all aliases (variables) from from and join clauses. The name after into is LINQ specific and represents the alias (variable) for accessing the GroupBy result. But what is between group and 'by'? There is no SQL equivalent.
Well, the result of GroupBy is of type IGrouping<TKey, TElement>, which has property TKey Key and also is IEnumerable<TElement>. The TKey is comong from the expression between by and into, while TElement (which is what you can access through into variable is coming from the expression between group and by.
In your sample you've put N there, that`s why you have access only to its properties.
In order to have access to other properties, you would use the typical LINQ construct for "composite" things, which is anonymous type projection, e.g.
var R = (
from N in SCHOOL_DB_Context.Con.NT_CTR
join S in SCHOOL_DB_Context.Con.STGs on N.STG_nt equals S.CD_STG
where N.CTR_nt == CTR
group new { N, S } by N.STG_nt into G
select new NT_CTR_Anal
{
G.Key, // N.STG_nt
SomeNPropSum = G.Sum(e => e.N.SomeNProp),
SomeSPropSum = G.Sum(e => e.S.SomeSProp),
};
As you can see, now you have access to both N and S properties inside grouping aggregate methods.

Related

C# Linq Group by Object

I have an issue of using group by in LINQ to SQL statement.
The cod I have is
var combinedItems = (from article in articles
join author in authors
on article.AuthorId equals author.Id into tempAuthors
from tempAuthor in tempAuthors.DefaultIfEmpty()
select new { article , author = tempAuthor});
var groups1 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article into g
select g.Key).ToList();
var groups2 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article.Id into g
select g.Key).ToList();
I tried to group in two different ways. The first way, I group by an object and the second way I just group by a field in one of the objects.
When I run groups1, I got an error saying need to evaluate in client side, while when I use groups2, it works all good. Can I ask what could be wrong? If I want to group by object, is there any way to do it?
In case you want to group by object, as you've not overridden Equals and GetHashCode() in your Article class or implemented IEqualityComparer<Article> you're just getting the default comparison, which checks if the references are equal. So what you need is something like this:
class GroupItemComparer : IEqualityComparer<Article>
{
public bool Equals(Article x, Article y)
{
return x.Id == y.Id &&
x.Name == y.Name;
}
public int GetHashCode(Article obj)
{
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode();
}
}
And then you need to change your query to lambda expression:
var groups1 = combinedItems.GroupBy(c => c.article , new GroupItemComparer())
.Select(c => c.Key).ToList();
In case you got any exception regarding translation your method to SQL, you can use AsEnumerable or ToList methods before your GroupBy method, with this methods after data is loaded, any further operation is performed using Linq to Objects, on the data already in memory.
As others have pointed out, the GroupBy is using reference equality by default, and you could get around it by specifying one or more properties to group by. But why is that an error?
The whole point of the query is to translate your Linq query into SQL. Since object reference equality on the client can't be easily translated to SQL, the translator doesn't support it and gives you an error.
When you provide one or more properties to group by, the provider can translate that to SQL (e.g. GROUP BY article.Id), and thus the second method works without error.

Linq query for Where on the Joined table without needing join

Trying to get a linq query (or lambda syntax) for the following SQL which Selects all "Data" which in the joining table have an Attribute equal to "blob".
EXCEPT: without explictly using the Join, but the
select data.*
from data
join settings on data.DataID = settings.DataID
where settings.Attribute = 'blob'
Explicitly defining the join
from d in dbcontext.Data
join s in dbcontext.Settings on d.DataID equals s.DataID
where s.Attribute == "blob"
select d
but is there a way to use the context dbcontext.Data.Settings
like the following?
from d in dbcontext.Data
where d.Settings.Attribute == "blob"
select d
Settings is a collection Type, so things like .Contains, and .Where come to mind.
using .Contains, my understanding is i would need to pass in an object type
where d.Settings.Contains(new Settings(d.DataID, "blob", null))
but i dont care about the null (Value) matching, just column Settings
some table structures
Data
DataID
Name
Settings
DataID
Attribute
Value
As I understand, you have Settings collection navigation property, so instead of explicit join you could simply use it ("navigate"):
from d in dbcontext.Data
from s in d.Settings
where s.Attribute == "blob"
select d
Alternatively you could use Any extension method which in this case is more appropriate than Contains (although Contains can also be used, but needs to be combined with Select):
dbcontext.Data.Where(d => d.Settings.Any(s => s.Attribute == "blob"))
For completeness, here is the Contains version:
dbcontext.Data.Where(d => d.Settings.Select(s => s.Attribute).Contains("blob"))
If I understand your question correctly, you want to create a LINQ that will grab any DataID that has an attribute of of "Blah" that is stored in another table.
If so this may work.
var dataIDs = Setting.Where(entry => entry.Attribute == "Blah")
.Select(entry => entry.DataID); // gets all DataIDs that match the attribute
var data = Data.Where(entry => entry.DataID in dataIDs); // gets data info based on DataIDs.
It should work, but what you should do instead is do an left join somewhat like
select a.*
from data a
left join settings b
on a.DataID = b.DataID
where b.Attribute = 'blob'
but in LINQ. This query would allow you to fetch all the data for DataIDs that match attribute 'blob. I haven't done it in LINQ so if someone more familiar with left joins with linq could respond that might work better

Counting in a Linq Query

I have a fairly complicated join query that I use with my database. Upon running it I end up with results that contain an baseID and a bunch of other fields. I then want to take this baseID and determine how many times it occurs in a table like this:
TableToBeCounted (One to Many)
{
baseID,
childID
}
How do I perform a linq query that still uses the query I already have and then JOINs the count() with the baseID?
Something like this in untested linq code:
from k in db.Kingdom
join p in db.Phylum on k.KingdomID equals p.KingdomID
where p.PhylumID == "Something"
join c in db.Class on p.PhylumID equals c.PhylumID
select new {c.ClassID, c.Name};
I then want to take that code and count how many orders are nested within each class. I then want to append a column using linq so that my final select looks like this:
select new {c.ClassID, c.Name, o.Count()}//Or something like that.
The entire example is based upon the Biological Classification system.
Assume for the example that I have multiple tables:
Kingdom
|--Phylum
|--Class
|--Order
Each Phylum has a Phylum ID and a Kingdom ID. Meaning that all phylum are a subset of a kingdom. All Orders are subsets of a Class ID. I want to count how many Orders below to each class.
select new {c.ClassID, c.Name, (from o in orders where o.classId == c.ClassId select o).Count()}
Is this possible for you? Best I can do without knowing more of the arch.
If the relationships are as you describe:
var foo = db.Class.Where(c=>c.Phylum.PhylumID == "something")
.Select(x=> new { ClassID = x.ClassID,
ClassName = x.Name,
NumOrders= x.Order.Count})
.ToList();
Side question: why are you joining those entities? Shouldn't they naturally be FK'd, thereby not requiring an explicit join?

Trying to create some dynamic linq

I'm trying to create a linq query based on some dynamic/optional arguments passed into a method.
User [Table] -> zero to many -> Vehicles [Table]
User [Table] -> zero to many -> Pets
So we want all users (including any vechile and/or pet info). Optional filters are
Vehicle numberplate
Pet name
Because the vehicle and pet tables are zero-to-many, i usually have outer joins between the user table and the vehicle|pet table.
To speed up the query, i was trying to create the dynamic linq and if we have an optional argument provided, redfine the outer join to an inner join.
(The context diagram will have the two tables linked as an outer join by default.)
Can this be done?
I'm also not sure if this SO post can help me, either.
I think you are heading in the wrong direction. You can easily use the fact that LINQ queries are composable here.
First, you would always use the outer join, and get all users with the appropriate vehicles and pets:
// Get all the users.
IQueryable<User> users = dbContext.Users;
Then you would add the filters if necessary:
// If a filter on the pet name is required, filter.
if (!string.IsNullOrEmpty(petNameFilter))
{
// Filter on pet name.
users = users.Where(u => u.Pets.Where(
p => p.Name == petNameFilter).Any());
}
// Add a filter on the license plate number.
if (!string.IsNullOrEmpty(licensePlateFilter))
{
// Filter on the license plate.
users = users.Where(
u => u.Cars.Where(c => c.LicensePlace == licensePlateFilter).Any());
}
Note that this will not filter out the pets or cars that don't meet the filter, as it is simply looking for the users that have pets with that name, or cars with that plate.
If you are trying to change tables or joins of a LINQ to SQL query at runtime you need to do that with reflection. LINQ expressions are not special; same as working with any other object call - you can change the value of properties and variables at runtime, but choosing which properties to change or which methods to call requires reflecting.
I would add to that by pointing out dynamically creating LINQ expressions via reflection is probably a little silly for most (all?) cases, since under the hood the expression is essentially reflected back into SQL statements. Might as well write the SQL yourself if you are doing it on-the-fly. The point of LINQ is to abstract the data source from the developer, not the end-user.
This is how I do what you are asking...
var results = u from dc.Users
join veh from dc.vehicles on u.userId equals v.userId into vtemp from v in vtemp.DefaultIfEmpty()
join pet from dc.pets on u.userId equals p.userId into ptemp from p in ptemp.DefaultItEmpty()
select new { user = u, vehicle = v, pet = p };
if ( !string.IsNullOrEmpty(petName) )
{
results = results.Where(r => r.pet.PetName == petName);
}
if ( !string.IsNullOrEmpty(licNum) )
{
results = results.Where(r => r.vehicle.LicNum == licNum);
}

How to do joins in LINQ on multiple fields in single join

I need to do a LINQ2DataSet query that does a join on more than one field (as
var result = from x in entity
join y in entity2
on x.field1 = y.field1
and
x.field2 = y.field2
I have yet found a suitable solution (I can add the extra constraints to a where clause, but this is far from a suitable solution, or use this solution, but that assumes an equijoin).
Is it possible in LINQ to join on multiple fields in a single join?
EDIT
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
is the solution I referenced as assuming an equijoin above.
Further EDIT
To answer criticism that my original example was an equijoin, I do acknowledge that, My current requirement is for an equijoin and I have already employed the solution I referenced above.
I am, however, trying to understand what possibilities and best practices I have / should employ with LINQ. I am going to need to do a Date range query join with a table ID soon, and was just pre-empting that issue, It looks like I shall have to add the date range in the where clause.
Thanks, as always, for all suggestions and comments given
var result = from x in entity
join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
var result = from x in entity1
join y in entity2
on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
You need to do this, if the column names are different in two entities.
The solution with the anonymous type should work fine. LINQ can only represent equijoins (with join clauses, anyway), and indeed that's what you've said you want to express anyway based on your original query.
If you don't like the version with the anonymous type for some specific reason, you should explain that reason.
If you want to do something other than what you originally asked for, please give an example of what you really want to do.
EDIT: Responding to the edit in the question: yes, to do a "date range" join, you need to use a where clause instead. They're semantically equivalent really, so it's just a matter of the optimisations available. Equijoins provide simple optimisation (in LINQ to Objects, which includes LINQ to DataSets) by creating a lookup based on the inner sequence - think of it as a hashtable from key to a sequence of entries matching that key.
Doing that with date ranges is somewhat harder. However, depending on exactly what you mean by a "date range join" you may be able to do something similar - if you're planning on creating "bands" of dates (e.g. one per year) such that two entries which occur in the same year (but not on the same date) should match, then you can do it just by using that band as the key. If it's more complicated, e.g. one side of the join provides a range, and the other side of the join provides a single date, matching if it falls within that range, that would be better handled with a where clause (after a second from clause) IMO. You could do some particularly funky magic by ordering one side or the other to find matches more efficiently, but that would be a lot of work - I'd only do that kind of thing after checking whether performance is an issue.
Just to complete this with an equivalent method chain syntax:
entity.Join(entity2, x => new {x.Field1, x.Field2},
y => new {y.Field1, y.Field2}, (x, y) => x);
While the last argument (x, y) => x is what you select (in the above case we select x).
I think a more readable and flexible option is to use Where function:
var result = from x in entity1
from y in entity2
.Where(y => y.field1 == x.field1 && y.field2 == x.field2)
This also allows to easily change from inner join to left join by appending .DefaultIfEmpty().
var result = from x in entity
join y in entity2
on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
select new
{
/// Columns
};
you could do something like (below)
var query = from p in context.T1
join q in context.T2
on
new { p.Col1, p.Col2 }
equals
new { q.Col1, q.Col2 }
select new {p...., q......};
Using the join operator you can only perform equijoins. Other types of joins can be constructed using other operators. I'm not sure whether the exact join you are trying to do would be easier using these methods or by changing the where clause. Documentation on the join clause can be found here. MSDN has an article on join operations with multiple links to examples of other joins, as well.
If the field name are different in entities
var result = from x in entity
join y in entity2 on
new {
field1= x.field1,
field2 = x.field2
}
equals
new {
field1= y.field1,
field2= y.myfield
}
select new {x,y});
As a full method chain that would look like this:
lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
(a, b) => new ResultItem
{
Id = a.Id,
ATotal = a.Total,
BTotal = b.Total
}).ToList();
I used tuples to do that, this is an example for two columns :
var list= list1.Join(list2,
e1 => (e1.val1,e1.val2),
e2 => (e2.val1,e2.val2),
(e1, e2) => e1).ToList();
from d in db.CourseDispatches
join du in db.DispatchUsers on d.id equals du.dispatch_id
join u in db.Users on du.user_id equals u.id
join fr in db.Forumreports on (d.course_id + '_' + du.user_id) equals (fr.course_id + '_'+ fr.uid)
this works for me
Declare a Class(Type) to hold the elements you want to join. In the below example declare JoinElement
public class **JoinElement**
{
public int? Id { get; set; }
public string Name { get; set; }
}
results = from course in courseQueryable.AsQueryable()
join agency in agencyQueryable.AsQueryable()
on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper }
equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

Categories