LINQ to Entities does not recognize the method ... get_Item(Int32) - entity-framework

(Please read before marking as duplicate as my particular scenario is unique)
I have the following code:
// Get each treasure hunt
var treasureHunts = dbContext.TreasureHunts.Where(i => i.UserName == User.Identity.Name).ToList();
// Populate each treasure hunt with the list of leaderboard entries
for (int i = 0; i <= treasureHunts.Count; i++)
{
treasureHunts[i].Leaderboard = dbContext.Leaderboard.Where(
leaderboard => leaderboard.TreasureHuntId == treasureHunts[i].TreasureHuntId).ToList();
}
On running the program, I get the following error from the second database query (dbContext.Leaderboard.Where...):
LINQ to Entities does not recognize the method
'QrCodeTreasureHunter.Models.TreasureHuntDetails get_Item(Int32)'
method, and this method cannot be translated into a store expression.
In the first query, I'm getting each of the treasure hunts associated with a particular user.
In the second part, I'm attempting to iterate through each of the treasure hunts, and populate the treasure hunt's Leaderboard List property with the associated leaderboard entries from my Leaderboard table.
From what I understand from reading around is that this query isn't possible in its current form with Entity Framework.
What workarounds or solutions would you be able to recommend to solve this problem? The ideal solution would involve no changes to the data models.
If it's relevant, here is the TreasureHunt model:
public class TreasureHuntDetails
{
public TreasureHuntDetails()
{
Clues = new List<Clue>();
Leaderboard = new List<Leaderboard>();
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof (TreasureHuntDetails), null, int.MaxValue,
false, true, null);
xml.SetSerializer<TreasureHuntDetails>(dcs);
}
[Key]
public int TreasureHuntId { get; set; }
[Required]
public virtual string UserName { get; set; }
[Required]
public String Name { get; set; }
public String Description { get; set; }
[Required]
public String Password { get; set; }
public String CompletionMessage { get; set; }
public String State { get; set; }
public List<Clue> Clues { get; set; }
public List<Leaderboard> Leaderboard { get; set; }
}
And here is the Leaderboard model:
public class Leaderboard
{
[Key]
public int Id { get; set; }
public int TreasureHuntId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Completion { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public Int64 TimeTaken { get; set; }
public TreasureHuntDetails TreasureHuntDetails { get; set; }
}
Good luck!

I'm not able to test it right now but it could be the indexer, try this:
foreach (var treauserHunt in treasureHunts)
{
treasureHunt.Leaderboard = dbContext.Leaderboard.Where(leaderboard =>
leaderboard.TreasureHuntId == treasureHunt.TreasureHuntId).ToList();
}
I'm not sure this is the problem, but I remember having some issues with indexing in arrays in LINQ queries, just can't remember if it was with the LINQ method syntax (the one you are using) or the other (the SQL-like);

Thanks for the answer; it solved my problem. I still wanted to use a for loop so I did something like this:
for (int i = 0; i <= treasureHunts.Count; i++)
{
var thisTreasureHunt = treasureHunts[i];
treasureHunts[i].Leaderboard = dbContext.Leaderboard.Where(
leaderboard => leaderboard.TreasureHuntId == thisTreasureHunt.TreasureHuntId).ToList);
}

Related

Why I am getting an error when i try to save form into quiz model with entity framework?

