Получение отношения один-к-одному для работы в Azure Appservice с использованием EF Code First - PullRequest
0 голосов
/ 23 мая 2019

Мне трудно заставить это работать, поэтому, может быть, вы можете помочь мне здесь.

Используемые технологии

  • Xamarin.Forms
  • Служба приложений Azure
  • Azure SQL
  • AzureMobileServices (MobileServiceClient, MobileServer)
  • Первый код структуры сущности

Введение

Приложение «Мой Xamarin.Forms» используется пользователями с адресами выставления счетов и доставки.Схема базы данных выглядит следующим образом:

Пользователи

  • Id
  • BillingAddress_Id
  • DeliveryAddress_Id

Адреса

  • Id
  • User_Id

При добавлении новых адресов я в основном делаю что-то подобное:

var user = await userService.GetUserByIdAsync(...);

var billingAddress = new Address
{
  UserId = user.Id,
  …
};

var deliveryAddress = new Address
{
  UserId = user.Id,
  …
}

user.BillingAddress = billingAddress;
user.DeliveryAddress = deliveryAddress;

await addressService.AddNewAddressAsync(billingAddress);
await addressService.AddNewAddressAsync(deliveryAddress);

await userService.UpdateUser(user);

AddNewAddressAsync - это сервисный метод, который в конечном итоге заставляет базовый репозиторий создавать новый адрес, подобный этому:

public async Task<Address> CreateAsync(Address item)
{
  await Addresses.InsertAsync(item);
  return item;
}

с адресами , являющимисяэкземпляр интерфейса IMobileServiceTable, созданный следующим образом:

private IMobileServiceTable<Address> Addresses
{
  get
  {
    var table = App.Client.GetTable<Address>();

    table.MobileServiceClient.SerializerSettings.ReferenceLoopHandling =     
      Newtonsoft.Json.ReferenceLoopHandling.Ignore;

    return table;
  }
}

UpdateUser , с другой стороны, должен исправлять существующего пользователя, вызывая базовый репозиторий, например:

public async Task<User> PatchAsync(User item)
{
  await Users.UpdateAsync(item);
  return item;
}

С Users , также являющимся экземпляром интерфейса IMobileServiceTable, создается так же, как и Адреса до:

private IMobileServiceTable<User> Users
{
  get
  {
    var table = App.Client.GetTable<User>();    

    table.MobileServiceClient.SerializerSettings.PreserveReferencesHandling = 
      Newtonsoft.Json.PreserveReferencesHandling.Objects;

    return table;
  }
}

На стороне сервера это то, чтоконтроллеры делают:

AddressCКонтроллер

[Authorize]
public class AddressController : TableController<Address>
{
  protected override void Initialize(HttpControllerContext controllerContext)
  {
    base.Initialize(controllerContext);
    var context = new AppContext();
    DomainManager = new EntityDomainManager<Address>(context, Request);
  }

  public async Task<IHttpActionResult> PostAddress(Address address)
  {
    Address current = await InsertAsync(address);
    return CreatedAtRoute("Tables", new { id = current.Id }, current);
  }

  ...
}

UserController

[Authorize]
public class UserController : TableController<User>
{
  protected override void Initialize(HttpControllerContext controllerContext)
  {
    base.Initialize(controllerContext);
    var context = new AppContext();
    DomainManager = new EntityDomainManager<User>(context, Request);
  }

  public Task<User> PatchUser(string id, Delta<User> patch)
  {
    return UpdateAsync(id, patch);
  }

  …
}

Вот что регистрируется при выполнении вышеуказанного кода:

Вставкапервого адреса

Запрос: POST http://localhost:51546/tables/Address {"$ id": "1", "id": null, ..., "UserId": "8953d3deb9b2459796aa00f43d7416cb",«Пользователь»: null} iisexpress.exe Информация: 0: Запрос, Метод = POST, URL = http://localhost:51546/tables/Address, Сообщение = 'http://localhost:51546/tables/Address' ... iisexpress.exe Информация: 0: Сообщение =' Модельсостояние действительно.Значения: address = SpenceAppService.DataObjects.Address ', Operation = HttpActionBinding.ExecuteBindingAsync ... INSERT [dbo]. [Адреса] ([Id], [UserId], ...) ЗНАЧЕНИЯ (@ 0, @ 1, ...) ВЫБРАТЬ ... ИЗ [dbo]. [Адреса] ГДЕ @@ ROWCOUNT> 0 И [Id] = @ 0 - @ 0: '2a407222f6984052b90d233fa9935286' (Тип = Строка, Размер = 128) - @ 1: '8953d3deb9b2459796aa00f43d7416cb '(Type = String, Size = 128) ... - Выполнение асинхронно в 23.05.2019 13:22:10 +02: 00 - Завершение за 7 мс с результатом: SqlDataReader

