I am trying to use linq with Entity Framework. In below code first join is red squiggle and I have this error.
Severity Code Description Project File Line Suppression State
Error CS1941 The type of one of the expressions in the join clause is
incorrect. Type inference failed in the call to 'Join'
Code
var vals = (from o in db.Words
join r in db.Results
on o.Id equals r.root
join s in db.Senses on r.Id equals s.results_id
select o ).Take(10) ;
EDIT:
After comments and realized my mistaken code I have decided add my entity class.
When I run my project then entity framework creates Id and root_Id columns on Sql Server then I considered I can use Resultset.root equals Words.Id structure
public class Word{
[Key]
public int Id { get; set; }
...}
public class Result{
[Key]
public int Id { get; set; }
public virtual Word root { get; set; }
...}
public class Result{
[Key]
public int Id { get; set; }
public virtual Result result { get; set; }
...}
And Context class
public class DatabaseContext : DbContext{
...
public DbSet<Word> Words { get; set; }
public DbSet<Result> Results { get; set; }
public DbSet<Sens> Senses { get; set; }
...
}
Id and root isnot compatible types but when I change the query with compatible ones problem is continuing. What do I go wrong. Thanks. Greetings
r.Id and s.Id have different types. Check them.
Maybe they are int and long or something else.
From #OzanTopal:
For more information check this link: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs1941
You are joining 3 tables but the result is just:
select o?
What is the purpose of your join?
Check the answer above or check also this if it is different types:
on o.Id equals r.root
on r.Id equals s.Id
r.val== SomeVal
This error is because the Id properties of Sense and Result types are incompatible. They don't have to be same. For example, its Okay (from compiler's perspective) for one to be int and another to be long. However, for example, string and int or Guid or int would be incompatible. The solution is to make both types compatible if not same. I understand that you may not be able to do so since your entity model may be built from the existing database and you may not have authority or may not want to change the underlying type of the columns.
If you want to solve the compilation problem in the code itself then you will have to do explicit type casting:
var vals = (from o in db.Words
join r in db.Results
on o.Id equals r.root
join s in db.Senses on r.Id equals Convert.ToString(s.Id)
where r.val== SomeVal
select o ).Take(10);
This assumes that the Id property of the Result class is string while the Id of the Sense class is some numeric type.
Related
I'm trying to replicate a SQL statement in EF Core but cant seem to find a way to do it, to set the scene I have the following table structure
Slot -> SlotInstance -> SlotInstanceUser
(a Slot can have many SlotInstances, a SlotInstance can have many SlotInstanceUsers)
When a user registers for a SlotInstance a record is created in SlotInstanceUsers storing the SlotInstanceId and UserId - all good there.
I'm able to write SQL to get a list of slot instances which the user has not registered for e.g.
SELECT
S.StartDate, S.EndDate, S.StartTime, S.EndTime, S.DayOfWeek,
SI.Date
FROM
Slot S WITH (NOLOCK)
INNER JOIN
SlotInstance SI WITH (NOLOCK) ON S.Id = SI.SlotId
WHERE
SI.ID not in (
SELECT
SlotInstanceId
FROM
SlotInstanceUser SIU WITH (NOLOCK)
WHERE
SIU.UserId = #UserID
)
ORDER BY
SI.Date
But I just cant seem to replicate this in EF core - what am I missing?
You can write the LINQ query pretty much the same way as the SQL query. Just remember that in LINQ select is last, variables (aliases) are mandatory, and the equivalent of SQL NOT IN is !Contains. e.g.
var query =
from s in db.Slots
join si in db.SlotInstances on s.Id equals si.SlotId
where !(from siu in db.SlotInstanceUsers
where siu.UserId == userId)
select siu.SlotInstanceId).Contains(si.Id)
orderby si.Date
select new
{
s.StartDate, s.EndDate, s.StartTime, s.EndTime, s.DayOfWeek,
si.Date
};
But in EF Core you have more options, especially for joins, since normally the relationships (and associated joins) are encapsulated with navigation properties. So the model you are describing with words in EF Core/C# terms is something like
public class Slot
{
public int Id { get; set; }
// Other properties...
public ICollection<SlotInstance> SlotInstances { get; set; }
}
public class SlotInstance
{
public int Id { get; set; }
// Other properties...
public Slot Slot { get; set; }
public ICollection<SlotInstanceUser> SlotInstanceUsers { get; set; }
}
public class SlotInstanceUser
{
public int Id { get; set; }
// Other properties...
public SlotInstance SlotInstance { get; set; }
}
and the query would be like
var query =
from s in db.Slots
from si in s.SlotInstances
where !si.SlotInstanceUsers.Any(siu => siu.UserId == userId)
orderby si.Date
select new
{
s.StartDate, s.EndDate, s.StartTime, s.EndTime, s.DayOfWeek,
si.Date
};
(this actually translates to SQL NOT EXISTS, but that's not essential).
And if you don't need projection, but simply slot instances (with slot info) which the user has not registered for, then it would be simply
var query = db.SlotInstances
.Include(si => si.Slot)
.Where(si => !si.SlotInstanceUsers.Any(siu => siu.UserId == userId))
I have two tables LookUpCodes and LookUpValues they are defined as below:
public partial class LookUpCodes
{
public int Id { get; set; }
public int? CodeId { get; set; }
public string Code { get; set; }
public string Description { get; set; }
}
public partial class LookUpValues
{
public int Id { get; set; }
public int CodeId { get; set; }
public string CodeValue { get; set; }
public bool? IsActive { get; set; }
}
Each LookUpCode can have multiple Values associated with it. I want to pass in a code and get associated list of values back.
This is probably a common question as I have seen this everywhere, I am not looking for an answer per se, if someone can just explain how to build the proper query I would be obliged.
Here is what I have done so far:
public IEnumerable<LookUpValues> GetValuesByCode(string cd)
{
var query = from code in _context.LookUpCodes
join values in _context.LookUpValues on code.CodeId equals values.CodeId
where code.Code == cd
select new { LookUpValues = values };
return (IEnumerable<LookUpValues>) query.ToList();
}
You are very close to that you are looking for:
public IEnumerable<LookUpValues> GetValuesByCode(string cd)
{
var query = from code in _context.LookUpCodes
join values in _context.LookUpValues
on code.CodeId equals values.CodeId
where code.Code == cd
select values;
return query;
}
Since you have written the join, I assume that you have understood how it works. However let's revisit it:
from a in listA
join b in listB
on a.commonId equals b.commonId
In the above snippet we join the contents of listA with the contents of listB and we base their join on a commonId property existing in items of both lists. Apparently the pair of a and b that fulfill the join criterion it would form one of the possible many results.
Then the where clause is applied on the results of the join. The joined items that pass thewherefilter is the new result. Even at this point the results is still pairs ofaandb`.
Last you project, using the select keyword each pair of the results to a new object. In your case, for each pair of code and values that passed also the where filter, you return only the values.
I'm using EF Code First (hybrid, database generation disabled) and I have two models/tables. I'm try to select and return all values in T1 and one field in a reference/lookup table so I can perform filtering on the list without requerying the database. I need to have the value of ItemName available so I can do comparisons.
If I were using SQL I'd just do something like this:
SELECT s.*, im.ItemName
FROM Specs s
INNER JOIN ItemMake im ON s.ItemMakeID = im.ID
My classes look something like this:
public class Spec {
public int ID {get; set;}
public int ItemMakeID {get; set;}
[ForeignKey("ItemMakeID")]
public ItemMake itemMake {get; set;}
}
public class ItemMake {
public int ID {get; set;}
public string ItemName {get; set;}
}
Currently my Linq to EF query looks like this. It doesn't work. I can't get at the ItemName property like I need to.
var specs = (from s in db.Specs
join im in db.ItemMakes on s.ItemMakeID equals im.ID
orderby s.modelNo select s).ToList();
What am I doing wrong?
That's because you're selecting just s in select clause. Use anonymous type declaration to get ItemName too:
var specs = (from s in db.Specs
join im in db.ItemMakes on s.ItemMakeID equals im.ID
orderby s.modelNo select new { s, im.ItemName }).ToList();
I'm trying to order a list of "parent" items based on a value in its sub-collection's sub-collection. Here's the specifics...
I have a Film entity (mapped to the Films table) that has a one-to-many collection of Release entities (mapped to the Releases table). Each Release has one or more ReleaseDate entities (mapped to the ReleaseDates table).
public class Film {
public int Id {get;set;}
public string Title {get;set;}
/* ... more properties here ...*/
}
public class Release {
public int Id {get;set;}
public int FilmId {get;set;}
public virtual Film Film {get;set;}
public virtual ICollection<ReleaseDate> ReleaseDates { get; set; }
/* ... more properties here ...*/
}
public class ReleaseDate {
public int Id {get;set;}
public DateTime Date {get;set;}
public int ReleaseId {get;set;}
public virtual Release Release {get;set;}
/* ... more properties here ...*/
}
Now, I want to order the Films by the earliest release date, but obviously a film could have no releases, and a release could have no release dates (again, 1-* relationships). The SQL equivalent would be...
SELECT * /* or whatever columns...*/
FROM dbo.Films F
LEFT OUTER JOIN dbo.Releases R ON R.FilmId = F.Id
LEFT OUTER JOIN dbo.ReleaseDates RD ON RD.ReleaseId = R.Id
ORDER BY RD.[Date] ASC /* or DESC */
How can I go about doing this?
var orderedFilms = Films.OrderBy(a=> a.Releases.Any() ?
a.Releases.Select(x=>x.ReleaseDates.Any() ?
x.ReleaseDates.Min(d=>d.Date).Date :
DateTime.Now).Min() : DateTime.Now);
Well, I changed my approach to this problem and resolved it based on the "normal" approaches out there, one of which was given as answer but subsequently deleted by the poster. What I ended up doing is moving my select down to the repository layer where I do have the DbContext and was able to do a "simple" left outer join style query like this...
var films = (from f in db.Films
join r in db.Releases on f.Id equals r.FilmId into FR
from a in FR.DefaultIfEmpty()
join d in db.ReleaseDates on a.Id equals d.ReleaseId into RRD
from b in RRD.DefaultIfEmpty()
orderby b.Date ascending
select f);
#KingKing, thanks for your answer, I think it may come in handy in some other places where we have these sort of aggregate fields based on properties of sub-collections or even properties of sub-sub-collections.
join diffrent field type in linq
public partial class Product
{
public int ID { get; set; }
public string CategoryID
{
get { return Myclass.increse(CategoryID); }
set { CategoryID = value; }
}
public string Name { get; set; }
}
public partial class ProductCategory
{
public int ID { get; set; }
public string Name { get; set; }
}
var query = (from c in dContext.ProductCategories
join p in dContext.Products
on Myclass.EncodeMD5(c.ID.ToString()) equals p.CategoryID
select new { id = p.ID, cat = p.CategoryID, name = p.Name, cat1 = c.Name }
).ToList();
The field should be converted to string
Then function runs EncodeMD5
error:
LINQ to Entities does not recognize the method 'System.String
EncodeMD5(System.String)' method, and this method cannot be translated
into a store expression.
You cannot call arbitrary .NET methods in LINQ-to-(some database backend) - the entire point of EF (etc) is that it wants to create SQL from your expression - something involving a where clause. It can work with simple properties and operators, and a few methods it knows about and can map into SQL, but it can't perform something it has never heard of (increse, EncodeMD5, etc) how would it know what SQL to write?
With something like MD5, your best bet would be to store the MD5 hash in the underlying table along with the ID. Likewise with the CategoryID's "increse" (whatever that is). So your query would end up working off these pre-calculated values:
on c.IDHash equals p.CategoryIDHash