Issues including navigation properties in query

£可爱£侵袭症+ 提交于 2019-12-13 14:13:46

问题


I am using Web API 2 with CORS/Entity Framework 6/Breeze.js from bower for a single page application and the breeze query is not expanding the navigation property.

I'll break it down from server to client.

// POCOs

public class Foo
{
    public int Id { get; set; }
    public Bar Bar { get; set; }
}

public class Bar
{
    public int Id { get; set; }
    public string SomeData { get; set; }
}

public class FooMap : EntityTypeConfiguration<Foo>
{
    public FooMap()
    {
        HasKey(t => t.Id);

        ToTable("Foo");
        Property(t => t.Id).HasColumnName("Id");

        HasRequired(t => t.Bar).WithMany().Map(t => t.MapKey("BarId"));
    }
}

// Web API Config // omitting static class and default route // config is HttpConfiguration

config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

// Breeze Controller

[BreezeController]
public class FooController : ApiController
{
    readonly EFContextProvider<SomeDbContext> _contextProvider =
        new EFContextProvider<SomeDbContext>();

    [HttpGet]
    public string Metadata()
    {
        return _contextProvider.Metadata();
    }

    [HttpGet]
    public IQueryable<Foo> Foos()
    {
        return _contextProvider.Context.Foos;
    }
}

// Front end land

// main.js

breeze.NamingConvention.camelCase.setAsDefault();

var FooService = (function(breeze) {
    var service = "http://localhost:58996/breeze/Foos";
    breeze.config.initializeAdapaterInstances({ dataService: 'webApi' });

    var manager = new breeze.EntityManager(service);
    var entityQuery = new breeze.EntityQuery();

    this.getAll = function(callback, errorCallback) {
        return manager.executeQuery(entityQuery.from('Foo').take(10).expand('Bar'), callback, errorCallback);
    };
})(window.breeze);

var fooService = new FooService();
fooService.getAll(function(data) {
    console.log(data);
}, function(error) {
    console.log(error);
});

Fiddler shows the JSON payload:

[
   {
      "$id":"1",
      "$type":"DataAccess.Models.Foo, DataAccess",
      "Id":"10",
      "Bar":{
         "$id":"2",
         "$type":"DataAccess.Models.Bar, DataAccess",
         "Id":"12",
         "SomeData":"Hello World"
      }
   }
]

but bar is not a field of the object in the array in chrome.


Edit:

The solution is to add a property to hold BarId and set it as a foreign key.

public class Foo
{
    public int Id { get; set; }
    public int BarId { get; set; }
    public Bar Bar { get; set; }
}

public class FooMap : EntityTypeConfiguration<Foo>
{
    public FooMap()
    {
        HasKey(t => t.Id);

        ToTable("Foo");
        Property(t => t.Id).HasColumnName("Id");
        Property(t => t.BarId).HasColumnName("BarId");

        HasRequired(t => t.Bar).WithMany().HasForeignKey(t => t.BarId);
    }
}

回答1:


What leaps out at me is that you do not have a Foreign Key (FK) property in the dependent Foo entity.

You have identified the FK column in the database table ("Foo.BarId") through the t.MapKey expression in your mapping.

HasRequired(t => t.Bar).WithMany().Map(t => t.MapKey("BarId"));

But you deliberately did not create a corresponding BarId FK property in your Foo entity.

Entity Framework can retrieve object graphs for associations that lack FK properties. Because you told Entity Framework about the FK column, it can materialize both the Foo and its Bar on the server. That's why you see related Bar data on the wire.

But BreezeJS lacks access to the database ... nor should it have any database awareness at all. Breeze only knows about (through metadata) your entity type properties. Without the FK property, Breeze has no way to associate a Bar with its parent Foo.

BreezeJS was probably able to materialize the Bar entity in cache. I'll bet (no promise) you'll find it there if you break in the success callback and execute the following in the console:

manager.getEntities('Bar');

But to BreezeJS, this is just another entity in cache. Breeze cannot populate the Foo.Bar navigation property because it does not know that or how the Foo and the Bar are related. Breeze needs the FK property to implement that Foo.Bar navigation property.

This is why we say that "BreezeJS associations must be supported by FK properties."

It takes considerable skill and knowledge - beyond the scope of this answer - to work with associations that lack FK properties. You would have to maintain the navigation properties with your own code; Breeze wouldn't be able to do it. My recommendation (including to myself): don't go there.

All should be well if you redefine "Foo.cs" as

public class Foo
{
    public int Id { get; set; }
    public int BarId { get; set; } // Bar nav's FK
    public Bar Bar { get; set; }
}

Btw, in case anyone wonders, CORS is not a factor in this question.



来源:https://stackoverflow.com/questions/20692463/issues-including-navigation-properties-in-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!