C# Linq : Group By different columns of different entity - c#

I have three entities ClassC, ClassS and ClassSA. I want to apply group by using LINQ.
using (var db = new SEntities())
{
var result = from c in db.ClassC
join s in db.ClassS on c.ID equals s.CID
join sa in db.ClassSA on s.SAID equals sa.ID
group sa by
new { c.Type, s.Date.Year}
into g
select new ClassSAY { Year = g.Key.Year, CI = g.Key.CIType, Count = g.Count(sa => sa.ID)};
}
I want to join ClassC, ClassS and ClassSA; group by two different properties of two different classes and store some part of result in another class's properties. I have searched by found group by only one class' properties.
It's giving error:
Cannot convert lambda expression to delegate type 'System.Func<SG.DAL.SA,bool>' because some of the return types in the block are not implicitly convertible to the delegate return type
Cannot implicitly convert type 'long' to 'bool'
Thanks.

Try to use like
Count = g.Count()
instead of
Count = g.Count(sa => sa.ID)

Related

What is the equivalent of this SQL statement in Linq?

I need to port this SQL statement to LINQ:
SELECT f.ID as IdFlight,
Tarif * 1 as Tarif,
f.Time, f.TimeOfArrival,
sl.Name as FromLoc,
sl.Country as FromCountry,
sl.Airport as FromAirport,
dl.Name as ToLoc,
dl.Country as ToCountry,
dl.Airport as ToAirport
FROM Flights as f
INNER JOIN Locations as sl ON sl.ID = f.ID_Source
INNER JOIN Locations as dl ON dl.ID = f.ID_Destination
INNER JOIN FlightsTarifs as ftf ON f.Id = ftf.IDFlight
WHERE f.ID_Destination =30005 AND f.Time <= DATEADD(day,4,'2018/05/24 00:00')
AND f.Time >= '2018/05/24 00:00' ORDER By f.Time, Tarif
My attempt in Linq:
IQueryable qinfo = from f in context.Flights
join sl in context.Locations on f.Id_Source equals sl.ID
join dl in context.Locations on f.Id_Destination equals dl.ID
join ftf in context.FlightsTarifs on f.ID equals ftf.IDFlight
where (f.Id_Source == aFormUser.FlightSrcID)
where (f.Id_Destination == aFormUser.FlightDestID)
where (f.Time.Date >= aFormUser.DepartureDate.Date)
where (f.Time.Date <= aFormUser.DepartureDate.Date.AddDays(4))
orderby f.Time, ftf.Tarif
select new {f.ID, ftf.Tarif, f.Time, f.TimeOfArrival,
sl.Name, sl.Country, sl.Airport,
dl.Name, dl.Country, dl.Airport };
I have some problems to solve now:
Since I am joining the table flights with the table locations twice, in order to get the name of source and of destination locations, doing this in LinQ causes a compiler error, that says dl.Name, dl.Country, dl,Airport are anonymous types and they would end having same name as the others sl.Name, sl.Country, sl.Airport.
I cannot use the "As" expression as I do in Sql or is there any Equivalent one in Linq?
I cannot multiply Tarif by the number of passengers while i am in the linq query, while it does not allow me to do this.
You can use the aliases with the new object initializer with the code below, which will also support multiplying the tarif:
select new {
f.ID,
Tarif = ftf.Tarif * 1, // Alias and multiply by your number
f.Time,
f.TimeOfArrival,
SourceName = sl.Name, // Alias
SourceCountry = sl.Country, // Alias
SourceAirport = sl.Airport, // Alias
DestName = dl.Name, // Alias
DestCountry = dl.Country, // Alias
DestAirport = dl.Airport // Alias
};
Just to provide a few more details in case others stumble on this, the root cause is that the code was using the new keyword to define an anonymous type with an object initializer that ran into multiple conflicts trying to define the anonymous class (multiple properties with same inferred name, and then unable to name property from expression when tarif was multiplied).
By explicitly naming the properties with conflicts, the compiler no longer had to infer the naming that generated the conflicts.
More: http://geekswithblogs.net/BlackRabbitCoder/archive/2012/06/21/c.net-little-wonders-the-joy-of-anonymous-types.aspx
The link above has some additional examples on how to use the object initializer with anonymous types.
This concept is called Projection, you have to select new anonymous type or alias according to your requirement.
Sample:
var result = data.Select( x => new { FieldName = x.Property } );

How to join 2 tables using linq but avoid anonymous object

I want to join 2 tables using linq and avoid anonymous object.
So far i use tuple.
var products = from tm in db.TargetMarkets
join tp in db.Products on tm.product_id equals tp.id
where tm.country == 2
select Tuple.Create<TargetMarket, Product>(tm, tp);
But, when i foreach
foreach (var p in products)
{
var a = p.Item1.id;
}
It throws an exception
LINQ to Entities does not recognize the method 'System.Tuple`2
Question:
Is there a way to keep my code strongly typed
What's wrong with tuple (optional)
Is there a way to keep my code strong type
You can define a new type and make object of that type instead of anonymous object.
class ProductTargetMarket
{
//Attributes
}
var ProductsTargetMarkets = from tm in db.TargetMarkets
join tp in db.Products on tm.product_id equals tp.id
where tm.country == 2
select new ProductTargetMarket{Attribute1OfProductTargetMarket = tp.Attribute1, Attribute1OfProductTargetMarket = tm.Attribute1 };
To create a tuple you may first convert it to anonymous type and then convert it to tuple, see this this and this post.

linq - format string from two fields in select new context

