CRUD API: Güncellenecek alanları nasıl belirlersiniz?


9

Diyelim ki bir çeşit veritabanında var olan bir çeşit veri yapınız var. Basit olması için, bu veri yapısı diyelim Person. Artık diğer uygulamaların oluşturmasına, okumasına, güncellemesine ve silmesine izin veren bir CRUD API'si tasarlamakla görevlisiniz Person. Basit olması için, bu API'ya bir tür web hizmeti üzerinden erişildiğini varsayalım.

CRUD'nin C, R ve D parçaları için tasarım basittir. C # benzeri fonksiyonel gösterim kullanacağım - uygulama SOAP, REST / JSON veya başka bir şey olabilir:

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

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

Güncelleme ne olacak? Yapılacak doğal şey,

void UpdatePerson(Identifier, Person);

ancak hangi alanların Persongüncelleneceğini nasıl belirlersiniz ?


Gelebileceğim çözümler:

  • Her zaman tam bir Kişinin geçmesini isteyebilirsiniz , yani müşteri doğum tarihini güncellemek için böyle bir şey yapar:

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

    Bununla birlikte, Get ve Güncelleme arasında bir tür işlem tutarlılığı veya kilitlenmesi gerekir; aksi takdirde, başka bir müşteri tarafından paralel olarak yapılan diğer değişikliklerin üzerine yazabilirsiniz. Bu, API'yi çok daha karmaşık hale getirecektir. Buna ek olarak, aşağıdaki sahte koddan (JSON desteğiyle bir istemci dili varsayarak) hata eğilimli

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

    - doğru görünen - yalnızca DateOfBirth değerini değiştirmekle kalmaz, diğer tüm alanları da null değerine sıfırlar.

  • Tüm alanları yoksayabilirsiniz null. Ancak, bunu değiştirmemek DateOfBirth ve kasıtlı olarak null değerine değiştirmek arasında nasıl bir fark yaratırsınız ?

  • İmzayı olarak değiştirin void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate).

  • İmzayı olarak değiştirin void UpdatePerson(Identifier, ListOfFieldValuePairs).

  • İletim protokolünün bazı özelliklerini kullanın: Örneğin, Kişinin JSON temsilinde bulunmayan tüm alanları yoksayabilirsiniz. Ancak, bu genellikle JSON'u kendiniz ayrıştırmanızı ve kitaplığınızın yerleşik özelliklerini (örneğin WCF) kullanamamanızı gerektirir.

Hiçbir çözüm benim için gerçekten zarif görünmüyor. Elbette, bu yaygın bir sorundur, peki herkes tarafından kullanılan en iyi uygulama çözümü nedir?


Tanımlayıcı neden kişinin bir parçası değil? PersonHala devam etmeyen yeni oluşturulan örnekler için ve tanımlayıcı, kalıcılık mekanizmasının bir parçası olarak kararlaştırıldığında, bunu null değerine bırakın. Cevaba gelince, JPA bir sürüm numarası kullanıyor; sürüm 23'ü okursanız, DB'deki sürüm 24 ise öğeyi güncellediğinizde yazma işlemi başarısız olur.
SJuan76

İzin ve hem iletişim PUTve PATCHyöntemleri. Kullanırken PATCH, yalnızca gönderme anahtarları değiştirilmeli, PUTtüm nesne değiştirilmelidir.
Lode

Yanıtlar:


8

Değişiklikleri bu nesne üzerinde gereksinim olarak izlemiyorsanız (örn. "Kullanıcı John ad ve doğum tarihini değiştirdi"), en basit olanı DB'deki tüm nesneyi tüketiciden aldığınız biriyle geçersiz kılmaktır. Bu yaklaşım, kablo yoluyla gönderilecek biraz daha fazla veri içerir, ancak güncellemeden önce okumaktan kaçınırsınız.

Etkinlik izleme gereksiniminiz varsa. Dünyanız çok daha karmaşık ve CRUD eylemleri hakkında nasıl bilgi depolayacağınızı ve bunları nasıl ele alacağınızı tasarlamanız gerekecek. Böyle bir gereksiniminiz yoksa dalmak istemediğiniz dünya budur.

Ayrı işlemlerle değerleri geçersiz kılma gereği, iyimser ve kötümser kilitleme hakkında araştırma yapmayı öneririm . Bu ortak senaryoyu hafifletiyorlar:

  1. Nesne user1 tarafından okunur
  2. Nesne user2 tarafından okunur
  3. User1 tarafından yazılan nesne
  4. User2 tarafından yazılan nesne ve user1 tarafından üzerine yazılan değişiklikler

Her kullanıcının farklı bir işlemi vardır, bu nedenle bununla standart SQL'dir. En yaygın olanı iyimser kilitlemedır (sürümler hakkında yorumda @ SJuan76 tarafından da belirtilmiştir). Sürümünüz DB'deki kaydınız ve yazma sırasında sürümler eşleşirse önce DB'ye bir göz atın. Sürümler eşleşmezse, birisinin nesneyi bu arada güncellediğini bilirsiniz ve bu durum hakkında tüketiciye hata mesajıyla yanıt vermeniz gerekir. Evet, bu durumu kullanıcıya göstermelisiniz.

Yazmadan önce gerçek kaydı DB'den okumanız gerektiğine dikkat edin (iyimser kilitleme sürümü karşılaştırması için), bu nedenle delta mantığı (yalnızca değiştirilen değerleri yazma) uygulamak, yazmadan önce ek okuma sorgusu gerektirmeyebilir.

Delta mantığına koymak büyük ölçüde tüketici ile sözleşmeye bağlıdır, ancak tüketici için en kolay olanın delta yerine tam yükü inşa etmek olduğuna dikkat edin.


2

İş yerinde bir PHP API'miz var. JSON nesnesinde bir alan gönderilmezse güncellemeler için NULL olarak ayarlanır. Sonra her şeyi saklı prosedüre geçirir. Saklı yordam her alanı field = IFNULL (giriş, alan) ile güncellemeye çalışır. JSON nesnesinde sadece 1 alan varsa, bu alan güncellenir. Ayarlanmış bir alanı açıkça boşaltmak için field = '' değerine sahip olmalıyız, DB daha sonra alanı boş dize veya bu sütunun varsayılan değeri ile güncelleştirir.


3
Bir alanı kasıtlı olarak null olmayan bir alanı null olarak nasıl ayarlarsınız?
Robert Harvey

Tüm alanlar NULL DEĞİL olarak ayarlanmıştır, bu nedenle CHAR alanları varsayılan olarak '' ve tüm tamsayı alanları 0 alır.
Jared Bernacchi

1

Sorgu Dizesi'nde güncellenen alanlar listesini belirtin.

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

Birleştirme saklanan verileri istek gövdesinin modeliyle uygulayın:

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.