Yoda
Yoda

Reputation: 18068

Entity Framework does not load related data when entities are not public

I have two classes in console application.

Person:

 class Person {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public virtual ICollection<Order> Orders { get; set; }

        public override string ToString() {
            return Id + " " + FirstName + " " + LastName; 
        }
    }

and Order:

 class Order {
        //Id to domyslna nazwa na identyfikator -> klucz glowny
        public int Id { get; set; }
        public int Total { get; set; }
        public virtual Person Owner { get; set; }

        public override string ToString() {
            return Id + " " + Total;
        }
    }

My dbContext is:

    class ApplicationDbContext : DbContext {
        public DbSet<Person> Persons { get; set; }
        public DbSet<Order> Orders { get; set; }
        public ApplicationDbContext()
            : base("Olek2") {
                Configuration.LazyLoadingEnabled = true;
                Configuration.ProxyCreationEnabled = true;

        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
       public static ApplicationDbContext Create() {
        return new ApplicationDbContext();

      }
}

I generated fresh database by update-database.

In Main method I try to fetch Person and then access person's orders but I get null pointer exception on p1.Orders.Count(). I made properties virtual, also enabled lazy loading, i don't know where the problem is.

    static void Main(string[] args) {
        ApplicationDbContext context = new ApplicationDbContext(); 
        Person p1 = context.Persons.Find(1);
        Console.WriteLine(p1); // THIS WORKS
        Console.WriteLine(context.Orders.Count()); //THIS IS 1
        Console.WriteLine(p1.Orders.Count()); //EXCEPTION HERE 
        Console.ReadKey();
    }

Also my Seed method:

protected override void Seed(ApplicationDbContext context) {
        Person person = null;

        if (!context.Persons.Any()) {
             person = new Person() {  FirstName = "Alaaaaa", LastName = "Kowlaska" };
            context.Persons.AddOrUpdate(person);
        }
        if (!context.Orders.Any()) {
            context.Orders.AddOrUpdate(new Order() { Total = 100, Owner = person });
        }

        context.SaveChanges();
    }

EDIT:

I got a hunch that it has something to do with public static ApplicationDbContext Create() has 0 references (VS2013 indicates that) in my console application(which fails).

In my ASP.NET-MVC(works fine) it was referenced by:

 public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context and user manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);

        ....
       } 

EDIT2 I had a hunch it does not seem to be connected anyhow.

Upvotes: 2

Views: 749

Answers (1)

haim770
haim770

Reputation: 49095

Since your entities are not marked with the public modifier (which means by default the compiler will make them internal), EF will not be able to generate proxies for the entity type.

Looking at the source code of EntityProxyFactory (line 577):

private static bool CanProxyType(EntityType ospaceEntityType)
{
    TypeAttributes access = ospaceEntityType.ClrType.Attributes & TypeAttributes.VisibilityMask;

    ConstructorInfo ctor = ospaceEntityType.ClrType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, Type.EmptyTypes, null);
    bool accessableCtor = ctor != null && (((ctor.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) ||
                                          ((ctor.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family) ||
                                          ((ctor.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem));

    return (!(ospaceEntityType.Abstract ||
             ospaceEntityType.ClrType.IsSealed ||
             typeof(IEntityWithRelationships).IsAssignableFrom(ospaceEntityType.ClrType) ||
             !accessableCtor) &&
             access == TypeAttributes.Public);
}

You can clearly notice the last line of CanProxyType checking whether the entity type is indeed public.

Now, since you don't initialize Person.Orders (in the constructor or anywhere else) and no proxy is involved to intercept the call to Orders (initialize it and detect the association to Order.Person), you end up with Orders being null and NRE is thrown.

Upvotes: 2

Related Questions