I am to new both entity framework and dotnet core. Shortly i want to explain what i did and what kind of an error i got?
What i did?
First I created a few models below.
public class Quiz
{
public int QuizID { get; set; }
public int UserID { get; set; }
public string Text { get; set; }
public User User { get; set; }
public ICollection<Question> Questions { get; set; }
}
public class Question
{
public int QuestionID { get; set; }
public string Text { get; set; }
public int QuizID { get; set; }
public Quiz Quiz { get; set; }
public IList<Option> Options { get; set; }
}
public class User
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public ICollection<Quiz> Quizzes { get; set; }
}
public class Option
{
public int OptionID { get; set; }
public string Choice { get; set; }
public int QuestionID { get; set; }
public bool? IsCorrect { get; set; }
public Question Question { get; set; }
}
Second I created IActionResult method for saving informations that comes from user.
public async Task<IActionResult> CreateQuiz()
{
Quiz quiz = new Quiz();
quiz.UserID = 0;
quiz.Text = Request.Form["content"].ToString();
_context.Add(quiz);
await _context.SaveChangesAsync();
for (int i = 1; i <= 4; i++)
{
Question question = new Question();
question.QuizID = quiz.QuizID;
question.Text = Request.Form["title_" + i].ToString();
_context.Add(question);
_context.SaveChanges();
for (int j = 1; j <= 4; j++)
{
Option option = new Option();
option.QuestionID = question.QuestionID;
option.Choice = Request.Form["option_a" + i].ToString();
option.IsCorrect = j == int.Parse(Request.Form["correct_answer_" + i].ToString());
_context.Add(option);
}
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Finally i got this error when i save form.
An unhandled exception occurred while processing the request.
SqliteException: SQLite Error 19: 'FOREIGN KEY constraint failed'.
Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(int rc,
sqlite3 db)
DbUpdateException: An error occurred while updating the entries. See
the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection
connection, CancellationToken cancellationToken)
I need your help guys.
*Sorry for spelling rules i am also new to english.
There are a few things you can do to help avoid issues like this. It's a bit hard to pin down from your description exactly what is wrong, but you can simplify your code considerably which should take care of it.
Firstly, follow a convention for your key names and use annotations to nominate your Keys rather than relying on convention. You have a mix of naming like "QuestionId" for the PK on Question, yet "Id" for the PK on User.
Next, I would remove all FK fields and use Shadow Properties for the FK fields. The issue with FK columns is they are a second source of truth for relationships when you use Navigation Properties. Is option.QuestionId the ID of the question, or option.Question.QuestionId? How do you guarantee these are always in sync?
Lastly for the entity definitions, declare the navigation properties as virtual. This serves both lazy-loading (as a failsafe) as well as change tracking via proxies.
So updating the entity definitions to something like:
public class Quiz
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuizID { get; set; }
public string Text { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Question> Questions { get; set; } = new List<Question>();
}
public class Question
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionID { get; set; }
public string Text { get; set; }
[ForeignKey("QuizID")] // Creates a shadow property mapped to a QuizID column in table.
public virtual Quiz Quiz { get; set; }
public virtual ICollection<Option> Options { get; set; } = new List<Option>();
}
public class User
{
[Key]
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ICollection<Quiz> Quizzes { get; set; } = new List<Quiz>();
}
public class Option
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int OptionID { get; set; }
public string Choice { get; set; }
public bool? IsCorrect { get; set; }
[ForeignKey("QuestionID")]
public virtual Question Question { get; set; }
}
Now, when it comes to creating your question data you can leverage EF to manage the FKs by populating a complete set of related entities and saving them together.
public async Task<IActionResult> CreateQuiz()
{
var systemUser = _context.Users.Single(x => x.UserId == 0);
Quiz quiz = new Quiz();
quiz.User = systemUser;
quiz.Text = Request.Form["content"].ToString();
for (int i = 1; i <= 4; i++)
{
Question question = new Question();
question.Text = Request.Form["title_" + i].ToString();
quiz.Questions.Add(question);
for (int j = 1; j <= 4; j++)
{
Option option = new Option();
option.Choice = Request.Form["option_a" + i].ToString();
option.IsCorrect = j == int.Parse(Request.Form["correct_answer_" + i].ToString());
question.Options.Add(option);
}
}
_context.Quizes.Add(quiz);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
This can likely be simplified even more, but I kept it close to the original so it should be easier to follow. Rather that calling SaveChanges several times and trying to set FKs, it is far better to create the objects and associate them with each other. We create our quiz, then go through and create the questions, associating them to the Quiz by adding them to the quiz.Questions collection. Then go through and do the same for the Question Options. Once we are done, we tell the context to add the Quiz to it's Quizes DbSet, and call SaveChanges once at the very end. EF will save all of the entities and their relationships automatically, populating the appropriate FKs. The benefit here is that all of the changes are committed together in one transaction rather than separate saves where a quiz might be saved, but there was an issue with one question or one option, leaving the database in an incomplete state at the point of failure.

Combining multiple IQueryable from different object types for TreeList DataSource

I search for a way to combine two or more IQueryables from different Object types in order to use it as a datasource for my treelist.
For the treelist I use the DevExpress WinForms component "TreeList".
It provides me the properties "KeyFieldName" which is usually mapped to the "ID" and the ParentFieldName which is mapped to the parent id in order to build a hierarchy.
I use entity framework 6 as or mapper.
I have the two following classes I would need to combine:
XObject:
[Table("tbl_objects")]
public class XObject
{
[Column("id")]
public int Id { get; set; }
[Column("display_name")]
public String DisplayName { get; set; }
[Column("description")]
public String Description { get; set; }
[Column("usage_reason")]
public String UsageReason { get; set; }
[Column("is_network_compatible")]
public bool IsNetworkCompatible { get; set; }
[Column("ip_address")]
public String IpAddress { get; set; }
[Column("network_name")]
public String NetworkName { get; set; }
[Column("serial_number")]
public String SerialNumber { get; set; }
[Column("manufacturer_identification_code")]
public String ManufacturerIdentificationCode { get; set; }
[Column("web_link")]
public String WebLink { get; set; }
[Column("warranty")]
public int WarrantyInDays { get; set; }
[Column("ref_manufacturer")]
public virtual XManufacturer Manufacturer { get; set; }
[Column("ref_order")]
public virtual XOrder Order { get; set; }
[Column("ref_owner")]
public virtual XOwner Owner { get; set; }
[Column("ref_room")]
public virtual XRoom Room { get; set; }
[Column("ref_object_folder")]
public virtual XObjectFolder ObjectFolder { get; set; }
public virtual ICollection<XAdditionalObjectData> AdditionalObjectData { get; set; }
}
XObjectFolder:
[Table("tbl_object_folders")]
public class XObjectFolder
{
[Column("id")]
public int Id { get; set; }
[Column("display_name")]
public String DisplayName { get; set; }
[Column("short_name")]
public String ShortName { get; set; }
[Column("ref_parent_folder")]
public virtual XObjectFolder ParentFolder { get; set; }
public virtual ICollection<XObjectFolder> ChildFolders { get; set; }
public virtual ICollection<XObject> Objects { get; set; }
[NotMapped]
public int ParentFolderId { get { return ParentFolder == null ? -1 : ParentFolder.Id; } }
}
As you've probably already seen, an object folder can contain subfolders but also objects.
My goal is to see this as one "datasource" in my treelist.
For example like this:
Object Folder A
Object Sub-Folder A
Object 1
Object 1
In other questions here I've found the possibilities to concat or union queryables, but that only works with them being the same type:
using (var db = new XDbContext(_conString))
{
// Queryables
var ofs = from of in db.ObjectFolders orderby of.DisplayName ascending select of; // <- All ObjectFolders
var obs = from obj in db.Objects orderby obj.DisplayName ascending select obj; // <- All Objects
// Concat them
var comb = ofs.Concat(obs); // <- not the same type
// As DataSource for my TreeList
TreeListObjects.DataSource = comb.ToList();
}
Which is why I am searching for a good way to make this possible.
I could also imagine me using a pretty bad approach to reach my goal. So I am open to suggestions. This is a personal project which I do to improve myself at stuff.
Thanks in advance!
EDIT
So I managed to get a step further by using an interface both classes share:
public interface ITreeListCombinable
{
int Id { get; set; }
int ParentId { get; }
String DisplayName { get; set; }
}
But... who would've thought... there occures another problem:
Have a look at the db structure:
Db_Struture
Since both objects are stored in different tables, the id's will certainly not be unique when combining them.
Which is necessary when setting the datasource.
Solution:
So I've taken my own approach to my problem and it worked out.
Full disclosure -> I consider myself a beginner, so this solution is probably not the best. Still, if anyone is in a similar situation, here's how it could work:
First I created an interface, which both the folder and objects share:
ITreeListCombinable
public interface ITreeListCombinable
{
int Id { get; set; }
int ParentId { get; }
int ListId { get; set; }
int ParentListId { get; set; }
String DisplayName { get; set; }
ObjectTreeListElementTypes TreeListElementType { get; }
}
I then made sure, both my XObject and XObjectFolder classes held the ObjectTreeListElementTypes value they're corresponding to:
ObjectTreeListElementTypes Enum:
public enum ObjectTreeListElementTypes
{
Folder,
Object
}
Classes:
[NotMapped]
public ObjectTreeListElementTypes TreeListElementType => ObjectTreeListElementTypes.Folder; // or *.Object for that matter
So afterwards I've wrote my own "controller" which handles my specific scenario.
ObjectTreeListElementController:
public class ObjectTreeListElementController
{
private List<ITreeListCombinable> _list;
public ObjectTreeListElementController()
{
_list = new List<ITreeListCombinable>();
}
public void AddRange(List<ITreeListCombinable> list)
{
// add incoming items to private _list
_list.AddRange(list);
}
public List<ITreeListCombinable> GetDataSourceList()
{
// create auto increment list id
var listId = 0;
foreach (var item in _list)
{
item.ListId = listId;
listId++;
}
// set new parent list id according to incremental list id
foreach (var item in _list)
{
var parents = _list.Where(x => x.Id == item.ParentId && x.TreeListElementType == ObjectTreeListElementTypes.Folder);
if (parents.Count() > 0)
item.ParentListId = parents.First().ListId;
else
item.ParentListId = -1;
}
return _list;
}
}
Essentially, when calling the GetDataSourceList() method, it firstly distributes incremental, temporary list-ids.
In a second loop I then search for the original parent id and match the tree list element type. If none is found, this folder is a root folder in my treelist, if one is found, the given list-id becomes the parent list id:
using (var db = new XDbContext(_conString))
{
// Queryables
IQueryable<ITreeListCombinable> ofs = from of in db.ObjectFolders orderby of.DisplayName ascending select of;
IQueryable<ITreeListCombinable> objs = from obj in db.Objects orderby obj.DisplayName ascending select obj;
var lofs = ofs.ToList();
var lobjs = objs.ToList();
var ctrl = new ObjectTreeListElementController();
ctrl.AddRange(lofs);
ctrl.AddRange(lobjs);
var sourceList = ctrl.GetDataSourceList();
// As DataSource for my TreeList
TreeListObjects.DataSource = sourceList;
}
And this brought me the exact output I've wanted:
Hope this helps another beginner :)