Подтвержденная транзакция в23.05.2019 13:22:11 +02: 00 Закрытое соединение в 23.05.2019 13:22:11 +02: 00 ... iisexpress.exe Информация: 0: Ответ, Статус = 201 (Создан), Метод = POST,Url = http://localhost:51546/tables/Address, Message = 'Content-type =' application / json;charset = utf-8 ', content-length = unknown' ... Ответ: Создан {"userId": "8953d3deb9b2459796aa00f43d7416cb", ..., "id": "2a407222f6984052b90d233fa9935286", ...}

Вставка второго адреса

Запрос: POST http://localhost:51546/tables/Address {"$ id": "1", "id": null, ..., "UserId":"8953d3deb9b2459796aa00f43d7416cb", "Пользователь": null} iisexpress.exe Информация: 0: Запрос, Метод = POST, URL = http://localhost:51546/tables/Address, Сообщение = 'http://localhost:51546/tables/Address' ... iisexpress.exe Информация: 0:Сообщение = 'Состояние модели действительно.Значения: address = SpenceAppService.DataObjects.Address ', Operation = HttpActionBinding.ExecuteBindingAsync ... INSERT [dbo]. [Адреса] ([Id], [UserId], ...) ЗНАЧЕНИЯ (@ 0, @ 1, ...) ВЫБРАТЬ ... ИЗ [dbo]. [Адреса] ГДЕ @@ ROWCOUNT> 0 И [Id] = @ 0 - @ 0: 'a56e1ca3a7b341a39bc00d22772e39e5' (Тип = Строка, Размер = 128) - @ 1: '8953d3deb9b2459796aa00f43d7416cb '(Type = String, Size = 128) ... - Выполнение асинхронно в 23.05.2019 13:22:11 +02: 00 - Завершено за 0 мс с результатом: SqlDataReader

Подтвержденная транзакция в23.05.2019 13:22:11 +02: 00 Закрыто соединение в 23.05.2019 13:22:11 +02: 00 ...iisexpress.exe Информация: 0: Ответ, Статус = 201 (Создан), Метод = POST, URL = http://localhost:51546/tables/Address, Сообщение = 'Content-type =' application / json; charset = utf-8 ', content-length = unknown' ... Ответ: Создан { "Идентификатор пользователя": "8953d3deb9b2459796aa00f43d7416cb", ..., "идентификатор": "a56e1ca3a7b341a39bc00d22772e39e5", ...}

Патч пользователя

Запрос: PATCH http://localhost:51546/tables/User/8953d3deb9b2459796aa00f43d7416cb { "$ ID": "1", "идентификатор": "8953d3deb9b2459796aa00f43d7416cb", ..., "BillingAddress": { "$ ID": "7", "идентификатор": "2a407222f6984052b90d233fa9935286", ..., "UserId ":" 8953d3deb9b2459796aa00f43d7416cb», "Пользователь": NULL}, "DeliveryAddress": { "$ ID": "8", "идентификатор": "a56e1ca3a7b341a39bc00d22772e39e5", ..., "UserId": "8953d3deb9b2459796aa00f43d7416cb", "Пользователь" :ноль}} iisexpress.exe Информация: 0: Запрос, Метод = PATCH, URL = http://localhost:51546/tables/User/8953d3deb9b2459796aa00f43d7416cb, Сообщение = 'http://localhost:51546/tables/User/8953d3deb9b2459796aa00f43d7416cb' ... iisexpress.exe Информация: 0: Сообщение = 'Состояние модели действительно. Значения: id = 8953d3deb9b2459796aa00f43d7416cb, patch = System.Web.Http.OData.Delta`1 [SpenceAppService.DataObjects.User] ', Operation = HttpActionBinding.ExecuteBindingAsync ... Открыто соединение асинхронно в 23.05.2019 13:22:30 +02: 00 ВЫБРАТЬ ... [Limit1]. [Id] AS [Id], ... [Limit1]. [BillingAddress_Id] AS [BillingAddress_Id], [Limit1]. [DeliveryAddress_Id] AS [DeliveryAddress_Id] ОТ (ВЫБЕРИТЕ ТОП (1) [Extent1]. [Id] AS [Id], ... [Extent1]. [BillingAddress_Id] AS [BillingAddress_Id], [Extent1]. [DeliveryAddress_Id] AS [DeliveryAddress_Id] FROM [dbo]. [Users] AS [Extent1] ГДЕ [Extent1]. [Id] = @ p__linq__0 ) AS [Limit1] - p__linq__0: '8953d3deb9b2459796aa00f43d7416cb' (тип = строка, размер = 4000) ...

