Как в ASP.NET Web API 2 сохранить ссылки на объекты при сериализации JSON?

У меня есть REST API, созданный с использованием ASP.NET Web API 2 и Entity Framework 6. Я хочу сериализовать / десериализовать объекты и сохранять ссылки на другие объекты.

Я определил класс модели Tag в ASP.NET, который также имеет класс контроллера vanilla, сгенерированный scaffolding:

// C#
// Tag.cs
public class Tag
{
    public int ID { get; set; }
    public string Value { get; set; }
    public Tag Parent { get; set; } // I know this can't be circular 
}

...

// Context.cs
...
public DbSet<Tag> Tag { get; set; }
...
modelBuilder.Entity<Tag>().HasOptional(_ => _.Parent);

Я создаю клиент на Python с модулями запросов и jsonPickle. У меня есть эквивалентный класс, определенный в Python:

# Python 
class Tag():
    def __init__(self, jSrc=None):
        #super(DBEntity,self).__init__()        
        self.ID = 0        
        self.Value = ""
        self.Parent = None

        if(jSrc != None):
             self.__dict__ = jsonpickle.decode(jSrc)

Следующий код Python работает правильно для создания записи тега в таблице тегов:

newTag = Tag()
newTag.Value = "Parent"
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post("http://server/api/Tags", data=jsonpickle.encode(newTag), headers=headers)


# ID | Value  | Parent_ID
# 1  | Parent | -

Но когда я назначаю родительский тег объекту тега с правильным набором идентификаторов, вместо используемой ссылки на родительский идентификатор создаются 2 новые записи тегов:

# Get Tag
rg = requests.get("http://server/api/Tags/1")
parentTag = Tag(rg.text)

# Create new Tag with Parent reference
newTag = Tag()
newTag.Value = "Child"
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
r = requests.post("http://server/api/Tags", data=jsonpickle.encode(newTag), headers=headers)

# ID | Value  | Parent_ID
# 1  | Parent | -
# 2  | Parent | - << This should net be created
# 3  | Child  | 2 << This should be 1

База данных теперь содержит 2x тега "Parent", однако в поле "Parent_ID" тега "Child" правильно установлен новый неправильно созданный тег "Parent".

Как я могу сказать системам ASP.NET найти идентификатор родительского тега и сохранить ссылку на исходный тег, а не просто десериализовать свойство Parent в новую запись тега?

1 ответ

Каждый запрос к ASP.NET, должен получить новый DbContext. Это означает, что каждый раз, когда вам нужно обновить базу данных, вы должны извлечь запись, которую вы хотите обновить, а затем обновить ее, прежде чем SaveChanges,

Если вы просто десериализовали объект и SaveChanges, он (как вы видите) создаст новую запись в базе данных.

Я рекомендую вам реализовать стандартный трехуровневый механизм с моделями представлений в вашем API/Controller, DTO на вашем бизнес-уровне и сущностями (которые у вас есть в настоящее время) на уровне базы данных.

Простое (но я бы посоветовал ошибочное) исправление было бы в вашем контроллере сделать что-то вроде:

public void MyAPI(ApiViewModel vm) 
{
 var entity = _dbContext.Find(tag.Id);
 entity.Value = vm.Value;
 _dbContext.SaveChanges();
 return Ok();
}
Другие вопросы по тегам