Referenced object is not loaded from database

This the table structure I have:
#region Tables
public class WorkoutProfile
{
public WorkoutProfile()
{
WorkoutExercises = new List<WorkoutExercise>();
}
[Key]
public int ProfileId { get; set; }
public string Name { get; set; }
public int Sets { get; set; }
public int RestAfterSetInSeconds { get; set; }
public virtual User User { get; set; }
public virtual ICollection<WorkoutExercise> WorkoutExercises { get; set; }
}
public class WorkoutExercise
{
[Key]
public int WorkoutId { get; set; }
public virtual Exercise Exercise { get; set; }
public int Order { get; set; }
public int WorkoutTimeInSeconds { get; set; }
public int RestAfterInSeconds { get; set; }
}
public class Exercise
{
[Key]
public long ExerciseId { get; set; }
public string Title { get; set; }
public string Visualisation { get; set; }
public bool IsDefault { get; set; } // Is exersice should be included when user first registers
}
public class User
{
[Key]
public long UserId { get; set; }
public string Email { get; set; }
public DateTime Registered { get; set; }
}
#endregion Tables
In the repository class I run the following linq query:
return context
.WorkoutProfiles.Include(w => w.WorkoutExercises)
.Where(q => q.User.UserId == userId && q.ProfileId == profileId)
.FirstOrDefault();
and I receive the good and old "Object reference not set to an instance of an object". When examining the result, see that Exercises property in WorkoutExercises is null.
This is how the database is created using code first approach:
So, the question is: why Exercises not included in WorkoutExercises object? Do I need to include it somehow? I am using .NET Core 2
The simple answer would be no lazy loading in EFCore. Not Released yet but if you want to dabble with alpha code, its in the repository. Based on your classes there are no collections for exercises in WorkoutExcercise.
Then you need to ThenInclude(w => w.Exercises) following your Include clause since EFCore doesn't do lazy loading.
I found a solution following this post
Altered my code as following:
var top = context
.Set<WorkoutProfile>()
.Where(q => q.ProfileId == profileId && q.User.UserId == userId)
.Include(q => q.WorkoutExercises)
.SingleOrDefault();
context
.Entry(top)
.Collection(e => e.WorkoutExercises)
.Query()
.OfType<WorkoutExercise>()
.Include(e => e.Exercise)
.Load();
And it worked

