Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过.NET Core Web API在实体框架Core中修补实体和相关实体_C#_Entity Framework Core_Npgsql_Http Patch - Fatal编程技术网

C# 通过.NET Core Web API在实体框架Core中修补实体和相关实体

C# 通过.NET Core Web API在实体框架Core中修补实体和相关实体,c#,entity-framework-core,npgsql,http-patch,C#,Entity Framework Core,Npgsql,Http Patch,我有一个用.NETCore编写的Web API,它使用EFCore管理postgresql数据库的插入和查询。该API对于现有实体的插入和查询非常有效,但我在解决如何进行部分“补丁”更新方面遇到了困难。客户机希望能够只传递他们希望更新的属性。因此,完整的客户JSON负载可能如下所示: { "customer": { "identification": { "membership_number&quo

我有一个用.NETCore编写的Web API,它使用EFCore管理postgresql数据库的插入和查询。该API对于现有实体的插入和查询非常有效,但我在解决如何进行部分“补丁”更新方面遇到了困难。客户机希望能够只传递他们希望更新的属性。因此,完整的客户JSON负载可能如下所示:

{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au",
            "loyalty_db_id": "4638092"
        },
        "name": {
            "title": "Ms",
            "first_name": "tx2bxtqoa",
            "surname": "oe6qoto"
        },
        "date_of_birth": "1980-12-24T00:00:00",
        "gender": "F",
        "customer_type": "3",
        "home_store_id": "777",
        "home_store_updated": "1980-12-24T00:00:00",
        "store_joined_id": "274",
        "store_joined_date": "1980-12-24T00:00:00",
        "status_reason": null,
        "status": "50",
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": true,
                "updating_store": null
            }
        ],
        "marketing_preferences": [],
        "address": {
            "address_line_1": "something stree",
            "address_line_2": "Snyder",
            "postcode": "3030"
        },
        "external_cards": [
            {
                "updating_store": null,
                "card_type": "PY",
                "card_design": null,
                "card_number": "2701138910268",
                "status": "ACTIVE",
                "physical_print": false
            }
        ]
    }
}
    public class CustomerPayload
    {
        public Identification Identification { get; set; }

        [JsonProperty("contact_information")]
        public ContactInfo[] ContactInformation { get; set; }
    }

    public class ContactInfo
    {
        public bool Validated { get; set; }
    }

    public class Identification
    {
        [JsonProperty("membership_number")]
        public string MembershipNumber { get; set; }

        public string SomePropertyNotInPayload { get; set; }
    }
    public class PartialCustomerPayloadWrapper
    {
        public JObject Customer { get; set; }
    }
但是客户端希望传入一个有效负载,如:

{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au"
        },
        "address": {
            "address_line_1": "something stree"
        },
    }
}
只更新
地址行1
属性。其余字段将保持原样。不幸的是,因为我将JSON转换为
CustomerPayload
对象,然后
CustomerPayload
对象转换为
Customer
对象(以及相关实体),如果未传递属性,则将其设置为
NULL

这意味着,当我在EF Core中使用
SetValues
复制属性时,未提供的属性将设置为NULL,然后在数据库中更新为NULL。除了要求客户机传递所有属性,只传递保持不变的属性的现有值之外,我不确定如何处理这个问题

因此,一旦传入的JSON转换为
CustomerPayload
(并验证属性),我就使用下面的代码将
CustomerPayload
转换为
Customer

