I am trying to include two relationships with the Include() method.
using (var db = new DBContext()) {
var result = db.ProduktKundeArtNummer
.Include(p => p.Kunde)
.Include(d => d.Produkt)
.ToList();
}
}
When I execute this snippet I get the following error message:
Lambda expression used inside Include is not valid.
I created my context by scaffolding my mysql database.
This is the class it is referring to:
public partial class ProduktKundeArtNummer {
[Key]
public int Produkt { get; set; }
[Key]
public int Kunde { get; set; }
[Required]
[StringLength(255)]
public string Artikelnummer { get; set; }
[ForeignKey(nameof(Kunde))]
[InverseProperty("ProduktKundeArtNummer")]
public virtual Kunde KundeNavigation { get; set; }
[ForeignKey(nameof(Produkt))]
[InverseProperty("ProduktKundeArtNummer")]
public virtual Produkt ProduktNavigation { get; set; }
}
I already googled a lot and as far as I know my snippet should work.
Does anyone know where I messed up?
Edit:
Using a string navigator works fine, but I would rather use the lambda expressions.
using (var db = new DBContext()) {
var result = db.ProduktKundeArtNummer
.Include("KundeNavigation")
.Include("ProduktNavigation")
.ToList();
}
}
You are trying to Include integer properties which are not a Navigation properties. Include is a declaration which navigation properties should be loaded with entity.
Probably you need this:
using (var db = new DBContext()) {
var result = db.ProduktKundeArtNummer
.Include(p => p.KundeNavigation)
.Include(d => d.ProduktNavigation)
.ToList();
}
Related
I have 2 queries as follows:
var q1 = await context.Submissions
.Include(s => s.Application)
.ToListAsync();
// q1 is of type List<Submissions>
var q2 = await context.Applications
.Select(a => new Application
{
Id = a.Id,
Member = a.Histories.OrderByDescending(h => h.ModifiedDate).FirstOrDefault().Member
}).ToListAsync();
// q2 is of type List<Applications>
Is there a way to combine these 2 queries and have the type as List<Submissions>?
Note: I'm using EF Core version 3
Submissions class:
public class Submission
{
public Guid Id { get; set; }
public string Name { get; set; }
public Application Application { get; set; }
public Guid? ApplicationId { get; set; }
}
Applications class:
public class Application
{
public Guid Id { get; set; }
public string Member { get; set; }
public ICollection<History> Histories { get { return _Histories; } set { _Histories = value; _currentMember =null; } }
private ICollection<History> _memberHistories;
private MemberHistory _currentMember = null;
}
There are .Include() and .ThenInclude()
var q1 = context.Submission
.Include(submission => submission.Application)
.ThenInclude(application = > application.Histories);
having too many includes can give performance issues, unless you actually start splitting up the query. Another approach would be to contain it in a select statement which often performs better. but gives sort of a split result.
var q2 = context.Submissions.Select(submission => new
{
SubMission = submission
Application = submission.Application
});
var result = q2.ToList().Select(t => t.Submission);
due to the built in EF Core mapper, the relations are handled for you so application are loaded and "attached" correctly to the Submission set on the result.
I'm using EF Core for my project. And I have a problem with nested query in EF Core.
I have 2 classes:
public class PermissionGroupDefinitionEntity : IEntity
{
public string Name { get; set; }
public string NormalizedName { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<PermissionDefinitionEntity> PermissionDefinitions { get; set; }
}
public class PermissionDefinitionEntity : IEntity
{
public string Name { get; set; }
public string NormalizedName { get; set; }
public string DisplayName { get; set; }
public bool IsEnabled { get; set; }
public virtual string GroupName { get; set; }
public virtual PermissionGroupDefinitionEntity Group { get; set; }
public virtual ICollection<PermissionDefinitionEntity> Children { get; set; }
}
and this is the ApplicationDbContext:
builder.Entity<PermissionDefinitionEntity>().HasOne(r => r.Group).WithMany(r => r.PermissionDefinitions).OnDelete(DeleteBehavior.Cascade);
builder.Entity<PermissionDefinitionEntity>().HasOne(r => r.Parent).WithMany(r => r.Children).OnDelete(DeleteBehavior.Cascade);
I want query all PermissionGroupDefinitionEntity included PermissionDefinitionEntity and self referencing of PermissionDefinitionEntity.
Can I do that with EF Core?
You need to recursively load PermissionDefinitions that placed in the PermissionGroupDefinitionEntity.
First, you should load all PermissionGroupDefinitionEntities including its children using the following query :
var query = _dbContext.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(p => p.PermissionDefinitions )
.ThenInclude(p => p.Children)
.ToListAsync();
Since every PermissionGroupDefinitionEntity has a list of PermissionDefinition you need a nested loops like this code :
foreach (var PermissionGroupDefinitionEntity in PermissionGroupDefinitionEntities)
{
foreach (var PermissionDefinitions in PermissionDefinitions)
{
}
}
Then in the inner loop you should call your recursive function.
See following link (sample for get all children recursively in Entity Framework Core)
https://patrickdesjardins.com/blog/how-to-load-hierarchical-structure-with-recursive-with-entity-framework-5
This way has terrible performance and I don't recommend that.
In this case it's seems you must write a stored procedure in SQL for better performance.
You can use .ThenInclude(i => ...) like so
var query = _context.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(i => i.PermissionDefinitions)
.ThenInclude(i => i.Group)
.AsQueryable();
Edit:
var query = _context.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(i => i.PermissionDefinitions)
.ThenInclude(i => i.Children)
.AsQueryable();
I'm trying to query something from an indirectly related entity into a single-purpose view model. Here's a repro of my entities:
public class Team {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<Member> Members { get; set; }
}
public class Member {
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class Pet {
[Key]
public int Id { get; set; }
public string Name { get; set; }
public Member Member { get; set; }
}
Each class is in a DbSet<T> in my database context.
This is the view model I want to construct from a query:
public class PetViewModel {
public string Name { get; set; }
public string TeamItIndirectlyBelongsTo { get; set; }
}
I do so with this query:
public PetViewModel[] QueryPetViewModel_1(string pattern) {
using (var context = new MyDbContext(connectionString)) {
return context.Pets
.Where(p => p.Name.Contains(pattern))
.ToArray()
.Select(p => new PetViewModel {
Name = p.Name,
TeamItIndirectlyBelongsTo = "TODO",
})
.ToArray();
}
}
But obviously there's still a "TODO" in there.
Gotcha: I can not change the entities at this moment, so I can't just include a List<Pet> property or a Team property on Member to help out. I want to fix things inside the query at the moment.
Here's my current solution:
public PetViewModel[] QueryPetViewModel_2(string pattern) {
using (var context = new MyDbContext(connectionString)) {
var petInfos = context.Pets
.Where(p => p.Name.Contains(pattern))
.Join(context.Members,
p => p.Member.Id,
m => m.Id,
(p, m) => new { Pet = p, Member = m }
)
.ToArray();
var result = new List<PetViewModel>();
foreach (var info in petInfos) {
var team = context.Teams
.SingleOrDefault(t => t.Members.Any(m => m.Id == info.Member.Id));
result.Add(new PetViewModel {
Name = info.Pet.Name,
TeamItIndirectlyBelongsTo = team?.Name,
});
}
return result.ToArray();
}
}
However, this has a "SELECT N+1" issue in there.
Is there a way to create just one EF query to get the desired result, without changing the entities?
PS. If you prefer a "plug and play" repro containing the above, see this gist.
You've made the things quite harder by not providing the necessary navigation properties, which as #Evk mentioned in the comments do not affect your database structure, but allow EF to supply the necessary joins when you write something like pet.Member.Team.Name (what you need here).
The additional problem with your model is that you don't have a navigation path neither from Team to Pet nor from Pet to Team since the "joining" entity Member has no navigation properties.
Still it's possible to get the information needed with a single query in some not so intuitive way by using the existing navigation properties and unusual join operator like this:
var result = (
from team in context.Teams
from member in team.Members
join pet in context.Pets on member.Id equals pet.Member.Id
where pet.Name.Contains(pattern)
select new PetViewModel
{
Name = pet.Name,
TeamItIndirectlyBelongsTo = team.Name
}).ToArray();
I am using a DbContext that has been provided and I cannot change it.
Edited for brevity, the context contains two types
public class LogPost {
public int ID { get; set; }
public string Comment { get; set; }
public virtual ICollection<LogReply> Replies { get; set; }
public LogPost() {
Replies = new List<LogReply>();
}
}
public class LogReply {
public int ID { get; set; }
public string Comment { get; set; }
}
There is no DbSet<LogReply> to work with, only a DbSet<LogPost>. If I try removing the reply from the post.Replies collection, it throws an exception about foreign key properties not being null (as expected).
How can I delete a reply?
I know you are unable to change the existing DbContext implementation, but the method to create a DbSet<T> for a DbContext is public (see here).
So you could try the following:
using(var db = new Context())
{
var post = db.LogPosts.First(p => p.ID == postID);
var reply = post.Replies.First(r => r.ID == replyID);
var logReplies = db.Set<LogReply>();
logReplies.Remove(reply);
db.SaveChanges();
}
The solution I am using is to get the DbEntityEntry for the entity and manually marking it as deleted.
using (var db = new Context()) {
var post = db.LogPosts.First(p => p.ID == postID);
var reply = post.Replies.First(r => r.ID == replyID);
db.Entry(reply).State = System.Data.Entity.EntityState.Deleted;
db.SaveChanges();
}
I am having an error on my web page:
{"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."}
It occours herein my view:
#model Service_Monitor_Web_Interface.Models.DashBoardViewModel
...
=> #Html.DisplayFor(modelItem => item.Service.Name)
Here is my ViewModel class:
public class DashBoardViewModel
{
public int NumberOfUsers { get; set; }
public virtual IEnumerable<ApplicationUser> recentUsers { get; set; }
public virtual IEnumerable<UserActivityLog> logs { get; set; }
public virtual IList<Service_Monitor_Domain.Entities.ServiceStatusHistory> serviceStatus { get; set; }
}
Here is my controller for the class:
public ActionResult Index()
{
DashBoardViewModel vm = new DashBoardViewModel();
var dbs = new EFServiceStatusHistoryRepository();
vm.serviceStatus = dbs.GetAllEnabledLatestStatusLog();
return View(vm);
}
and here is my function that access the db:
public List<ServiceStatusHistory> GetAllEnabledLatestStatusLog()
{
List<ServiceStatusHistory> list = null;
try
{
using (var db = new EFDbContext())
{
//var results = db.ServiceStatusHistory.Where(h => h.Service.Enabled)
// .OrderByDescending(h => h.Id)
// .GroupBy(h => h.Service.Id)
// .Select(grp => grp.FirstOrDefault());
var results = db.ServiceStatusHistory
.Where(x => x.Service.Enabled)
.GroupBy(x => x.Service)
.Select(x => x.OrderByDescending(y => y.time).FirstOrDefault());
list = results.ToList();
}
return list;
}
catch
{
//Error
}
return list;
}
Here is my entity:
public class ServiceStatusHistory
{
[Key]
[Required]
public int Id { get; set; }
// Forenkey to service
[Required]
public virtual Service Service { get; set; }
[Required]
public ServiceStatus Status { get; set; }
[Required]
public string Messages { get; set; }
[Required]
//Time Logged in the db
public DateTime time { get; set; }
[Required]
//Time the service last called the update method on the client
public DateTime LastUpdateTime { get; set; }
}
I think it has something to do with lazy loading. But I have not had this problem before when querying the same table? However it was just a simple select query.
Consider "Eager" loading of the Service class in your GetAllEnabledLatestStatusLog(). Be sure to include using System.Data.Entity
using System.Data.Entity
var results = db.ServiceStatusHistory
.Include("Service")
.Where(x => x.Service.Enabled)
.GroupBy(x => x.Service)
.Select(x => x.OrderByDescending(y => y.time).FirstOrDefault());
See MSDN Loading Related Entities
I'd say that the problem here is that in your query you are not explicitly loading the related entities (in this case, ServiceStatusHistory.Service), presumably Lazy Loading is on by default.
So later on when you refer to item.Service.Name, the underlying EF class realises that it needs to load these related entities - except that the associated ObjectContext (in this case, EFDbContext) has long since been disposed.
So probably the simplest way to fix this is to modify your query to .Include() the related Service entities
var results = db.ServiceStatusHistory
.Include("Service")
.Where(x => x.Service.Enabled)
.GroupBy(x => x.Service)
.Select(x => x.OrderByDescending(y => y.time).FirstOrDefault());
or you could turn off lazy loading for that query:
using (var db = new EFDbContext())
{
db.Configuration.LazyLoadingEnabled=false;
//use original query here
}