samedi 4 août 2018

How can you implement the Stairway pattern with an Entity Framework Core solution?

So... I'm trying to follow the stairway pattern in a .Net Core Entity Framework project. I'm using EF Core 2.1.

My Solution Structure is:

MyApp.Api    
    MyApp.Data
    MyApp.Data.Interfaces
    MyApp.Logic
    MyApp.Logic.Interfaces

e.g. the API (as dependency root) depends on all the rest, but the other projects only depend on abstractions.

MyApp.Data  
    MyApp.Data.Interfaces
MyApp.Logic
    MyApp.Logic.Interfaces
    MyApp.Data.Interfaces

I've got a complex data model, with child navigation properties. The problem I'm having is the input json matches the schema of the complex type, but isn't deemed valid when it hits the validation/model-binding stage (i.e. it doesn't hit the Action method). If I remove the child props, it accepts it as valid input (I'm using the [FromBody] attribute, btw).

For reference, here's the Controller method:

[HttpPost]
public IActionResult Post([FromBody] House house)
{
    var item = houseService.Add(house);
    return CreatedAtRoute("GetById", new { id = item.Id }, item);
}

The problem code is in the MyApp.Data project (which only has a reference to the MyApp.Data.Interfaces project). For the child/navigation props I'm using interfaces, like so:

public interface IHouse 
{
    string Name {get;set;}
    IEnumerable<IRoom> Rooms {get;set;}    
}

public interface IRoom
{
    int Windows {get;set;}
}

public class House : IHouse 
{
    public string Name {get;set;}
    public IEnumerable<IRoom> Rooms {get;set;}
}

If I post some Json to that endpoint, I get an error:

{
    "name": "Dunroamin",
    "rooms": [
        {
            "windows": 2
        }
    ]
}

HTTP 400: Bad Request
{
    "rooms[0].windows": [
        "The input was not valid."
    ]
}

If I change the code to the following (replace the child property's interface with a concrete type), the error goes away.

public interface IHouse 
{
    string Name {get;set;}
    IEnumerable<Room> Rooms {get;set;}    
}

public interface IRoom
{
    int Windows {get;set;}
}

public class House : IHouse 
{
    public string Name {get;set;}
    public IEnumerable<Room> Rooms {get;set;}
}

But this violates the stairway pattern, because I'm using a concrete type in my MyApp.Data.Interfaces project. I've seen an implied reference to difficulty when it comes to EF Core and the stairway pattern, but no suggestion of a solution. I can see the problem is with EF needing a concrete type in order to map it to a SQL query, but how can these two things co-exist? Is it worth it?

I'm not committed to this architecture, btw, I just wanted to practice using it again and I'm stubborn, so am willing to make it work for the sake of it...

Aucun commentaire:

Enregistrer un commentaire