Entity Framework 6 (code first) using child collection foreign key without the parents primary key

I really think I am missing something here that's probably really simple that's not jumping out at me.
I have these objects and I am trying to join a parent object to a child collection but not necessarily using the parent's primary key. In sql I can do this pretty easily, but it's bugging me why this cannot happen using code first. I am trying to join CompetitorMatchInformation to BrandSkuPricing by the ErpSkuId.
public class CompetitorMatchInformation {
[Key(), Column("MatchId")]
public long MatchId { get; set; }
[Column("ErpSkuId")]
public int? ErpSkuId { get; set; }
[Column("CompetitorId")]
public int CompetitorId { get; set; }
[ForeignKey("CompetitorId")]
public virtual Competitors Competitor { get; set; }
[ForeignKey("CompetitorItemToErpSkuMatchId")]
//[ForeignKey("ErpSkuId")]
public virtual List<BrandSkuPricing> BrandSkuPricing { get; set; }
}
public class Competitors
{
[Key(), Column("CompetitorId")]
public int CompetitorId { get; set; }
[Column("CompetitorName")]
public string CompetitorName { get; set; }
}
public class BrandSkuPricing
{
[Key(), Column("BrandSkuId")]
public int BrandSkuId { get; set; }
[Column("CompetitorItemToErpSkuMatchId")]
public long CompetitorItemToErpSkuMatchId { get; set; }
[Column("ErpSkuId")]
public int? ErpSkuId { get; set; }
[Column("Price")]
public decimal? Price { get; set; }
[Column("BrandId")]
public int? BrandId { get; set; }
[Column("BrandSourceSytemId")]
public string BrandSourceSytemId { get; set; }
[Column("BrandName")]
public string BrandName { get; set; }
[Column("BrandSkuNumber")]
public string BrandSkuNumber { get; set; }
}
The Competitor comes over correctly, but the child collection not so much. This isn't a normal scenario I know, but the underlying view for BrandSkuPricing has a relationship that's not entirely normal.
The query I am using is
public List<CompetitorMatchInformation> GetCompetitorMatchInfoByCompetitorItemId(long competitorItemId, int? brandId = null)
{
var query = this.Entity.Include(x => x.CurrentChallenges).Include(x => x.BrandSkuPricing);
var list = query.Where(x => x.CompetitorItemId == competitorItemId &&
((x.CurrentChallenges.Count > 0 && x.CurrentChallenges.Any(w => !w.IsResolved)) ||
x.CurrentChallenges.Count == 0))
.ToList();
list.ForEach(l =>
{
if (brandId.HasValue)
{
l.BrandSkuPricing = l.BrandSkuPricing.Where(x => x.BrandId == brandId).ToList();
}
});
return list;
}
And in the model builder, I have nothing. I have tried but cannot get it to work even in the builder. Anyway I can get the child collection to join on ErpSkuId? I have changed the underlying view to pull in the CompetitorItemToErpSkuMatchId so it working that way, but this scenario of joining on something that isn't a key will come up for me a lot soon.
Thanks!

How do i check whether a collection has been eager loaded with Entity Framework?

I recently used the following code
var errorCount =
split.Profiles.SelectMany(p => p.Logs)
.Count(l => l.LogTypeId == (int)LogType.Error);
errorCount returned zero because I forgot to include my logs table when I built the split entity.
How can I detect whether the split.Profiles.Logs collection has been eager loaded?
I am using Model First.
the class for Profile is
public partial class Profile
{
public Profile()
{
this.Log = new HashSet<Log>();
}
public int ProfileId { get; set; }
public int SplitId { get; set; }
public string Filename { get; set; }
public System.DateTime StartTime { get; set; }
public System.DateTime EndTime { get; set; }
public virtual ICollection<Log> Log { get; set; }
public virtual SplitUpload SplitUpload { get; set; }
}
#Hopeless you pointed me in the right direct. First I had to check the first collection was loaded. Then I needed to check that if it had any members then their child collection was also loaded