public Customer Convert(CustomerPayload source)
{
    Customer customer = new Customer
            {
                McaId = source.RequestCustomer.Identification.MembershipNumber,
                BusinessPartnerId = source.RequestCustomer.Identification.BusinessPartnerId,
                Status = source.RequestCustomer.Status,
                StatusReason = source.RequestCustomer.StatusReason, 
                LoyaltyDbId = source.RequestCustomer.Identification.LoyaltyDbId,
                Gender = source.RequestCustomer.Gender,
                DateOfBirth = source.RequestCustomer.DateOfBirth,
                CustomerType = source.RequestCustomer.CustomerType,
                HomeStoreId = source.RequestCustomer.HomeStoreId,
                HomeStoreUpdated = source.RequestCustomer.HomeStoreUpdated,
                StoreJoined = source.RequestCustomer.StoreJoinedId,
                CreatedDate = Functions.GenerateDateTimeByLocale(),
                UpdatedBy = Functions.DbUser
            };

    if (source.RequestCustomer.Name != null)
    {
        customer.Title = source.RequestCustomer.Name.Title;
        customer.FirstName = source.RequestCustomer.Name.FirstName;
        customer.LastName = source.RequestCustomer.Name.Surname;
    }

    if (source.RequestCustomer.Address != null)
    {
        customer.Address.Add(new Address
                {
                    AddressType = source.RequestCustomer.Address.AddressType,
                    AddressLine1 = source.RequestCustomer.Address.AddressLine1,
                    AddressLine2 = source.RequestCustomer.Address.AddressLine2,
                    Suburb = source.RequestCustomer.Address.Suburb,
                    Postcode = source.RequestCustomer.Address.Postcode,
                    Region = source.RequestCustomer.Address.State, 
                    Country = source.RequestCustomer.Address.Country,
                    CreatedDate = Functions.GenerateDateTimeByLocale(),
                    UpdatedBy = Functions.DbUser,
                    UpdatingStore = source.RequestCustomer.Address.UpdatingStore,
                    AddressValidated = source.RequestCustomer.Address.AddressValidated,
                    AddressUndeliverable = source.RequestCustomer.Address.AddressUndeliverable
                });
    }

    if (source.RequestCustomer.MarketingPreferences != null)
    {
        customer.MarketingPreferences = source.RequestCustomer.MarketingPreferences
                    .Select(x => new MarketingPreferences()
                    {
                        ChannelId = x.Channel,
                        OptIn = x.OptIn,
                        ValidFromDate = x.ValidFromDate,
                        UpdatedBy = Functions.DbUser,
                        CreatedDate = Functions.GenerateDateTimeByLocale(),
                        UpdatingStore = x.UpdatingStore,
                        ContentTypePreferences = (from c in x.ContentTypePreferences
                            where x.ContentTypePreferences != null
                            select new ContentTypePreferences
                            {
                                TypeId = c.Type,
                                OptIn = c.OptIn,
                                ValidFromDate = c.ValidFromDate,
                                ChannelId = x.Channel //TODO: Check if this will just naturally be passed in JSON so can use c. instead of x.)
                            }).ToList(),
                    })
                    .ToList();
    }

    if (source.RequestCustomer.ContactInformation != null)
    {
        // Validate email if present
        var emails = (from e in source.RequestCustomer.ContactInformation
                      where e.ContactType.ToUpper() == ContactInformation.ContactTypes.Email && e.ContactValue != null
                    select e.ContactValue);

        foreach (var email in emails)
        {
            Console.WriteLine($"Validating email {email}");

            if (!IsValidEmail(email))
            {
                throw new Exception($"Email address {email} is not valid.");
            }
        }

        customer.ContactInformation = source.RequestCustomer.ContactInformation
                    .Select(x => new ContactInformation()
                    {
                        ContactType = x.ContactType,
                        ContactValue = x.ContactValue,
                        CreatedDate = Functions.GenerateDateTimeByLocale(),
                        UpdatedBy = Functions.DbUser,
                        Validated = x.Validated,
                        UpdatingStore = x.UpdatingStore

                    })
                    .ToList();
        }

        if (source.RequestCustomer.ExternalCards != null)
        {
            customer.ExternalCards = source.RequestCustomer.ExternalCards
                    .Select(x => new ExternalCards()
                    {
                        CardNumber = x.CardNumber,
                        CardStatus = x.Status.ToUpper(),
                        CardDesign = x.CardDesign,
                        CardType = x.CardType,
                        UpdatingStore = x.UpdatingStore,
                        UpdatedBy = Functions.DbUser
                    })
                    .ToList();
        }

        Console.WriteLine($"{customer.ToJson()}");
        return customer; 
   }
