当对象的一部分是更新而另一部分是创建时,则跨表处理DTO [英] Handling DTOs across tables when one part of the object is an update while another is a create

查看:88
本文介绍了当对象的一部分是更新而另一部分是创建时,则跨表处理DTO的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下情况下,我在决定如何处理Entity Framework的DTO时遇到问题.

I am having an issue deciding how to handle DTOs for Entity Framework in the following scenario.

我有一个客户表,在另一个表中有客户的联系人.我的问题来自这样一个事实,即可以在最初没有任何联系的情况下创建客户.因此,更新(HTTP PUT)可以通过添加新联系人来更新现有客户.或者,它可以更新现有客户bt,从而更新现有联系人.在后一种情况下,我在DTO中的联系人对象将需要一个ID.在前一种情况下不会.

I have a customer table, and in a separate table I have the customer's contacts. My problem comes from the fact that a customer can initially be created without any contacts. So an update (HTTP PUT) may update an existing customer by adding a new contact. Or it may update an existing customer bt updating an existing contact. In the latter case, my contact object in the DTO will need an ID. In the former case it won't.

public class UpdateCustomer {
  int Id {get; set;}
  public string Name {get; set;}
  public ICollection<Contact> Contacts {get; set;}
}

联系人DTO可能如下所示:

A contact DTO may look like this:

public class Contact {
  <Will need contactID on update>
  public CustomerId {get; set; }
  public string ContactType {get; set;}
  public string PhoneNumber {get; set;}
}

当用户想要创建客户并使用帖子同时创建联系人时,这非常有用.没有联系对象需要ID.但是,假设他们通过一个联系人创建了一个客户.现在他们想更新该客户,更新现有的联系人,还要添加一个新的联系人.这仍然是对Put方法的调用,但是列表中的一个Contact需要一个ID,而另一个不需要.

This works great when a user wants to create a customer and create contacts at the same time using a post. No contact objects need IDs. But let's say they create a customer with one contact. Now they want to update that customer, update the existing contact, but also add a new contact. This will still be a call to the Put method, but one Contact in the list will need an ID, the other won't.

我看到了几种解决方法.最明显的是将客户创建/更新和联系人创建/更新分离为单独的请求.现在,我可以为每个对象分配一个单独的对象,每个对象带有一个ID,一个没有ID.这就是我的目标,而客户端应用程序只需要进行两个单独的API调用即可.

I see a couple ways to solve this. The most obvious is separate customer creation/updates and contact creation/updates into separate requests. Now I can have a separate objects for each, one with an ID, one without. This is what I am leaning towards, and the client side app would just have to make two separate API calls.

另一个选择是无论如何都在服务器的Contact对象中包含一个ID.如果客户端应用程序不提供联系人ID,则它将为null.如果看到它为null,则可以尝试添加;如果不为null,则可以尝试更新.

The other option is to include an ID in the Contact object on the server side regardless. If the client app doesn't supply a contact ID, it will be null. If I see it's null I can attempt an add, if it's not null I can attempt an update.

我喜欢第一个想法,因为它使想法分开,但会导致额外的API调用.我喜欢第二个,因为它允许一个API调用一次更新与客户关联的所有组件.缺点是我可能会假设最终用户在不希望获得ID的情况下真正想要更新时便希望添加.

I like the first idea because it keeps thinks separate, but results in extra API calls. I like the second because it allows one API call to update all components associated with a customer at once. The drawback is I may assume the end user wants an add when they really want an update if they leave off an ID.

希望这是有道理的.我只是在寻找建议或反馈.

Hopefully this makes sense. I'm just looking for suggestions or feedback.

更新:根据Roar的建议,这是我下面的工作解决方案.请注意,我在原始帖子中将该类称为客户",但实际上是我数据库中的客户".我只是发现在有关Web api的帖子中使用Client会造成混淆.

Update: Using Roar's suggestion this is my working solution below. Note that I referred to the class as customer in my original post but that's actually Client in my DB. I just found it to be confusing using Client in a post about a web api.

   public async Task UpdateClient(Dtos.Shared.Client client)
    {
        if (!ClientExists(client.ClientId))
        {
            throw new ClientNotFoundException();
        }

        Client clientToUpdate = await AdministrativeContext.ListClients()
            .Where(c => c.ClientUuid == client.ClientId)
            .FirstOrDefaultAsync();

        clientToUpdate.ClientName = client.ClientName;
        //filter existing db elements that are still in DTO as well
        clientToUpdate.Contacts = clientToUpdate.Contacts
            .Where(c => client.Contacts.Any(con => con.ContactId == c.ContactUuid))
            .ToList();

        client.Contacts.ToList().ForEach(async contact =>
        {
            if (contact.ContactId == null || contact.ContactId == Guid.Empty) //if guid missing, we want to add
            {
                clientToUpdate.Contacts.Add(new Entities.Contact()
                {
                    ClientId = await AdministrativeContext.GetClientKeyFromGuidAsync(client.ClientId),
                    ContactUuid = Guid.NewGuid(),
                    FirstName = contact.FirstName,
                    LastName = contact.LastName,
                    Address = contact.Address,
                    Email = contact.Email,
                    Phone = contact.Phone,
                    TypeId = (await AdministrativeContext.ContactTypes.Where(ct => ct.ContactTypeName == contact.ContactType).FirstOrDefaultAsync()).ContactTypeId
                });
            }
            else //guid defined means update
            {
                Contact existingContact = clientToUpdate.Contacts.Where(c => contact.ContactId == c.ContactUuid).FirstOrDefault();

                existingContact.ClientId = await AdministrativeContext.GetClientKeyFromGuidAsync(client.ClientId);
                existingContact.Address = contact.Address;
                existingContact.Email = contact.Email;
                existingContact.FirstName = contact.FirstName;
                existingContact.LastName = contact.LastName;
                existingContact.Phone = contact.Phone;
                existingContact.TypeId = (await AdministrativeContext.ContactTypes
                    .Where(ct => ct.ContactTypeName == contact.ContactType)
                    .FirstOrDefaultAsync()).ContactTypeId;
            }
        });

        await AdministrativeContext.SaveChangesAsync();
    }

推荐答案

我通常在单个请求中解决这种更新情况.

I solve the update-case usually like this in a single request.

  • 从数据库中获取客户,并填充联系人集合
  • 遍历客户数据库实例中的所有联系人.如果客户DTO中不存在该联系人,则从客户数据库实例的联系人集合中删除联系人
  • 遍历DTO的所有联系人.如果Contact#Id = 0(在这种情况下可能为null),则将新联系人添加到客户数据库实例中的联系人集合,否则更新联系人集合中的现有联系人实例
  • 使用DTO中的剩余数据更新客户数据库实例
  • 将客户以及联系人集合保存到数据库(需要级联)

我通常与NHibernate合作,但该原则也应与EF合作.

I'm usually working with NHibernate, but the principle should work with EF as well.

优点:

  • 简单,处理联系人的创建,更新和删除以及客户更新
  • 单个数据库调用以进行更新

缺点:

  • 每次更新都需要有效载荷中的所有联系人

这篇关于当对象的一部分是更新而另一部分是创建时,则跨表处理DTO的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