Implementing WebAPI with circular dependencies and

2019-09-08 11:11发布

问题:

How it is possible to implement such "smart" GET in WebAPI? I have 2 classes that i want to get from my service. Let's say its book store which has relation to book objects.

public class Book
{
    Guid Id { get; set; }
    string Title { get; set; }
    Store Store { get; set; }
}

public class Store
{
    Guid Id { get; set; }
    string Title { get; set; }
    string Owner { get; set; }
    IEnumerable<Book> Books { get; set; }
}

I have backend with SQL server and EntityFramework adapter in code. How should I make to return the list of books only after I need it (call for it), like with OData? As you can see I have circular dependancies, which makes my JSON serializer to show errors. I resolve it with:

config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling  = Newtonsoft.Json.ReferenceLoopHandling.Serialize; 
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;

but serialized all data and my response is HUGE, which is not good for my Windows Phone app. In some cases I need only Store object, but in other cases I need Store with all referenced books.

In controllers I user [Queryable] attribute to use OData queries. P.S. I can't create more web-methods like GetBooks, GetStore and so on. It should be one method.

How could I resolve my problem?

回答1:

How could I resolve my problem?

Do not return Entities from your API action methods.

Instead, return models.

So instead of this:

public IEnumerable<Store> GetStores()
{
    using (var db = new MyDbContext())
    {
        return db.Stores;
    }
}

Do something like this:

public IEnumerable<StoreModel> GetStores()
{
    using (var db = new MyDbContext())
    {
        var entities = db.Stores;
        return entities.Select(x => new StoreModel
        {
            Id = x.Id,
            Title = x.Title,
            Owner = x.Owner,
            Books = x.Books.Select(y => new BookModel
            {
                Id = y.Id,
                Title = y.Title,
                StoreId = x.Id,
            }),
        });
    }
}

By doing it this way you eliminate the circular dependency graph. Book no longer references Store, only StoreId.



回答2:

You need to make it hypermedia driven. Books and stores can be thought of as different resources. When a GET request is issued against a particular book, like @danlugwig has suggested, you can return a book model along with the URIs for its corresponding store. The consumer can make use of this URI to retrieve the Store details. Similarly for Store resource.