How to join 2 tables using linq but avoid anonymous object - c#

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.

Related

#C Razor Join Data LINQ

Attempting to display a table with a join on the Rating
to the ID within the Ratings table. I'm hitting an issue with the join within my statement.
var Movie = from u in _context.Movie
join g in _context.Ratings
on u.Rating equals g.ID
select (a => new List
{
Ratings = u.Rating,
MovieRating = g.MovieRating
});
Movie = await Movie.ToListAsync();
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
You have some weird syntax like (a => new List { ... Not sure where you are going with that.
Do you have a class named List? are you trying to get a List? Why is "a" in there...since it is not referenced anywhere. You also try to change the type of the Movie variable in your code, the var keyword doesn't work that way. On the first line, you assign a IQueryable< T> to it and later on you try to assign a List< T> to it.
try something like this:
var movieQuery = from u in _context.Movie
join g in _context.Ratings on u.Rating equals g.ID
select new
{
Ratings = u.Rating,
MovieRating = g.MovieRating
};
var movies = await movieQuery.ToListAsync(); // query will be executed here
The example code above uses an anonymous class as result so movies will be of type List< T> where T is an anonymous class, if you do have a class called List (for some reason) representing a single result item, then use
select new List { ...
instead of
select new { ...
and if you don't wanna split the query / actual list into 2 variables, you can do something like:
var movies = await (from u in _context.Movie
join g in _context.Ratings on u.Rating equals g.ID
select new
{
Ratings = u.Rating,
MovieRating = g.MovieRating
}).ToListAsync();

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 } );

Unable to cast anonymous list into known list in LINQ

I have a LINQ Query which returns me results from one table. I need to convert it into the List of that table model. The anonymous type of groupManager is List<a> wherea is {Group g5}
var groups = new List<Group>();
var groupManager = (from a in db.AUsers
join b in db.BUsers on a.Id equals b.UserID into group1
from g1 in group1.DefaultIfEmpty()
join c in db.UserRoles on g1.ID equals c.UserID into group2
from g2 in group2.DefaultIfEmpty()
join d in db.Roles on g2.RoleID equals d.ID into group3
from g3 in group3.DefaultIfEmpty()
join e in db.RoleGroups on g3.ID equals e.RoleID into group4
from g4 in group4.DefaultIfEmpty()
join f in db.Groups on g4.GroupID equals f.ID into group5
from g5 in group5.DefaultIfEmpty()
where a.Id == user.ID && g5.Name != ""
select new{ Group = g5}).ToList();
groups = groupManager.Cast<Group>().ToList();
This code does not seem to work.The error I am getting is {"Unable to cast object of type '<>f__AnonymousType11`1[Group]' to type 'Group'."} Am I missing something?
hmmm... did you try this?
select new Group(g5)).ToList();
or this
select g5).ToList();
hard to say more without knowing anything about the group object or the other types in your example.
You can likely do this without the anonymous type or the cast.
var groups = (from a in db.AUsers
// Your query...
select new Group
{
// Properties of Group
Name = g5.Name,
AnotherProperty = g5.AnotherProperty
}).ToList();
Provided answers were absolutely correct, but I guess it could be good to point one subtle thing out regarding Cast method (and to answer "Am I missing something" part).
Cast method really serves different purpose than casting all objects in list to another type. No surprise that compiler throws this exception since what Cast does is taking IEnumerable (non generic version) and returning the same collection, but with generic IEnumerable<T>. This is one of two methods in LINQ that are taking non generic IEnumerable as an argument (the second is OfType), so you could have a collection of given type, cast it to IEnumerable<T> and use other LINQ methods which require IEnumerable<T> as an argument.
Just look at the source code
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) {
IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
if (typedSource != null) return typedSource;
if (source == null) throw Error.ArgumentNull("source");
return CastIterator<TResult>(source);
}
and in CastIterator
foreach (object obj in source) yield return (TResult)obj;
so your collection should be able to be casted to given type T in order to Cast to work. It won't cast your anonymous types to concrete types.
If you select the actual object then that's what you get back as an IEnumerable of Group:
var groups = new List<Group>();
var groupManager = (from a in db.AUsers
join b in db.BUsers on a.Id equals b.UserID into group1
from g1 in group1.DefaultIfEmpty()
join c in db.UserRoles on g1.ID equals c.UserID into group2
from g2 in group2.DefaultIfEmpty()
join d in db.Roles on g2.RoleID equals d.ID into group3
from g3 in group3.DefaultIfEmpty()
join e in db.RoleGroups on g3.ID equals e.RoleID into group4
from g4 in group4.DefaultIfEmpty()
join f in db.Groups on g4.GroupID equals f.ID into group5
from g5 in group5.DefaultIfEmpty()
where a.Id == user.ID && g5.Name != ""
select g5).ToList()
groups = groupManager;
The .ToList() will then convert it to a list. No need to create a dynamic object and then cast.

C# Linq : Group By different columns of different entity

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)

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