然后我使用下面的方法进行更新。我现在有一个最好的折衷办法,就是他们可以省略某些部分(比如地址,或者联系人信息中的任何内容等),并且不会更新任何内容,但是他们希望能够完全灵活地传递各个属性,我想提供这一点。我如何重新构造它,以便如果它们不传递客户或相关实体(地址等)的特定属性,它们在EF Core生成的SetValues或update语句中被忽略

public static CustomerPayload UpdateCustomerRecord(CustomerPayload customerPayload)
    {
        try
        {
            var updateCustomer = customerPayload.Convert(customerPayload);
            var customer = GetCustomerByCardNumber(updateCustomer.ExternalCards.First().CardNumber);

            Console.WriteLine($"Existing customer {customer.McaId} will be updated from incoming customer {updateCustomer.McaId}");

            using (var loyalty = new loyaltyContext())
            {
                loyalty.Attach(customer);
               
                // If any address is provided
                if (updateCustomer.Address.Any())
                {
                    Console.WriteLine($"Update customer has an address");
                    foreach (Address a in updateCustomer.Address)
                    {
                        Console.WriteLine($"Address of type {a.AddressType}");
                        if (customer.Address.Any(x => x.AddressType == a.AddressType))
                        {
                            Console.WriteLine($"Customer already has an address of this type, so it is updated.");
                            a.AddressInternalId = customer.Address.First(x => x.AddressType == a.AddressType).AddressInternalId;
                            a.CustomerInternalId = customer.Address.First(x => x.AddressType == a.AddressType).CustomerInternalId;
                            a.CreatedDate = customer.Address.First(x => x.AddressType == a.AddressType).CreatedDate;
                            a.UpdatedDate = Functions.GenerateDateTimeByLocale();
                            a.UpdatedBy = Functions.DbUser;
                            loyalty.Entry(customer.Address.First(x => x.AddressType == a.AddressType)).CurrentValues.SetValues(a);
                        }
                        else
                        {
                            Console.WriteLine($"Customer does not have an address of this type, so it is inserted.");
                            customer.AddAddressToCustomer(a);
                        }
                    }
                }
                // We want to update contact information 
                if (updateCustomer.ContactInformation.Any())
                {
                    Console.WriteLine($"Some contact information has been provided to update");
                    foreach (var c in updateCustomer.ContactInformation)
                    {
                        Console.WriteLine($"Assessing contact information {c.ContactValue} of type {c.ContactType}");
                        if (customer.ContactInformation.Any(ci => ci.ContactType == c.ContactType))
                        {
                            Console.WriteLine($"The customer already has a contact type of {c.ContactType}");
                            // we have an existing contact of this type so update
                            var existContact = (from cn in customer.ContactInformation
                                                where cn.ContactType == c.ContactType
                                                select cn).Single();

                            Console.WriteLine($"Existing contact id is {existContact.ContactInternalId} with value {existContact.ContactValue} from customer id {existContact.CustomerInternalId} which should match db customer {customer.CustomerInternalId}");
                            // Link the incoming contact to the existing contact by Id 
                            c.CustomerInternalId = existContact.CustomerInternalId;
                            c.ContactInternalId = existContact.ContactInternalId;

                            // Set the update date time to now
                            c.UpdatedDate = Functions.GenerateDateTimeByLocale();
                            c.UpdatedBy = Functions.DbUser;
                            c.CreatedDate = existContact.CreatedDate;
                            loyalty.Entry(existContact).CurrentValues.SetValues(c);
                        }
                        else
                        {
                            Console.WriteLine($"There is no existing type of {c.ContactType} so creating a new entry");
                            // we have no existing contact of this type so create
                            customer.AddContactInformationToCustomer(c);
                        }
                    }
                }

                updateCustomer.CustomerInternalId = customer.CustomerInternalId;
                updateCustomer.CreatedDate = customer.CreatedDate;
                updateCustomer.UpdatedDate = Functions.GenerateDateTimeByLocale();

                loyalty.Entry(customer).CurrentValues.SetValues(updateCustomer);
                loyalty.Entry(customer).State = EntityState.Modified;

                if (updateCustomer.BusinessPartnerId == null)
                {
                    Console.WriteLine($"BPID not specified or NULL. Do not update.");
                    loyalty.Entry(customer).Property(x => x.BusinessPartnerId).IsModified = false;
                }

                // CustomerPayload used to check name, as Customer has no outer references/element for name details. 
                if (customerPayload.RequestCustomer.Name == null)
                {
                    loyalty.Entry(customer).Property(x => x.FirstName).IsModified = false;
                    loyalty.Entry(customer).Property(x => x.LastName).IsModified = false;
                    loyalty.Entry(customer).Property(x => x.Title).IsModified = false;
                }

                loyalty.SaveChanges();
                customerPayload = customer.Convert(customer);

                // Return customer so we can access mcaid, bpid etc. 
                return customerPayload; 
            }
        }
        catch (ArgumentNullException e)
        {
            Console.WriteLine(e);
            throw new CustomerNotFoundException();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex}");
            throw ex; 
        }
    }