INSERT [dbo]. [Адреса] ([Id], [UserId], ...) ЦЕННОСТИ (@ 0, @ 1, ...) ВЫБРАТЬ ... ОТ [dbo]. [Адреса] ГДЕ @@ ROWCOUNT> 0 И [Id] = @ 0 - @ 0: '2a407222f6984052b90d233fa9935286' (тип = строка, размер = 128) - @ 1: '8953d3deb9b2459796aa00f43d7416cb' (тип = строка, размер = 128) ... - Выполнение асинхронно в 23.05.2019 13:22:30 +02: 00 - Ошибка в 1 мс с ошибкой: нарушение ограничения PRIMARY KEY 'PK_dbo.Addresses'. Невозможно вставить дубликат ключа в объект 'dbo.Addresses'. Дубликат значения ключа (2a407222f6984052b90d233fa9935286). Заявление было прекращено. ... Запрос не может быть завершен. (Конфликт)

Задача

Как видите, при исправлении пользователя базовый метод пытается также вставить адреса, которые уже были вставлены, поэтому сообщение об ошибке имеет смысл.

Однако, как мне заставить это работать?

Когда я сначала не вставляю адреса, они не получают идентификаторы с сервера, поэтому, когда userService исправляет пользователя, который, в свою очередь, пытается вставить адреса (которые тогда еще не существуют), я получаю сообщение об ошибке, сообщающее, что поле Id (адреса) является обязательным.

Когда я добавляю поля Id для обоих BillingAddress и DeliveryAddress к объекту пользователя и пропускаю настройку полей адресов перед обновлением пользователя, но вместо этого устанавливаю их соответствующие идентификаторы (после вставки адресов ) все работает как положено.

Тем не менее, я все еще, кажется, скучаю по üoint, потому что я думал, что Entity Framework должна быть в состоянии обработать все это внутренне.

Я знаю, что работа с Azure Appservice, Entity Framework, Code First, бэкендом ASP.NET MVC и зависимыми таблицами, доступ к которым осуществляется с помощью клиентского и серверного API мобильных приложений Azure, делает вещи по-другому, но я не был смог найти образцы, которые наглядно показывают, как это сделать правильно ...

Я также читал о том, что нужно настраивать отношения, используя свободный API или декорируя свойства. Однако, даже если ничего не делать, мне кажется, что фреймворк способен справиться с этим сам. Например, при добавлении свойства BillingAddressId к объекту пользовательских данных на стороне сервера и выполнении Add-Migration код миграции содержит ограничения внешнего ключа без необходимости их явной настройки:

public override void Up()
{
  AddColumn("dbo.Users", "BillingAddressId", c => c.String(maxLength: 128));
  AddColumn("dbo.Users", "DeliveryAddressId", c => c.String(maxLength: 128));
  CreateIndex("dbo.Users", "BillingAddressId");
  CreateIndex("dbo.Users", "DeliveryAddressId");
  AddForeignKey("dbo.Users", "BillingAddressId", "dbo.Addresses", "Id");
  AddForeignKey("dbo.Users", "DeliveryAddressId", "dbo.Addresses", "Id");
}

1 Ответ

0 голосов
/ 24 мая 2019

Вы Пользовательские сущности не знают об объектах Address, которые были добавлены в БД.То, что вы хотите сделать, это var entity = context.Users.Find(id); и обновить entity с тем, что в patch, используя либо entity.Prop = patch.Prop;, либо используя что-то вроде Automapper для обновления сущности, а затем вызвать ваш UpdateAsync.Это позволит контексту узнать о новых изменениях, внесенных в вашу сущность User, и заполнить внешние ключи.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...