I have foreign table with one to many relation. I write linq query as left join equivalent and implement group by relevant id field.
from p in db.personal join pn in
(from t in db.phoneNumbers
group t by t.personID into g
select new { id = g.Key,
number = g.Select(t => t.number),
prefix = g.Select(t => t.prefix)
}).AsEnumerable()
on p.ID equals pn.id
into lPN from lpn in lPN.DefaultIfEmpty()
//join wsd in db.basicOperations on p.ID equals wsd.personID
where p.ID == id.Value
select new partialPersonDetailsViewModel()
{
id = id.Value,
genderType = p.genderType,
sPhoneNumbers="(" +lpn.prefix+") "+lpn.number
}).FirstOrDefault();
But at sPhoneNumbers="(" +lpn.prefix+") "+lpn.number this place VS notify me about error:
Error 1 Operator '+' cannot be applied to operands of type 'System.Collections.Generic.IEnumerable' and 'System.Collections.Generic.IEnumerable'
Please help me pass this error and solve problem.
Both of lpn.prefix and lpn.number are evaluated as g.Select(...), so in fact they are IEnumerable<T> (exactly as error message declares it).
Evaluating them as g.Select(...).FirstOrDefault() should help you since it will extract value of type T from IEnumerable<T>.
Update:
In your case, when lpn.prefix and lpn.number are actually lists of values and you need to concatenate these lists - you can use something like:
sPhoneNumbers = String.Join("; ",
lpn.prefix.Select((p, i) =>
String.Format("({0}){1}",
p,
lpn.numbers.Skip(i).Take(1).FirstOrDefault())));
Or you can use Enumerable.Zip method, as it was suggested in comments by #Chris:
sPhoneNumbers = String.Join("; ",
lpn.prefix.Zip(lpn.numbers,
(s, s1) => string.Format("({0}){1}", s, s1)));

Error returning List<T> in method

I am using Entity Framework and LinQ using get List from two tables. This is my code:
public List<tbl_KidsMagazines> km_GetListJoins()
{
var resultsList = (from s in AgEntities.tbl_KidsMagazines
join sa in AgEntities.tbl_UserMaster
on s.CreatedBy equals sa.User_IndexID
select new { s, sa }).ToList();
return resultsList;
}
But I am getting the below error:
Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<Solution.Bussines.Entities.tbl_KidsMagazines>
You are creating a list of anonymous types but your return type is List<tbl_KidsMagazines>, so you must create a list of tbl_KidsMagazines instead of anonymous types in your query.Something like this should work:
var resultsList = (from s in AgEntities.tbl_KidsMagazines
join sa in AgEntities.tbl_UserMaster
on s.CreatedBy equals sa.User_IndexID
select new { s, sa })
.AsEnumerable()
.Select(x => new tbl_KidsMagazines { // set properties })
.ToList();
Set the return type to an object.
The return type of your method is List<tbl_KidsMagazines>
while in code you are returning Mix of two tables whose type has not been declared anywhere i.e. it is an anonymous type.

LINQ-to-entities generic == workaround

I have a following LINQ-to-entities query
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var q = (from h in ItemHistory
where h.OperationId ==
(from h1 in ItemHistory
where h1.GenericId == h.GenericId
select h1.OperationId).Min()
select h);
return q;
}
ItemHistory is a generic query. It can be obtained in the following way
var history1 = MyEntitiySet1.Select(obj =>
new History<long>{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
new History<string>{ obj.OperationId, GenericId = obj.StringId });
In the end of all I want a generic query being able to work with any entity collection convertible to History<T>.
The problem is the code does not compile because of GenericId comparison in the inner query (Operator '==' cannot be applied to operands of type 'T' and 'T').
If I change == to h1.GenericId.Equals(h.GenericId) I get the following NotSupportedException:
Unable to cast the type 'System.Int64' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.
I've tried to do grouping instead of subquery and join the results.
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var grouped = (from h1 in ItemHistory
group h1 by h1.GenericId into tt
select new
{
GenericId = tt.Key,
OperationId = tt.Min(ttt => ttt.OperationId)
});
var q = (from h in ItemHistory
join g in grouped
on new { h.OperationId, h.GenericId }
equals new { g.OperationId, g.GenericId }
select h);
return q;
}
It compiles because GenericId's are compared with equals keyword and it works but the query with real data is too slow (it has been running for 11 hours on dedicated postgresql server).
There is an option to build a whole expression for the outer where statement. But the code would be too long and unclear.
Are there any simple workarounds for equality comparison with generics in LINQ-to-entities?
Try this, I think it should accomplish what you want without the extra query/join
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var q = from h in ItemHistory
group h by h.GenericId into tt
let first = (from t in tt
orderby t.GenericId
select t).FirstOrDefault()
select first;
return q;
}
IQueryable<History<T>> GetFirstOperationsForEveryId<T>
(IQueryable<History<T>> ItemHistory)
{
var grouped = (from h1 in ItemHistory
group t by h1.GenericId into tt
select new
{
GenericId = tt.Key,
OperationId = tt.Min(ttt => ttt.OperationId)
});
var q = (from h in ItemHistory
join g in grouped
on new { h.OperationId, h.GenericId }
equals new { g.OperationId, g.GenericId }
select h);
return q;
}
You could also set a generic constraint on T for an IItemHistory inteface that implements the GenericId and OperationId property.
My question already contains a solution. The second method with group + join works well if the table is properly indexed. It takes 3.28 seconds to retrieve 370k rows from the database table. In fact in non-generic variant the first query is slower on postgresql than the second one. 26.68 seconds vs 4.75.

Categories

Resources