映射标识部分的示例:

public class Identification
{
    [DisplayName("business_partner_id")]
    [Description("A business_partner_id is required")]
    [StringLength(10)]
    [DataType(DataType.Text)]
    [JsonProperty("business_partner_id", Required = Required.Default)]
    public string BusinessPartnerId { get; set; } 

    [DisplayName("membership_number")]
    [Description("A membership_number is required")]
    [StringLength(50)]
    [DataType(DataType.Text)]
    [JsonProperty("membership_number", Required = Required.Default)]
    public string MembershipNumber { get; set; }

    [DisplayName("loyalty_db_id")]
    [Description("A loyalty_db_id is required")]
    [StringLength(50)]
    [DataType(DataType.Text)]
    [JsonProperty("loyalty_db_id", Required = Required.Default)]
    public string LoyaltyDbId { get; set; }
}

好的,所以我确信我遗漏了一些东西,因为这是绝对赤裸裸的,但基本的想法如下

如果您的DTO类看起来像这样:

{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au",
            "loyalty_db_id": "4638092"
        },
        "name": {
            "title": "Ms",
            "first_name": "tx2bxtqoa",
            "surname": "oe6qoto"
        },
        "date_of_birth": "1980-12-24T00:00:00",
        "gender": "F",
        "customer_type": "3",
        "home_store_id": "777",
        "home_store_updated": "1980-12-24T00:00:00",
        "store_joined_id": "274",
        "store_joined_date": "1980-12-24T00:00:00",
        "status_reason": null,
        "status": "50",
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": true,
                "updating_store": null
            }
        ],
        "marketing_preferences": [],
        "address": {
            "address_line_1": "something stree",
            "address_line_2": "Snyder",
            "postcode": "3030"
        },
        "external_cards": [
            {
                "updating_store": null,
                "card_type": "PY",
                "card_design": null,
                "card_number": "2701138910268",
                "status": "ACTIVE",
                "physical_print": false
            }
        ]
    }
}
    public class CustomerPayload
    {
        public Identification Identification { get; set; }

        [JsonProperty("contact_information")]
        public ContactInfo[] ContactInformation { get; set; }
    }

    public class ContactInfo
    {
        public bool Validated { get; set; }
    }

    public class Identification
    {
        [JsonProperty("membership_number")]
        public string MembershipNumber { get; set; }

        public string SomePropertyNotInPayload { get; set; }
    }
    public class PartialCustomerPayloadWrapper
    {
        public JObject Customer { get; set; }
    }
