JamesT
JamesT

Reputation: 3028

Breezejs error when loading an entity with related data

I'm no doubt missing something obvious here so perhaps the collective power of the internet can point it out to me.

I have a code first database structure, consisting of Users, Groups and a GroupUsers table to link the two (EF can handle n-n relationships itself but breezejs needs the table explicitly https://stackoverflow.com/a/13636530/249813).

I'm then trying to load users via Breezejs at the client. This works fine when a user is not a member of any groups, but fails when they are with the following message.

object #<object> has no method getproperty

Could anyone show me where I'm going wrong?

Database:

public class MyContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Group> Groups { get; set; }
    public DbSet<GroupUser> GroupUsers { get; set; }
}

public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    public string Name { get; set; }

    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    public bool IsActive { get; set; }
    public bool IsAdmin { get; set; }
    public virtual ICollection<GroupUser> Groups { get; set; }
}

public class Group
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    public string Name { get; set; }
    public virtual ICollection<GroupUser> Users { get; set; } 
}

public class GroupUser
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [ForeignKey("User")]
    public Guid UserId { get; set; }

    [ForeignKey("Group")]
    public Guid GroupId { get; set; }

    public virtual Group Group { get; set; }
    public virtual User User { get; set; }
}

Javascript

var serviceName = '/breeze/admin';
var manager = new breeze.EntityManager(serviceName);
var query = breeze.EntityQuery.from('Users');

manager.executeQuery(query)
    .then(function(data) {
        //do something good
    })
    .fail(function(error) {
        //do something bad.
    });

JSON produced by Breeze API controller when user isn't a member of a group (Works fine)

[
    {
        $id: "1",
        $type: "System.Data.Entity.DynamicProxies.User_41E3B55B982835BEAAA863D9A28B60E1A07D42FBEB52862F05DCD4BAE3F6171C, EntityFrameworkDynamicProxies-MyBreezeTest",
        Groups: [ ],
        Id: "665b2a59-f1ae-41c6-8d6b-df758713db01",
        Name: "Me",
        Email: "[email protected]",
        IsActive: true,
        IsAdmin: true
    }
]

JSON produced by Breeze API controller when user IS a member of a group (Fails)

[
    {
        $id: "1",
        $type: "System.Data.Entity.DynamicProxies.User_41E3B55B982835BEAAA863D9A28B60E1A07D42FBEB52862F05DCD4BAE3F6171C, EntityFrameworkDynamicProxies-MyBreezeTest",
        Groups: [
            {
                $id: "2",
                $type: "System.Data.Entity.DynamicProxies.GroupUser_144E644846F6E074946B6DFE31A643AFBBDA0EF263BEBCAA9A6A12E67649A7CF, EntityFrameworkDynamicProxies-MyBreezeTest",
                Group: {
                    $id: "3",
                    $type: "System.Data.Entity.DynamicProxies.Group_4EDDE8FACA0560ADBB390090E3A6A147D06867A4A693B111AECF35D2CE7E458C, EntityFrameworkDynamicProxies-MyBreezeTest",
                    Users: [
                        {
                            $ref: "2"
                        }
                    ],
                    Id: "86365a48-6f45-4614-b0b0-8011ec0e0d77",
                    Name: "Test Group"
                },
                User: {
                    $ref: "1"
                },
                Id: "952faa7b-658d-4e12-8f18-dc9b0898d684",
                UserId: "665b2a59-f1ae-41c6-8d6b-df758713db01",
                GroupId: "86365a48-6f45-4614-b0b0-8011ec0e0d77"
            }
        ],
        Id: "665b2a59-f1ae-41c6-8d6b-df758713db01",
        Name: "Me",
        Email: "[email protected]",
        IsActive: true,
        IsAdmin: true
    }
]

Edit For Future Readers

Margabit's answer below correctly identifies the problem, however for ease of reference the simplest way to solve this is not do this in the breeze controller:

var context = new MyContext();

but instead do this

var context = _myEFContextProvider.Context;

Since EFContextProvider will disable proxy creation and lazy loading on it's own instance and allow you to keep your virtual and avoid globally disabling lazy loading in the context constructor.

Upvotes: 2

Views: 2099

Answers (1)

margabit
margabit

Reputation: 2954

I think you might be using Lazy Loading in EF. This is why you have Dynamic Proxies in the navigation properties.

In Breeze documentation says that Lazy Loading is not an option for Breeze:

Lazy loading

On several occasions we’ve alluded to the possibility that a related entity exists in remote storage but is not presently in cache. Breeze navigation properties only return entities that are in cache.

In some systems, a navigation property would automatically fetch the related entities from remote storage if they were not already in cache. This process is often called “lazy loading” or “deferred loading” because related entities are only loaded into cache when requested.

Lazy loading is not a Breeze option. Breeze expects you to load related entities explicitly. You can load them either directly by querying for them or indirectly as a byproduct of another query.

So you will need to disable Lazy Load and load the Groups and Type entities explicitly.

You can remove the Lazy Loading by removing the virtual in the navigation properties (just the ones that you don't want to load) or globally by setting in the context:

  this.Configuration.LazyLoadingEnabled = false; 

You can include the navigation properties by:

[HttpGet]
public IQueryable<YourEntity> Orders() {
    return _contextProvider.Context.YourEntities.Include("Groups").Include("Type");
}  

Or by loading them in the Query from JavaScript:

EntityQuery.from("YourEntities")
.expand("Groups", "Type");

References:

EFContextProvider

Navigation Properties

Upvotes: 3

Related Questions