Error with lazy loading in Entity Framework Entity - c#

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
}

Related

The instance of entity type X cannot be tracked because another instance with the key value '{Id:}' is already being tracked

i am using entity framework core 7 in my .net 7 project. Here is my update method
public async Task UpdateAsync(int id, SiteDto dto)
{
Site site = await _context.Sites.Where(x => x.Id == id)
.Include(x => x.Network)
.Include (x => x.Centre)
.Include(x => x.SiteDayParts).ThenInclude(x => x.SiteFrames)
.Include(x => x.Resolution)
.SingleOrDefaultAsync();
_mapper.Map(dto, site);
await _context.SaveChangesAsync();
}
SiteDto object, context has the same fields
public class SiteDto
{
public string Name { get; set; }
public string Address { get; set; }
public MediaFormat Formats { get; set; }
public int ResolutionId { get; set; }
public List<SiteDayPartDto> SiteDayParts { get; set; }
}
public class SiteDayPartDto
{
public int Id { get; set; }
public TimeSpan StartTime { get; set; }
public TimeSpan EndTime { get; set; }
public int Duration { get; set; }
public List<SiteFrameDto> SiteFrames { get; set; }
}
public class SiteFrameDto
{
public int Id { get; set; }
public int FrameId { get; set; }
}
Mapping config
CreateMap<SiteDto, Site>();
CreateMap<SiteDayPartDto, SiteDayPart>()
.ForMember(dest => dest.AdLength, opt => opt.MapFrom(x => x.Duration));
CreateMap<SiteFrameDto, SiteFrame>()
When I trying to save changes, I get an error "The instance of entity type 'SiteFrame' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."
Database - Postgresql.
Context registered as scoped, mapper, as I know, don`t create new object.
It is interesting that, if I use entity framework 6, it works well.
I have solved the problem like this. First, install Automapper.Collections and add:
services.AddAutoMapper((serviceProvider, automapper) =>
{
automapper.AddCollectionMappers();
}, Assembly.GetExecutingAssembly());
Then if you have nested list with a nested list, as in my example, add this mapping:
CreateMap<SiteDayPartDto, SiteDayPart>()
.EqualityComparison((odto, o) => odto.Id == o.Id)
CreateMap<SiteFrameDto, SiteFrame>()
.EqualityComparison((odto, o) => odto.Id == o.Id)
EqualityComparison will check if entity contains dto or not (it may be useful https://github.com/AutoMapper/Automapper.Collection). Good luck!

Combine 2 results into one in Entity Framework Core

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.

Nested include Entity Framework Core

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

Lambda expression used inside Include is not valid. Include not working

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

Mapping Data Entity with Self-Navigating Property to Business Entity

I'm having a lot of trouble with creating my business entities from my data entities.
Github
My Data.Entities.User looks as follows:
public class User
{
public User()
{
Messages = new List<Message>();
Followers = new List<User>();
Favorites = new List<Message>();
Notifications = new List<Notification>();
SubscribedTopics = new List<Topic>();
}
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Tag { get; set; }
public string Picture { get; set; }
public ICollection<Message> Messages { get; set; }
public ICollection<User> Followers { get; set; }
public ICollection<Message> Favorites { get; set; }
public ICollection<Notification> Notifications { get; set; }
public ICollection<Topic> SubscribedTopics { get; set; }
}
My Data.Mappers.UserMapper looks like this:
class UserMapper : EntityTypeConfiguration<User>
{
public UserMapper()
{
// Table Mapping
ToTable("Users");
// Primary Key
HasKey(u => u.Id);
Property(u => u.Id)
.IsRequired();
// Properties
Property(u => u.Name)
.IsRequired()
.HasMaxLength(140);
Property(u => u.Email)
.IsRequired()
.HasMaxLength(255)
.IsUnicode(false);
Property(u => u.Tag)
.IsRequired()
.IsUnicode(false)
.HasMaxLength(255)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));
Property(u => u.Picture)
.IsOptional();
// Relationships
HasMany(u => u.Followers)
.WithMany()
.Map(u => u.MapLeftKey("FollowerID"));
HasMany(u => u.Favorites)
.WithMany()
.Map(u => u.MapLeftKey("MessageID"));
HasMany(u => u.SubscribedTopics)
.WithMany(t => t.Subscribers)
.Map(u =>
{
u.ToTable("TopicSubscribers");
u.MapLeftKey("UserID");
u.MapRightKey("TopicID");
});
}
}
Finally, my Domain.Entities.User like this:
public class User : EntityBase<string>
{
public string Name { get; set; }
public string Email { get; set; }
public string Tag { get; set; }
public string Picture { get; set; }
public IEnumerable<Message> Messages { get; set; }
public IEnumerable<User> Followers { get; set; }
public IEnumerable<Message> Favorites { get; set; }
public IEnumerable<Notification> Notifications { get; set; }
public IEnumerable<Topic> SubscribedTopics { get; set; }
protected override void Validate()
{
if (string.IsNullOrWhiteSpace(Name))
{
AddBrokenRule(new ValidationRule("Name", "Name_Missing"));
}
if (string.IsNullOrWhiteSpace(Email))
{
AddBrokenRule(new ValidationRule("Email", "Email_Missing"));
}
if (string.IsNullOrWhiteSpace(Tag))
{
AddBrokenRule(new ValidationRule("Tag", "Tag_Missing"));
}
System.Uri uriResult;
if (!string.IsNullOrWhiteSpace(Picture) &&
Uri.TryCreate(Picture, UriKind.Absolute, out uriResult) &&
(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps))
{
AddBrokenRule(new ValidationRule("Picture", "Picture_InvalidURI"));
}
}
}
EntityBase adds the Id parameter, so as far as parameters are concerned, these two classes should be identical.
The part where I run into trouble is mapping the Data Entity to the Domain Entity.
public override IEnumerable<User> GetAll()
{
IEnumerable<User> user = _context.Users.Project()
.To<User>("Followers");
return user;
}
I think what's causing trouble is the circular navigational properties. User1 might have a follower named User2, while at the same time following User2.
So far I have tried both AutoMapper and ValueInjecter, but I have not had any success with either.
I tried adding "Virtual" to all navigational properties, enabling lazy and proxy loading, but this causes both AutoMapper and ValueInjecter to fail. ValueInjecter due to a already opened datareader and AutoMapper because of a type mismatch.
I tried explicitly loading navigational properties, but as soon as I Include("Followers") on User, I get a stackoverflow.
Trying to create a AutoMapperConfiguration where I specify a maxDepth of 1 yields a stackoverflow unless I add opt.ExplicitExpansion to every navigational property.
If i then try to explicitly expand a navigational property, I get
The type 'ShortStuff.Domain.Entities.User' appears in two structurally
incompatible initializations within a single LINQ to Entities query. A
type can be initialized in two places in the same query, but only if
the same properties are set in both places and those properties are
set in the same order.
Ideally I would want a solution that lets me explicitly control which navigational properties to expand without recursing.
For example, I'd like to do something like:
_context.Users.Include("Followers").NoNavigation().AsEnumerable();
And then I would be able to access User.Followers and have a list of other users, with their navigational properties set to null.
Many thanks!
Full source code of my Repository / Service learning project can be found on Github at https://github.com/Bio2hazard/ShortStuff/tree/master/ShortStuffApi
Edit:
I made some progress.
I got things to work by turning off proxy generation & lazy loading, and then using ValueInjector like so:
IEnumerable<Data.Entities.User> userList = _context.Users.Include("Followers").Include("Favorites").Include("Messages").Include("Notifications").Include("SubscribedTopics");
IEnumerable<User> users = userList.Select(u => new User
{
Id = u.Id,
Email = u.Email,
Picture = u.Picture,
Tag = u.Tag,
Name = u.Name,
Followers = u.Followers.Select(uu => new User().InjectFrom<SmartConventionInjection>(uu)).Cast<User>(),
Favorites = u.Favorites.Select(uf => new Message().InjectFrom<SmartConventionInjection>(uf)).Cast<Message>(),
Messages = u.Messages.Select(um => new Message().InjectFrom<SmartConventionInjection>(um)).Cast<Message>(),
Notifications = u.Notifications.Select(un => new Notification().InjectFrom<SmartConventionInjection>(un)).Cast<Notification>(),
SubscribedTopics = u.SubscribedTopics.Select(ut => new Topic().InjectFrom<SmartConventionInjection>(ut)).Cast<Topic>()
});
But that's a ton of code. I could probably create a factory for this, but there has got to be a easier way, right?
with ValueInjecter you can use the SmartConventionInjection which will only access the properties if it needs to get the value:
http://valueinjecter.codeplex.com/wikipage?title=SmartConventionInjection&referringTitle=Home
other injections usually get the value too so that you could use it in the matching algorithm
for an example of using valueinjecter with Entity Framework (code first, latest)
have a look at this project: http://prodinner.codeplex.com

Categories

Resources