-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Opt-out of lazy-loading for specific navigations #10787
Comments
Our current scenario is using EF.CORE against SQLServer w/temporal tables AND the use of owned entity types. Without using lazy loading this works as expected, but as soon as lazy loading is enabled it all fall to pieces. Short, enabling lazy loading forces us to declare all owned entity types' "navigation" properties as virtual, which in turn makes EF - through the use of LL proxies - end up with a lot of "self-joins" on the table with the owned entity types. Without temporal queries this works, though unescessary, but when temporal query clauses are introduced it makes a real mess and a lot of hazzle. Allowing for some sort of conditional lazy loading, preferably by using / not using the virtual keyword, would solve the problem in an elegant way. |
@mikke49 It's not at all clear to me how lazy-loading is causing the issues you are seeing. Can you please file a new issue with complete details and include a runnable project/solution or complete code listing that demonstrates the behavior you are seeing? |
@ajcvickers Thanks for your reply. I'll try to be brief since we've found a solution. The original problem we experienced was with this scenario:
With this scenario we got a NullreferenceException and the following stack trace:
Disabling lazy loading removed the exception and it worked. We never digged into the details for the null reference exception, though. However, what we saw was that the use of FromSql made the final query contain one Left Join to the base table for each owned entity. This is unfortunate, and we assumed that this somehow also affected the logic for the lazy loading. We have now solved this and avoid the use of FromSql completely. What we do now is extend LINQ with new methods for history quering, and tap into the framwork and modify the SQL generation to create properly formatted history queries without using sub-queries. The firsts testes show that this also removed the "self-joins", and it all seems to work. Some more testing to do, but we believe this will be our working solution. Thanks for your time. |
@ajcvickers To add on this issue (and possibly clarify what I think @mikke49 was describing): public class Product : AuditedEntity, INamedEntity
{
public string Name { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
/// <summary>
/// Complex (owned) type - not a foreign key
/// </summary>
public ProductDimension Dimensions { get; set; }
} which has the setup: public class ProductMap : AuditedEntityMap<Product>
{
public override void Configure(EntityTypeBuilder<Product> builder)
{
base.Configure(builder);
builder.MapName();
builder.HasOne(x => x.Company)
.WithMany(x => x.Products)
.HasForeignKey(x => x.CompanyId)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
builder.OwnsOne(x => x.Dimensions);
}
} and the owned type, ProductDimension is described here: public class ProductDimension
{
public decimal Decimal { get; set; }
public ToleranceType DecimalToleranceType { get; set; }
public decimal? DecimalTolerance { get; set; }
public decimal Width { get; set; }
public ToleranceType WidthToleranceType { get; set; }
public decimal? WidthTolerance { get; set; }
public decimal Length { get; set; }
public ToleranceType LengthToleranceType { get; set; }
public decimal? LengthTolerance { get; set; }
public int MaxCoilSkidWeight { get; set; }
public int? CoilID { get; set; }
public int? MaxCoilOD { get; set; }
} (NOTE: ToleranceType is an enum) I have lazy loading setup and am using SQL Server.
Which is unexpected (and frankly, incorrect) because ProductDimension is specified as an Owned type, and as such should not require lazy loading. |
@mikke49 Thanks for the feedback. I have created #12462 for cases where we configure lazy loading even if the navigation property will always be eager loaded. However, it is not the case that an owned type will never require lazy-loading since it may contain a navigation property to some other entity type, and this should still have lazy-loading semantics. |
I think it's important because currently, it forces you to change all your models. IIRC EF6 worked that way it didn't require all properties to be marked as |
Here might be a use case that I'm currently running into. I'm using zzzprojects' entity framework plus to add audit tables to my project. It's a third-party library for which I don't (but theoretically could, let's just say I don't) have access to source. If I use this third-party dll, which was not configured for lazy loading entities, when I enable lazy loading I get an exception stating that these entities must be virtual. I have no way to change that. Since there will (hopefully) be many third-party libraries providing entities and code, I'd say that it may behoove the team to come up with a way for us to enable lazy loading for our own entities while releasing other entities of the requirement to do so. |
I don´t know how long it needs to implement many-to-many support without join-table - but as long as this is not supported @ajcvickers great workaround (https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-4-a-more-general-abstraction/) cannot be used as is, because of the private Navigation to the join-table. Since it is possible to workaround this by using a |
Part of #10787 Also fixes two issues with service properties and their delegates: - The delegate is now always created pointing to the instance of the service property stored in the entity. Previously, it could sometimes be a delegate pointing to a new service instance. - The service property instance is always treated as an IInjectableService when attaching or materializing, so the metadata is always present on the instance.
Part of #10787 Like the EF6 behavior, but opt-in.
Part of #10787 Also fixes two issues with service properties and their delegates: - The delegate is now always created pointing to the instance of the service property stored in the entity. Previously, it could sometimes be a delegate pointing to a new service instance. - The service property instance is always treated as an IInjectableService when attaching or materializing, so the metadata is always present on the instance.
Field only navigations will still throw an exception in |
@sprengo Even if they are configured to not lazy-load? Or if proxies are configured to ignore virtual properties? |
Yes, that's because this check
comes before your mentioned checks. |
…the navigation Also when non-virtual navigations are allowed. Fixes #10787 (comment)
Currently when lazy-loading proxies are used every entity type in the model must be suitable to proxy and all navigations must be virtual. This issue is about allowing some entity types/navigations to be lazy-loaded while others are not.
It is not clear how important this is. If this is something you would use, then please comment and describe the scenario.
The text was updated successfully, but these errors were encountered: