Skip to content
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

UseLazyLoadingProxies does not work when model is built outside of OnModelCreating #15532

Open
Tracked by #22952
pravinkarthy opened this issue Apr 30, 2019 · 11 comments

Comments

@pravinkarthy
Copy link

pravinkarthy commented Apr 30, 2019

Lazy loading of associated entity objects does not work when model is built outside of OnModelCreating and the associated object despite keeping all the methods virtual.

Eg.,

    `protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        { 
            optionsBuilder
                .UseLazyLoadingProxies()
                .UseModel(new ModelBuilderService().GetOrCreateCompiledModel())
                .UseSqlServer(@"connectionstring",
                    sqlOption => sqlOption.UseNetTopologySuite());

        }`

Further technical details

Lazy loading works only when models are built overriding onModelCreating() method

EF Core version: 2.2.4
Database Provider: 2.2.4
Operating system: Windows
IDE: Visual Studio 2017 15.4

@ajcvickers
Copy link
Member

@pravinkarthy Can you post the code you are using to build the model?

@pravinkarthy
Copy link
Author

pravinkarthy commented Apr 30, 2019

@ajvickers Thanks for responding. Here is the code that I use for ModelBuilding.

public class ModelBuilderService 
    {
        private static IModel GetOrCreateCompiledModel(IEnumerable<string> modelSupplyingAssemblyPatterns)
        {
            var conventions = SqlServerConventionSetBuilder.Build();
            var modelBuilder = new ModelBuilder(conventions);

            var modelBuilderType = typeof(ModelBuilder);
            var entityMethod = modelBuilderType.GetMethod("Entity", modelBuilderType.GetGenericArguments());
            var pathToUse = AppDomain.CurrentDomain.BaseDirectory;

            if (!AppDomain.CurrentDomain.BaseDirectory.Contains("bin"))
            {
                pathToUse = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");
            }

            var entitiesAdded = new HashSet<string>();

            if (entityMethod == null)
            {
                throw new NullReferenceException("Cannot find Entity method on DbModelBuilder");
            }

            foreach (var assemblyPattern in modelSupplyingAssemblyPatterns)
            {
                var dataProviderModels = Directory.EnumerateFiles(pathToUse, assemblyPattern, SearchOption.AllDirectories);

                foreach (var dll in dataProviderModels)
                {
                    var assembly = Assembly.LoadFrom(dll);

                    modelBuilder.ApplyConfigurationsFromAssembly(assembly);

                    var typesToRegister = assembly.GetTypesInheritingFrom<BaseObject>();

                    foreach (var entity in typesToRegister)
                    { 
                        if (entitiesAdded.Add(entity.FullName))
                        {
                            entityMethod.MakeGenericMethod(entity)
                                        .Invoke(modelBuilder, new object[] { });
                        }
                    }
                }
            } 
            return modelBuilder.Model;
        } 
    }

@ajcvickers
Copy link
Member

@pravinkarthy Thanks!

Note for triage: the issue here is that the proxies package adds a convention, but there is no way for this to be picked up when creating the model builder externally.

@pravinkarthy
Copy link
Author

pravinkarthy commented Apr 30, 2019

@ajcvickers We used the same code in EF6 for lazyloading. But on our .NET core transformation, got blocked with this issue with EFCore since we follow a generic pattern to fetch entities.
Is eager loading the only workaround for this issue or is there a way to create proxy object for entities and convert the actual entity into that proxy object to fetch related entities? Would really appreciate your advice on this since we have completed the .NET core transformation for our application and this is a major blocker for us to move ahead.
Thanks.

@pravinkarthy
Copy link
Author

@ajcvickers Tried injecting the ILazyLoader service into an entity as a workaround but even that did not help for fetching many-many related entities where data was loading only for the first entity of many-many relationship but did not load the data for the second object.

` public partial class PersonOrganisation
{
public int Cluster { get; set; }
[Column("Person_ID")]
public Guid? PersonId { get; set; }
[Column("Organisation_ID")]
public Guid? OrganisationId { get; set; }

    [ForeignKey("OrganisationId")]
    [InverseProperty("PersonOrganisations")]
    public virtual Organisation Organisation { get; set; }
    [ForeignKey("PersonId")]
    [InverseProperty("PersonOrganisations")]
    public virtual Person Person { get; set; }
}

public partial class Person
{
    private ICollection<PersonOrganisation> personOrganisations;
    private Person(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }
    [InverseProperty("Person")]
    public virtual ICollection<PersonOrganisation> PersonOrganisations {
        get => LazyLoader.Load(this, ref personOrganisations);
        set => personOrganisations = value;
    }

}`

On LazyLoading of this "PersonOrganisations" collection, each "PersonOrganisation" object had only "Person" object in "PersonOrganisation" Entity getting fetched but the "Organisation" object was null and not fetched.

@ajcvickers
Copy link
Member

@pravinkarthy We haven't fully investigated this yet, but my initial thoughts are that it is the ProxiesConventionSetCustomizer (see https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore.Proxies/Proxies/Internal/ProxiesConventionSetCustomizer.cs) that is not getting registered.

@pravinkarthy
Copy link
Author

On analysis, found out that the lazyloadingproxies applies conventions after the entities are built with "OnModelCreating()". But in this case where models are built outside using modelbuilder, we are forced to set the conventionset object during the Modelbuilder object creation itself and then adding the entities to the Modelbuilder which seems to be root-cause of the issue here.

@ajcvickers
Copy link
Member

Notes from triage: we will allow extensions to expose conventions in a way that they can then be plugged into a stand-alone model builder.

@AndriySvyryd
Copy link
Member

Depends on #214 and possibly on #9329

@Luigi6821
Copy link

Is the issue solved or still in backlog?
I have same problem using preview version 7.0.0-preview.7.22376.2
Thanks in advance

@ajcvickers
Copy link
Member

@Luigi6821 As the milestone indicates, this is still in the backlog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants