I use Entity Framework as an ORM for SQL Server.
This is the code inside my POST API:
var group = await _context.Groups.FindAsync(id);
foreach (CalendarRuleGroup calendarRuleGroup in group.CalendarRuleGroups)
{
CalendarRule calendarRule = _context.CalendarRules
.Where(cr => cr.CalendarRuleGroupId == calendarRuleGroup.Id)
.First();
CalendarRule calendarRuleTemp = (CalendarRule)calendarRule.Clone();
_context.CalendarRules.Add(calendarRuleTemp);
}
await _context.SaveChangesAsync();
This is the Clone() function:
public object Clone()
{
CalendarRule clonedRule = (CalendarRule)MemberwiseClone();
clonedRule.Calendar = null;
clonedRule.CalendarId = default;
clonedRule.CalendarRuleGroup = null;
clonedRule.CalendarRuleGroupId = default;
clonedRule.CalendarContexts = null;
clonedRule.Id = default;
List<CalendarRuleCondition> conditions = new List<CalendarRuleCondition>();
foreach (CalendarRuleCondition condition in clonedRule.CalendarRuleConditions)
{
conditions.Add((CalendarRuleCondition)condition.Clone());
}
clonedRule.CalendarRuleConditions = conditions;
return clonedRule;
}
This is the model of CalendarRule:
public class CalendarRule: ICloneable
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartingFrom { get; set; }
public DateTime? Until { get; set; }
public int? Count { get; set; }
public double Duration { get; set; }
public Frequency Frequency { get; set; }
public int Interval { get; set; }
public int Priority { get; set; }
public bool IsWorking { get; set; }
public int Efficiency { get; set; }
public int Cost { get; set; }
public bool AllowedSetup { get; set; }
public int Color { get; set; }
public string Description { get; set; }
public bool? IsActive { get; set; }
public int CalendarId { get; set; }
public int? CalendarRuleGroupId { get; set; }
public virtual Calendar Calendar { get; set; }
public virtual CalendarRuleGroup CalendarRuleGroup { get; set; }
public virtual ICollection<CalendarContext> CalendarContexts { get; set; }
public virtual ICollection<CalendarRuleCondition> CalendarRuleConditions { get; set; }
}
And this is the error that appears when "clonedRule.CalendarRuleConditions" inside the foreach executes:
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HMG79T5K5ECK", Request id "0HMG79T5K5ECK:00000002": An unhandled exception was thrown by the application.
System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.DetachedLazyLoadingWarning': An attempt was made to lazy-load navigation 'CalendarRuleConditions' on a detached entity of type 'CalendarRuleProxy'. Lazy-loading is not supported for detached entities or entities that are loaded with 'AsNoTracking'. This exception can be suppressed or logged by passing event ID 'CoreEventId.DetachedLazyLoadingWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
It appears as if the data contained in "CalendarRule.CalendarRuleConditions" has not been loaded. I don't use "AsNoTracking" and there isn't any call to SaveChangesAsync() before the code of the API that i have posted. How can I solve the problem?
CodePudding user response:
This question is similar to this other.
I think the problem is in (CalendarRule)MemberwiseClone()
, you should create entity with Create
(ef) or CreateProxy
(efcore) intead new
.
CodePudding user response:
This code here doesn't make any sense:
foreach (CalendarRuleCondition condition in clonedRule.CalendarRuleConditions)
{
conditions.Add((CalendarRuleCondition)condition.Clone());
}
clonedRule.CalendarRuleConditions = conditions;
If you are calling this Clone() method from within the entity instance you want to clone, this should be:
foreach (CalendarRuleCondition condition in CalendarRuleConditions)
{
conditions.Add((CalendarRuleCondition)condition.Clone());
}
clonedRule.CalendarRuleConditions = conditions;
Where we go through each condition in this object's lazy loaded CalendarRuleConditions to build a cloned list for the clone. Even then it would be better to eager load the CalendarRuleConditions and only rely on lazy loading as a fall-back.