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 } );
Related
I struggled for hours to find a solution to this. I'm posting as a question and answer for the benefit of others who may come after me.
I wanted to make a cross join between two tables. For demonstration purposes, let's say I want to print a matrix of people and dishes, so I can fill in how much each person likes each food
var q = from p in db.People
from d in db.Dishes
select new
{
PersonID = p.ID,
PersonName = p.Name,
DishID = d.ID,
DishName = d.Name
};
But when I executed this query, I got an exception:
Unable to create a constant value of type 'MyDomain.Dish'. Only primitive types or enumeration types are supported in this context.
I searched all over for references to this error, and found lots of references to Contains() conditions... but I didn't have any Contains() conditions in my query.
I tried reversing the order of the tables:
var q = from d in db.Dishes
from p in db.People
...
And the error, too, got reversed!
Unable to create a constant value of type 'MyDomain.Person'. Only primitive types or enumeration types are supported in this context.
What's more, I discovered that if I first pulled my Person and Dish tables into memory using .ToList(), it worked fine, thus:
var q = from d in db.Dishes.ToList()
from p in db.People.ToList()
...
But it's very wasteful to load two entire tables into memory, when all I'm interested in is the ID and Name fields of each.
So how to make this cross join work in a single query?
The multiple references to the Contains() condition tipped me off that the first table was somehow trying to do a "contains" on the second one. So instead of doing a cross join, I did an inner join on a universally true condition:
var q = from p in db.People
join d in db.Dishes on 1 equals 1
select new
{
PersonID = p.ID,
PersonName = p.Name,
DishID = d.ID,
DishName = d.Name
};
Voila! EF is happy and the cross join comes back without any complaints.
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)
The columns names and types are identical, however it's coming from two separate entities. Here is a simplified example:
--Query View
var v_res = from s in db.V_CMUCUSTOMER
select s;
--Values from table less values from view
var res = from s in db.CMUCUSTOMERs
where !(from v in v_res
select v.ID).Contains(s.ID)
select s;
--join table and view values into one variable
var res_v_res = (from c in res
select c).Union(from v in v_res
select v);
I get the following error however:
Instance argument: cannot convert from 'System.Linq.IQueryable' to System.Linq.ParallelQuery
If you specify a new anonymous type and use ToList() for both then you should be able to Union them as follows :
var v_res = (from s in db.V_CMUCUSTOMER
select new { custName = s.customer_name custAddress = s.address}).ToList();
--Values from table less values from view
var res = (from s in db.CMUCUSTOMERs
where !(from v in v_res
select v.ID).Contains(s.ID)
select new { custName = s.customer_name custAddress = s.address }).ToList();
--join table and view values into one variable
var res_v_res = v_res.Union(res);
This may be onerous if there are dozens of columns but should still work.
When I run a similar query in LINQPad, I get an error message claiming that s in Contains(s.ID) is not defined in this context.
If I replace && with where all queries are successfully executed.
What would be an EF method syntax equivalent for the following TSQL query?
select istb.service_id, ss.service_desc, selected=1
from istb_services istb
inner join setup_services ss on istb.service_id=ss.service_id
where istb.istb_id=3
union
select ss.service_id, ss.service_desc, selected=0
from setup_services ss
where ss.service_id not in (select service_id from istb_services where istb_id=3)
I tried converting the not in part of the query like following:
var _existing = context.istb_services.Where(e => e.istb_id == IstbID);
var _others = context.setup_services.Except(_existing);
but it is generating compile-time error:
The best overloaded method match for 'System.Data.Objects.ObjectQuery.Except(System.Data.Objects.ObjectQuery)' has some invalid arguments
I understand I can't pass different type of ObjectQuery to the .Except method but then what would be the alternative code?
Thanks,
Try the following:
var resultA =
from istb in istb_services
join ss in setup_services on istb.service_id equals ss.service_id
where istb.istb_id == 3
select new { istb.service_id, ss.service_desc, selected = true };
var resultB =
from ss in setup_services
where !istb_services.Any(istb =>
istb.service_id == ss.service_id &&
istb.istb_id == 3)
select new { ss.service_id, ss.service_desc, selected = false };
var result = resultA.Union(resultB);
Anonymous type initializers having identical fields should be compiled to the same anonymous type, making the two sequences compatible for the Union operation.
In my Linq, I am trying to make an inner join to a nullable field. Employee and Department have a relation, Department may have an EmployeeID or may have a null. So what would be my join, if i want only the records that satisifed the inner join (no result for null EmployeeIDs):
var result = from emp in employees
join dept in departments
on new { Source = emp.EmployeeID }
equals new { Source = dept.EmployeeID };
I am getting an exception:
The type of one of the expressions in the join clause is incorrect.
Type Inference failed in a call to 'join'.
Thanks
To compare Int? and Int, append .Value to the nullable property:
var result = from emp in employees
join dept in departments
on new { Source = emp.EmployeeID }
equals new { Source = dept.EmployeeID.Value };
What if you reverse your join and put a little where in there?
var result = from department in departments
where department.EmployeeID != null
join employee in employees
on department.EmployeeID.Value equals employee.EmployeeID
select new { employee, department };
Found a useful answer from another link at https://social.msdn.microsoft.com/Forums/en-US/bf98ec7a-cb80-4901-8eb2-3aa6636a4fde/linq-join-error-the-type-of-one-of-the-expressions-in-the-join-clause-is-incorrect-type-inference?forum=linqprojectgeneral
To join multi-valued keys you need to construct an anonymous typ on both sides of the 'equals' that is the same type. The anonymous type initializer expression infers both type and name of members from the expression you supply. In your case the names of the members are different so the types end up being different so C# cannot figure out the common type between the two.
on new { VC.Make, VC.Model } equals new { MD.MakeID, MD.RefNum }
should be
on new { VC.Make, CV.Model } equals new { Make = MD.MakeID, Model = MD.RefNum }
Using the name = value syntax in the initializer you can specify the name the compiler uses when creating the type. If all members types & names are the same then the anonymous types are the same type.
Check the type on emp.EmployeeID and dept.EmployeeID. You might be missing a cast if they are different.
something like:
on new { Source = emp.EmployeeID }
equals new { Source = **(int)**dept.EmployeeID };
Looks like emp.EmployeeID is of type int and dept.EmployeeID is of type nullable<int>.
I had the same issue, where my charge_codes.CompanyId was nullable but my order_items.CompanyId was NOT nullable.
So I had to get my charge codes into their own ananomous type and make it not be nullable.
var chargeCodes = from s in db.Charge_Codes
where s.CompanyID != null
select new { CompanyID = (int)s.CompanyID,
Charge_CodeID = s.Charge_CodeID,
Revenue_Code_Id = (int)s.Revenue_CodeID, };
//now my chargeCodes contains an anonymous with a non nullable CompanyID and
//a non nullable Revenue_CodeID
//use chargeCodes here
var query = from oi in db.Order_Items
join cc in chargeCodes on
new {oi.CompanyID, oi.Charge_CodeID} equals new {cc.CompanyID, cc.Charge_CodeID}
In my scenario I had this error on a join using multiple columns. The property names were different and one of them was also nullable. All I did was creating a name to those properties and adding the ".Value" on the nullable value so the LINQ Join could correctly associate those properties.
var query = from y in Context.Table1
join y in Context.Table2 on new { Field1 = x.Field1.Value, Field2 = x.Field2 }
equals new { Field1 = y.Field1DiffName, Field2 = y.Field2 }
I hope it helps whoever is facing this issue.