我们需要声明一根拐杖(因为某些原因,您的样本具有顶级“客户”属性,如下所示:

{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au",
            "loyalty_db_id": "4638092"
        },
        "name": {
            "title": "Ms",
            "first_name": "tx2bxtqoa",
            "surname": "oe6qoto"
        },
        "date_of_birth": "1980-12-24T00:00:00",
        "gender": "F",
        "customer_type": "3",
        "home_store_id": "777",
        "home_store_updated": "1980-12-24T00:00:00",
        "store_joined_id": "274",
        "store_joined_date": "1980-12-24T00:00:00",
        "status_reason": null,
        "status": "50",
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": true,
                "updating_store": null
            }
        ],
        "marketing_preferences": [],
        "address": {
            "address_line_1": "something stree",
            "address_line_2": "Snyder",
            "postcode": "3030"
        },
        "external_cards": [
            {
                "updating_store": null,
                "card_type": "PY",
                "card_design": null,
                "card_number": "2701138910268",
                "status": "ACTIVE",
                "physical_print": false
            }
        ]
    }
}
    public class CustomerPayload
    {
        public Identification Identification { get; set; }

        [JsonProperty("contact_information")]
        public ContactInfo[] ContactInformation { get; set; }
    }

    public class ContactInfo
    {
        public bool Validated { get; set; }
    }

    public class Identification
    {
        [JsonProperty("membership_number")]
        public string MembershipNumber { get; set; }

        public string SomePropertyNotInPayload { get; set; }
    }
    public class PartialCustomerPayloadWrapper
    {
        public JObject Customer { get; set; }
    }
然后我们可以用一种方法来完成所有的巫术:

    private void SetThings(object target, JObject jObj)
    {
        var properties = target.GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Select(x =>
        {
            var attr = x
            .GetCustomAttributes(typeof(JsonPropertyAttribute), false)
            .FirstOrDefault();

            string jPropName = null;
            if (attr != null)
            {
                jPropName = ((JsonPropertyAttribute)attr).PropertyName;
            }

            return (Property: x, Name: x.Name, JsonName: jPropName);
        });

        foreach (var val in jObj)
        {
            var key = val.Key.ToLowerInvariant();
            var property = properties
                .FirstOrDefault(x => x.Name.ToLowerInvariant() == key ||
                x.JsonName?.ToLowerInvariant() == key);

            if (property == default)
            {
                continue;
            }

            if (val.Value.Type == JTokenType.Object)
            {
                var newTarget = property.Property.GetValue(target);
                if (newTarget == null)
                {
                    newTarget = Activator.CreateInstance(property.Property.PropertyType);
                    property.Property.SetValue(target, newTarget);
                }

                SetThings(property.Property.GetValue(target), (JObject)val.Value);
            }
            else
            {
                property.Property.SetValue(target, val.Value.ToObject(property.Property.PropertyType));
            }
        }
    }
最后,我们的API行动:

    [HttpPost]
    public string Post([FromBody] PartialCustomerPayloadWrapper wrapper)
    {
    // So  here i expect you to get data from DB 
    // and then pass through the method that converts the db object to `CustomerPayload`
    // Since i do not have that, this is just a POCO with some properties initialized.
        var dbCustomer = new CustomerPayload { Identification = new Identification { SomePropertyNotInPayload = "banana" } };

        var customer = wrapper.Customer;

        SetThings(dbCustomer, customer);
     // at this point our SomePropertyNotInPayload is still banana, but contact info and MembershipNumber are set
        return "OK";
    }
我使用此有效载荷进行测试:


{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au"
        },
        "address": {
            "address_line_1": "something stree"
        },
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": false,
                "updating_store": null
            }
        ]
    }
}

注意:这种方法最大的缺点是你不能真正地将“联系人信息”结合起来,因为你需要某种主键(我假设它已经在为你的客户准备的过程中)。如果你有,你可以通过检查
JTokenType.Array
来扩展巫毒部分,然后通过类似的设置来处理单个项目。

好的,所以我肯定我遗漏了一些东西,因为这完全是赤裸裸的,但基本思想如下

如果您的DTO类看起来像这样:

{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au",
            "loyalty_db_id": "4638092"
        },
        "name": {
            "title": "Ms",
            "first_name": "tx2bxtqoa",
            "surname": "oe6qoto"
        },
        "date_of_birth": "1980-12-24T00:00:00",
        "gender": "F",
        "customer_type": "3",
        "home_store_id": "777",
        "home_store_updated": "1980-12-24T00:00:00",
        "store_joined_id": "274",
        "store_joined_date": "1980-12-24T00:00:00",
        "status_reason": null,
        "status": "50",
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": true,
                "updating_store": null
            }
        ],
        "marketing_preferences": [],
        "address": {
            "address_line_1": "something stree",
            "address_line_2": "Snyder",
            "postcode": "3030"
        },
        "external_cards": [
            {
                "updating_store": null,
                "card_type": "PY",
                "card_design": null,
                "card_number": "2701138910268",
                "status": "ACTIVE",
                "physical_print": false
            }
        ]
    }
}
    public class CustomerPayload
    {
        public Identification Identification { get; set; }

        [JsonProperty("contact_information")]
        public ContactInfo[] ContactInformation { get; set; }
    }

    public class ContactInfo
    {
        public bool Validated { get; set; }
    }

    public class Identification
    {
        [JsonProperty("membership_number")]
        public string MembershipNumber { get; set; }

        public string SomePropertyNotInPayload { get; set; }
    }
    public class PartialCustomerPayloadWrapper
    {
        public JObject Customer { get; set; }
    }
我们需要声明一根拐杖(因为某些原因,您的样本具有顶级“客户”属性,如下所示:

{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au",
            "loyalty_db_id": "4638092"
        },
        "name": {
            "title": "Ms",
            "first_name": "tx2bxtqoa",
            "surname": "oe6qoto"
        },
        "date_of_birth": "1980-12-24T00:00:00",
        "gender": "F",
        "customer_type": "3",
        "home_store_id": "777",
        "home_store_updated": "1980-12-24T00:00:00",
        "store_joined_id": "274",
        "store_joined_date": "1980-12-24T00:00:00",
        "status_reason": null,
        "status": "50",
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": true,
                "updating_store": null
            }
        ],
        "marketing_preferences": [],
        "address": {
            "address_line_1": "something stree",
            "address_line_2": "Snyder",
            "postcode": "3030"
        },
        "external_cards": [
            {
                "updating_store": null,
                "card_type": "PY",
                "card_design": null,
                "card_number": "2701138910268",
                "status": "ACTIVE",
                "physical_print": false
            }
        ]
    }
}
    public class CustomerPayload
    {
        public Identification Identification { get; set; }

        [JsonProperty("contact_information")]
        public ContactInfo[] ContactInformation { get; set; }
    }

    public class ContactInfo
    {
        public bool Validated { get; set; }
    }

    public class Identification
    {
        [JsonProperty("membership_number")]
        public string MembershipNumber { get; set; }

        public string SomePropertyNotInPayload { get; set; }
    }
    public class PartialCustomerPayloadWrapper
    {
        public JObject Customer { get; set; }
    }
然后我们可以用一种方法来完成所有的巫术:

    private void SetThings(object target, JObject jObj)
    {
        var properties = target.GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Select(x =>
        {
            var attr = x
            .GetCustomAttributes(typeof(JsonPropertyAttribute), false)
            .FirstOrDefault();

            string jPropName = null;
            if (attr != null)
            {
                jPropName = ((JsonPropertyAttribute)attr).PropertyName;
            }

            return (Property: x, Name: x.Name, JsonName: jPropName);
        });

        foreach (var val in jObj)
        {
            var key = val.Key.ToLowerInvariant();
            var property = properties
                .FirstOrDefault(x => x.Name.ToLowerInvariant() == key ||
                x.JsonName?.ToLowerInvariant() == key);

            if (property == default)
            {
                continue;
            }

            if (val.Value.Type == JTokenType.Object)
            {
                var newTarget = property.Property.GetValue(target);
                if (newTarget == null)
                {
                    newTarget = Activator.CreateInstance(property.Property.PropertyType);
                    property.Property.SetValue(target, newTarget);
                }

                SetThings(property.Property.GetValue(target), (JObject)val.Value);
            }
            else
            {
                property.Property.SetValue(target, val.Value.ToObject(property.Property.PropertyType));
            }
        }
    }
