CRUD API: คุณจะระบุฟิลด์ที่จะอัปเดตได้อย่างไร


9

สมมติว่าคุณมีโครงสร้างข้อมูลบางประเภทซึ่งยังคงอยู่ในฐานข้อมูลบางประเภท เพื่อความง่ายเราจะเรียกโครงสร้างข้อมูลPersonนี้ ขณะนี้คุณได้รับมอบหมายให้ออกแบบ CRUD API ซึ่งอนุญาตให้แอปพลิเคชันอื่นสร้างอ่านอัปเดตและลบPersons เพื่อความง่ายลองสมมติว่า API นี้เข้าถึงได้ผ่านบริการเว็บบางประเภท

สำหรับชิ้นส่วน C, R และ D ของ CRUD การออกแบบนั้นง่าย ฉันจะใช้เครื่องหมายคล้ายฟังก์ชัน C # - การใช้งานอาจเป็น SOAP, REST / JSON หรืออย่างอื่น:

class Person {
    string Name;
    DateTime? DateOfBirth;
    ...
}

Identifier CreatePerson(Person);
Person GetPerson(Identifier);
void DeletePerson(Identifier);

แล้วการอัพเดตล่ะ? สิ่งที่ต้องทำตามธรรมชาติคือ

void UpdatePerson(Identifier, Person);

แต่วิธีการที่คุณจะต้องระบุให้ซึ่งด้านของPersonการอัปเดต?


วิธีแก้ปัญหาที่ฉันสามารถทำได้:

  • คุณสามารถกำหนดให้บุคคลที่สมบูรณ์ต้องผ่านเช่นลูกค้าจะทำสิ่งนี้เพื่ออัปเดตวันเกิด:

    p = GetPerson(id);
    p.DateOfBirth = ...;
    UpdatePerson(id, p);
    

    อย่างไรก็ตามนั่นจะต้องมีความสอดคล้องของธุรกรรมหรือการล็อคระหว่างการรับและการปรับปรุง มิฉะนั้นคุณสามารถเขียนทับการเปลี่ยนแปลงอื่น ๆ ที่ทำในแบบคู่ขนานโดยไคลเอนต์อื่น ๆ สิ่งนี้จะทำให้ API มีความซับซ้อนมากขึ้น นอกจากนี้ยังมีข้อผิดพลาดเกิดขึ้นเนื่องจากรหัสหลอกต่อไปนี้ (สมมติว่าภาษาของลูกค้าที่มีการสนับสนุน JSON)

    UpdatePerson(id, { "DateOfBirth": "2015-01-01" });
    

    - ซึ่งมีลักษณะที่ถูกต้อง - ไม่เพียง แต่จะเปลี่ยน DateOfBirth แต่ยังรีเซ็ตสาขาอื่น ๆ ทั้งหมดเพื่อ null

  • nullคุณอาจจะไม่สนใจทุกสาขาที่มี อย่างไรก็ตามคุณจะสร้างความแตกต่างระหว่างการไม่เปลี่ยน DateOfBirthและจงใจเปลี่ยนเป็นโมฆะได้อย่างไร

  • void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)เปลี่ยนลายเซ็นไป

  • void UpdatePerson(Identifier, ListOfFieldValuePairs)เปลี่ยนลายเซ็นไป

  • ใช้คุณสมบัติบางอย่างของโปรโตคอลการส่ง: ตัวอย่างเช่นคุณสามารถละเว้นฟิลด์ทั้งหมดที่ไม่มีอยู่ในการแสดง JSON ของบุคคล อย่างไรก็ตามโดยทั่วไปแล้วจะต้องแยกวิเคราะห์ JSON ด้วยตนเองและไม่สามารถใช้คุณสมบัติที่มีอยู่ในตัวของไลบรารีของคุณ (เช่น WCF)

ไม่มีวิธีแก้ปัญหาใดที่ดูสง่างามสำหรับฉัน แน่นอนว่านี่เป็นปัญหาที่พบบ่อยดังนั้นทุกคนจึงใช้วิธีแก้ปัญหาที่ดีที่สุด


เหตุใดตัวระบุจึงไม่ได้เป็นส่วนหนึ่งของบุคคล สำหรับPersonอินสแตนซ์ที่สร้างขึ้นใหม่ซึ่งยังคงไม่ถูกยืนยันและในกรณีที่ตัวระบุถูกตัดสินใจว่าเป็นส่วนหนึ่งของกลไกการคงอยู่ให้ปล่อยให้เป็นโมฆะ สำหรับคำตอบ JPA ใช้หมายเลขเวอร์ชัน ถ้าคุณอ่านเวอร์ชัน 23 เมื่อคุณอัพเดตไอเท็มหากเวอร์ชันใน DB เป็น 24 การเขียนล้มเหลว
SJuan76

อนุญาตและสื่อสารทั้งPUTและPATCHวิธีการ เมื่อใช้PATCHควรส่งกุญแจเท่านั้นที่จะถูกแทนที่ด้วยPUTวัตถุทั้งหมดจะถูกแทนที่
แร่

คำตอบ:


8

