Как в 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();
}