最后,我们的API行动:

    [HttpPost]
    public string Post([FromBody] PartialCustomerPayloadWrapper wrapper)
    {
    // So  here i expect you to get data from DB 
    // and then pass through the method that converts the db object to `CustomerPayload`
    // Since i do not have that, this is just a POCO with some properties initialized.
        var dbCustomer = new CustomerPayload { Identification = new Identification { SomePropertyNotInPayload = "banana" } };

        var customer = wrapper.Customer;

        SetThings(dbCustomer, customer);
     // at this point our SomePropertyNotInPayload is still banana, but contact info and MembershipNumber are set
        return "OK";
    }
我使用此有效载荷进行测试:


{
    "customer": {
        "identification": {
            "membership_number": "2701138910268@priceline.com.au"
        },
        "address": {
            "address_line_1": "something stree"
        },
        "contact_information": [
            {
                "contact_type": "EMAIL",
                "contact_value": "2yupelxqui@hotmails.com",
                "validated": true,
                "updating_store": null
            },
            {
                "contact_type": "MOBILE",
                "contact_value": "xxxxxxxxx",
                "validated": false,
                "updating_store": null
            }
        ]
    }
}

注意:这种方法最大的缺点是你不能真正地将“联系人信息”结合起来,因为你需要某种主键(我假设它已经在为你的客户准备的过程中)。如果你有,你可以通过检查
JTokenType.Array
来扩展巫毒部分,然后通过类似的设置来处理单个项目。

我太懒了,无法全部输入,但我要做的是将它们的内容反序列化到
字典中,然后使用反射来提取模型属性,并仅在ictionary键包含它。如果与您一起工作的人对此表示怀疑,那么第二个最好的方法是使用传入模型和db模型,并对其进行反思,但缺点是->如果他们想实际将值设置为null,该怎么办?在字典中,您可以使用
.ContainsKey()
但对于您的型号-不是真的。顺便说一句,它在您的有效负载中看起来像一个真实的移动电话号码,可能需要删除它。好的,我将尝试提供一个,可能不是针对您的完整型号。要做到这一点,您能告诉我您是在使用Json.net序列化程序还是新的
System.Text.Json
序列化程序吗?您能展示一下您是如何映射的吗<代码>标识。成员身份号到
成员身份号
。是自动映射器还是
[JsonProperty]
?因为没有这个片段样本就无法工作感谢zaitsman,我已经添加了一些字段的样本以及它们是如何映射的。我本来会添加它的,但上次我问了一些关于这个的问题,并粘贴了一个属性和属性的列表,所以有人对我粘贴了太多的信息进行了抨击。我太懒了键入所有内容,但我要做的是将它们的内容反序列化到一个
字典中,然后使用反射来提取模型属性,并仅在字典键包含它时进行设置。如果你正在与ARK合作的任何人都这样做,那么第二个最好的方法就是使用传入模型和db模型,并对此进行反射,但缺点是->如果他们真的想把一个值设为null呢?如果是字典,你可以使用
.ContainsKey()
但对于您的型号-不是真的。顺便说一句,它在您的有效负载中看起来像一个真实的移动电话号码,可能需要删除它。好的,我将尝试提供一个,可能不是针对您的完整型号。要做到这一点,您能告诉我您是在使用Json.net序列化程序还是新的
System.Text.Json
序列化程序吗?您能展示一下您是如何映射的吗<代码>标识。成员身份号到
成员身份号
。那是automapper?还是
[JsonProperty]
?因为没有这个片段样本就不能工作感谢zaitsman,我已经添加了一些字段的样本以及它们的映射方式。我本来会添加它的,但上次我添加了它