หากคุณไม่มีการติดตามการเปลี่ยนแปลงตามข้อกำหนดของวัตถุนี้ (เช่น "ผู้ใช้ John เปลี่ยนชื่อและวันเดือนปีเกิด") สิ่งที่ง่ายที่สุดคือการแทนที่วัตถุทั้งหมดใน DB ด้วยสิ่งที่คุณได้รับจาก Consumer วิธีการนี้จะเกี่ยวข้องกับข้อมูลที่ส่งผ่านสายสัญญาณเพิ่มขึ้นเล็กน้อย แต่คุณหลีกเลี่ยงการอ่านก่อนการอัพเดท

หากคุณมีความต้องการติดตามกิจกรรม โลกของคุณมีความซับซ้อนมากขึ้นและคุณจะต้องออกแบบวิธีการจัดเก็บข้อมูลเกี่ยวกับการกระทำ CRUD และวิธีการดักจับพวกเขา นั่นคือโลกที่คุณไม่ต้องการดำน้ำหากคุณไม่มีความต้องการดังกล่าว

ตามค่าเอาชนะโดยการทำธุรกรรมที่แยกจากกันผมจะแนะนำการทำวิจัยทั่วโลกในแง่ดีและแง่ร้ายล็อค พวกเขาลดสถานการณ์ทั่วไปนี้:

  1. วัตถุถูกอ่านโดย user1
  2. วัตถุถูกอ่านโดย user2
  3. วัตถุที่เขียนโดย user1
  4. วัตถุที่เขียนโดย user2 และการเปลี่ยนแปลงที่ถูกเขียนทับโดย user1

ผู้ใช้แต่ละคนมีการทำธุรกรรมที่แตกต่างกันดังนั้น SQL มาตรฐานกับสิ่งนี้ พบมากที่สุดคือการล็อคในแง่ดี (ยังกล่าวถึงโดย @ SJuan76 ในความคิดเห็นเกี่ยวกับรุ่น) รุ่นของคุณบันทึกของคุณในฐานข้อมูลและในระหว่างการเขียนคุณก่อนดูในฐานข้อมูลถ้ารุ่นที่ตรงกัน หากเวอร์ชันไม่ตรงกันคุณรู้ว่ามีคนอัปเดตออบเจกต์ในระหว่างนั้นและคุณจำเป็นต้องตอบกลับด้วยข้อความแสดงข้อผิดพลาดแก่ผู้บริโภคเกี่ยวกับสถานการณ์นี้ ใช่คุณต้องแสดงสถานการณ์นี้แก่ผู้ใช้

ขอให้สังเกตว่าคุณจำเป็นต้องอ่านบันทึกจริงจากฐานข้อมูลก่อนที่จะเขียน (สำหรับการเปรียบเทียบรุ่นล็อคในแง่ดี) ดังนั้นการใช้ตรรกะเดลตา (เขียนเฉพาะค่าที่เปลี่ยนแปลง) อาจไม่จำเป็นต้องใช้แบบสอบถามการอ่านเพิ่มเติมก่อนที่จะเขียน

การใส่ลอจิกเดลต้านั้นขึ้นอยู่กับสัญญาที่ทำกับผู้บริโภค แต่สิ่งที่ง่ายที่สุดสำหรับผู้บริโภคคือการสร้างเพย์โหลดแบบเต็มแทนที่จะใช้เดลตา


2

เรามี PHP API ในที่ทำงาน สำหรับการปรับปรุงหากไม่มีการส่งฟิลด์ในวัตถุ JSON จะได้รับการตั้งค่าเป็น NULL จากนั้นมันจะส่งผ่านทุกอย่างไปยังกระบวนงานที่เก็บไว้ กระบวนงานที่เก็บไว้พยายามอัปเดตทุกฟิลด์ด้วย field = IFNULL (อินพุต, ฟิลด์) ดังนั้นหากมีเพียง 1 ฟิลด์ที่อยู่ในวัตถุ JSON จะมีการอัปเดตเฉพาะเขตข้อมูลนั้น ในการล้างฟิลด์ชุดที่เราต้องมี field = '' อย่างชัดเจนฐานข้อมูลจะอัพเดทฟิลด์ด้วยสตริงว่างหรือค่าเริ่มต้นของคอลัมน์นั้น


3
คุณจะตั้งค่าฟิลด์เป็นโมฆะโดยเจตนาอย่างไร
Robert Harvey

เขตข้อมูลทั้งหมดถูกตั้งค่าไว้ไม่เป็นค่าเริ่มต้นดังนั้นเขตข้อมูล CHAR จะได้รับ '' และเขตข้อมูลจำนวนเต็มทั้งหมดจะได้รับ 0
Jared Bernacchi

1

ระบุรายการเขตข้อมูลที่ปรับปรุงแล้วใน Query String

PUT /resource/:id?fields=name,address,dob Body { //resource body }

ใช้ผสานข้อมูลที่เก็บไว้กับโมเดลจากเนื้อความคำขอ:

private ResourceModel MergeResourceModel(ResourceModel original, ResourceModel updated, List<string> fields)
{
    var comparer = new FieldComparer();

    foreach (
            var item in
            typeof (ResourceModel).GetProperties()
                    .Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof (JsonIgnoreAttribute))))
    {
        if (fields.Contains(item.Name, comparer))
        {
            var property = typeof (ResourceModel).GetProperty(item.Name);
            property.SetValue(original, property.GetValue(updated));
        }
    }

    return original;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.