Bagikan melalui


Pengambilan data dan operasi CUD dalam aplikasi n-tingkat (LINQ ke SQL)

Saat Anda menserialisasikan objek entitas seperti Pelanggan atau Pesanan ke klien melalui jaringan, entitas tersebut terlepas dari konteks datanya. Konteks data tidak lagi melacak perubahannya atau asosiasinya dengan objek lain. Hal ini bukan masalah selama klien hanya membaca data. Juga relatif mudah untuk memungkinkan klien menambahkan baris baru ke database. Namun, jika aplikasi Anda mengharuskan klien dapat memperbarui atau menghapus data, maka Anda harus melampirkan entitas ke konteks data baru sebelum memanggil DataContext.SubmitChanges. Selain itu, jika Anda menggunakan pemeriksaan konkurensi optimis dengan nilai asli, maka Anda juga memerlukan cara untuk menyediakan database baik entitas asli maupun entitas seperti yang dimodifikasi. Metode Attach disediakan untuk memungkinkan Anda memasukkan entitas ke dalam konteks data baru setelah dilepaskan.

Bahkan jika Anda menserialisasikan objek proksi sebagai pengganti entitas LINQ ke SQL, Anda masih harus membuat entitas pada lapisan akses data (DAL), dan melampirkannya ke yang baru System.Data.Linq.DataContext, untuk mengirimkan data ke database.

LINQ untuk SQL sama sekali tidak acuh tentang bagaimana entitas diserialisasikan. Untuk informasi selengkapnya tentang cara menggunakan alat Object Relational Designer dan SQLMetal untuk menghasilkan kelas yang dapat diserialisasikan dengan menggunakan Windows Communication Foundation (WCF), lihat Cara: Membuat Entitas Dapat Diserialisasikan.

Catatan

Hanya panggil metode Attach pada entitas baru atau deserialisasi. Satu-satunya cara agar entitas terlepas dari konteks data aslinya adalah agar diserialisasikan. Jika Anda mencoba melampirkan entitas yang tidak terdeteksi ke konteks data baru, dan entitas tersebut masih memiliki pemuat yang ditangguhkan dari konteks data sebelumnya, LINQ ke SQL akan memberikan pengecualian. Entitas dengan pemuat yang ditangguhkan dari dua konteks data yang berbeda dapat menyebabkan hasil yang tidak diinginkan saat Anda melakukan operasi sisipkan, perbarui, dan hapus pada entitas tersebut. Untuk informasi selengkapnya tentang pemuat yang ditangguhkan, lihat Ditangguhkan versus Pemuatan Segera.

Mengambil Data

Panggilan Metode Klien

Contoh berikut menunjukkan contoh panggilan metode ke DAL dari klien Formulir Windows. Dalam contoh ini, DAL diimplementasikan sebagai Pustaka Layanan Windows:

Private Function GetProdsByCat_Click(ByVal sender As Object, ByVal e _
    As EventArgs)

    ' Create the WCF client proxy.
    Dim proxy As New NorthwindServiceReference.Service1Client

    ' Call the method on the service.
    Dim products As NorthwindServiceReference.Product() = _
        proxy.GetProductsByCategory(1)

    ' If the database uses original values for concurrency checks,
    ' the client needs to store them and pass them back to the
    ' middle tier along with the new values when updating data.

    For Each v As NorthwindClient1.NorthwindServiceReference.Product _
        In products
        ' Persist to a List(Of Product) declared at class scope.
        ' Additional change-tracking logic is the responsibility
        ' of the presentation tier and/or middle tier.
        originalProducts.Add(v)
    Next

    ' (Not shown) Bind the products list to a control
    ' and/or perform whatever processing is necessary.
End Function
private void GetProdsByCat_Click(object sender, EventArgs e)
{
    // Create the WCF client proxy.
    NorthwindServiceReference.Service1Client proxy =
    new NorthwindClient.NorthwindServiceReference.Service1Client();

    // Call the method on the service.
    NorthwindServiceReference.Product[] products =
    proxy.GetProductsByCategory(1);

    // If the database uses original values for concurrency checks,
    // the client needs to store them and pass them back to the
    // middle tier along with the new values when updating data.
    foreach (var v in products)
    {
        // Persist to a list<Product> declared at class scope.
        // Additional change-tracking logic is the responsibility
        // of the presentation tier and/or middle tier.
        originalProducts.Add(v);
    }

    // (Not shown) Bind the products list to a control
    // and/or perform whatever processing is necessary.
    }

Implementasi Tingkat Menengah

Contoh berikut menunjukkan implementasi metode antarmuka pada tingkat tengah. Berikut ini adalah dua poin utama yang perlu diperhatikan:

  • DataContext dinyatakan pada cakupan metode.
  • Metode mengembalikan koleksi IEnumerable dari hasil aktual. Pembuat serialisasi akan menjalankan kueri untuk mengirim hasil kembali ke tingkat klien/presentasi. Untuk mengakses hasil kueri secara lokal di tingkat tengah, Anda bisa memaksa eksekusi dengan memanggil ToList atau ToArray pada variabel kueri. Anda kemudian dapat mengembalikan daftar atau array tersebut sebagai IEnumerable.
Public Function GetProductsByCategory(ByVal categoryID As Integer) _
    As IEnumerable(Of Product)

    Dim db As New NorthwindClasses1DataContext(connectionString)
    Dim productQuery = _
    From prod In db.Products _
    Where prod.CategoryID = categoryID _
    Select prod

    Return productQuery.AsEnumerable()

End Function
public IEnumerable<Product> GetProductsByCategory(int categoryID)
{
    NorthwindClasses1DataContext db =
    new NorthwindClasses1DataContext(connectionString);

    IEnumerable<Product> productQuery =
    from prod in db.Products
    where prod.CategoryID == categoryID
    select prod;

    return productQuery.AsEnumerable();
}

Instans konteks data harus memiliki masa pakai satu "unit kerja." Dalam lingkungan yang digabungkan secara longgar, satu unit pekerjaan biasanya kecil, mungkin satu transaksi optimis, termasuk satu panggilan ke SubmitChanges. Oleh karena itu, konteks data dibuat dan dibuang pada cakupan metode. Jika unit kerja menyertakan panggilan ke logika aturan bisnis, maka umumnya Anda ingin menyimpan instans DataContext untuk seluruh operasi tersebut. Bagaimanapun, instans DataContext tidak dimaksudkan untuk tetap hidup untuk jangka waktu yang lama di sejumlah transaksi arbitrer.

Metode ini akan mengembalikan objek Produk tetapi bukan koleksi objek Order_Detail yang terkait dengan setiap Produk. Gunakan objek DataLoadOptions untuk mengubah perilaku default ini. Untuk informasi selengkapnya, lihat Cara: Mengontrol Berapa Banyak Data Terkait yang Diambil.

Sisipkan Data

Untuk menyisipkan objek baru, tingkat presentasi hanya memanggil metode yang relevan pada antarmuka tingkat tengah, dan meneruskan objek baru untuk disisipkan. Dalam beberapa kasus, mungkin lebih efisien bagi klien untuk meneruskan hanya beberapa nilai dan memiliki tingkat menengah membangun objek lengkap.

Implementasi Tingkat Menengah

Pada tingkat tengah, DataContext baru dibuat, objek dilampirkan ke DataContext dengan menggunakan metode InsertOnSubmit, dan objek disisipkan ketika SubmitChanges dipanggil. Pengecualian, panggilan balik, dan kondisi kesalahan dapat ditangani sama seperti dalam skenario layanan Web lainnya.

' No call to Attach is necessary for inserts.
Public Sub InsertOrder(ByVal o As Order)

    Dim db As New NorthwindClasses1DataContext(connectionString)
    db.Orders.InsertOnSubmit(o)

    ' Exception handling not shown.
    db.SubmitChanges()

End Sub
// No call to Attach is necessary for inserts.
    public void InsertOrder(Order o)
    {
        NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString);
        db.Orders.InsertOnSubmit(o);

        // Exception handling not shown.
        db.SubmitChanges();
    }

Hapus data

Untuk menghapus objek yang ada dari database, tingkat presentasi memanggil metode yang relevan pada antarmuka tingkat tengah, dan meneruskan salinannya yang menyertakan nilai asli objek yang akan dihapus.

Operasi penghapusan melibatkan pemeriksaan konkurensi optimis, dan objek yang akan dihapus harus terlebih dahulu dilampirkan ke konteks data baru. Dalam contoh ini, parameter Boolean diatur ke false untuk menunjukkan bahwa objek tidak memiliki stempel waktu (RowVersion). Jika tabel database Anda menghasilkan stempel waktu untuk setiap rekaman, maka pemeriksaan konkurensi jauh lebih sederhana, terutama untuk klien. Cukup teruskan objek asli atau yang dimodifikasi dan atur parameter Boolean ke true. Bagaimanapun, pada tingkat tengah biasanya perlu untuk menangkap ChangeConflictException. Untuk informasi selengkapnya tentang cara menangani konflik konkurensi optimis, lihat Konkurensi Optimis: Gambaran Umum.

Saat menghapus entitas yang memiliki batasan kunci asing pada tabel terkait, Anda harus terlebih dahulu menghapus semua objek dalam koleksi EntitySet<TEntity>nya.

' Attach is necessary for deletes.
Public Sub DeleteOrder(ByVal order As Order)
    Dim db As New NorthwindClasses1DataContext(connectionString)

    db.Orders.Attach(order, False)
    ' This will throw an exception if the order has order details.
    db.Orders.DeleteOnSubmit(order)

    Try
        ' ConflictMode is an optional parameter.
        db.SubmitChanges(ConflictMode.ContinueOnConflict)

    Catch ex As ChangeConflictException
        ' Get conflict information, and take actions
        ' that are appropriate for your application.
        ' See MSDN Article "How to: Manage Change
        ' Conflicts (LINQ to SQL).

    End Try
End Sub
// Attach is necessary for deletes.
public void DeleteOrder(Order order)
{
    NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString);

    db.Orders.Attach(order, false);
    // This will throw an exception if the order has order details.
    db.Orders.DeleteOnSubmit(order);
    try
    {
        // ConflictMode is an optional parameter.
        db.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
       // Get conflict information, and take actions
       // that are appropriate for your application.
       // See MSDN Article How to: Manage Change Conflicts (LINQ to SQL).
    }
}

Memperbarui Data

LINQ untuk SQL mendukung pembaruan dalam skenario ini yang melibatkan konkurensi optimis:

  • Konkurensi optimis berdasarkan stempel waktu atau nomor RowVersion.
  • Konkurensi optimis berdasarkan nilai asli subset properti entitas.
  • Konkurensi optimis berdasarkan entitas asli dan yang dimodifikasi lengkap.

Anda juga dapat melakukan pembaruan atau penghapusan pada entitas bersama dengan hubungannya, misalnya Pelanggan dan koleksi objek Pesanan terkait. Saat Anda membuat modifikasi pada klien pada grafik objek entitas dan koleksi turunannya (EntitySet), dan pemeriksaan konkurensi optimis memerlukan nilai asli, klien harus memberikan nilai asli tersebut untuk setiap entitas dan objek EntitySet<TEntity>. Jika Anda ingin memungkinkan klien untuk membuat serangkaian pembaruan, penghapusan, dan penyisipan terkait dalam satu panggilan metode, Anda harus memberi klien cara untuk menunjukkan jenis operasi apa yang harus dilakukan pada setiap entitas. Pada tingkat tengah, Anda kemudian harus memanggil metode Attach yang sesuai dan kemudian InsertOnSubmit, DeleteAllOnSubmit, atau InsertOnSubmit (tanpa Attach, untuk penyisipan) untuk setiap entitas sebelum Anda memanggil SubmitChanges. Jangan mengambil data dari database sebagai cara untuk mendapatkan nilai asli sebelum Anda mencoba pembaruan.

Untuk informasi selengkapnya tentang konkurensi optimis, lihat Konkurensi Optimis: Gambaran Umum. Untuk informasi terperinci tentang mengatasi konflik perubahan konkurensi optimis, lihat Cara: Mengelola Konflik Perubahan.

Contoh berikut menunjukkan setiap skenario:

Konkurensi optimis dengan stempel waktu

' Assume that "customer" has been sent by client.
' Attach with "true" to say this is a modified entity
' and it can be checked for optimistic concurrency
' because it has a column that is marked with the
' "RowVersion" attribute.

db.Customers.Attach(customer, True)

Try
    ' Optional: Specify a ConflictMode value
    ' in call to SubmitChanges.
    db.SubmitChanges()
Catch ex As ChangeConflictException
    ' Handle conflict based on options provided.
    ' See MSDN article "How to: Manage Change
    ' Conflicts (LINQ to SQL)".
End Try
// Assume that "customer" has been sent by client.
// Attach with "true" to say this is a modified entity
// and it can be checked for optimistic concurrency because
//  it has a column that is marked with "RowVersion" attribute
db.Customers.Attach(customer, true)
try
{
    // Optional: Specify a ConflictMode value
    // in call to SubmitChanges.
    db.SubmitChanges();
}
catch(ChangeConflictException e)
{
    // Handle conflict based on options provided
    // See MSDN article How to: Manage Change Conflicts (LINQ to SQL).
}

Dengan Subset Nilai Asli

Dalam pendekatan ini, klien mengembalikan objek berseri lengkap, bersama dengan nilai yang akan dimodifikasi.

Public Sub UpdateProductInventory(ByVal p As Product, ByVal _
    unitsInStock As Short?, ByVal unitsOnOrder As Short?)

    Using db As New NorthwindClasses1DataContext(connectionString)
        ' p is the original unmodified product
        ' that was obtained from the database.
        ' The client kept a copy and returns it now.
        db.Products.Attach(p, False)

        ' Now that the original values are in the data context,
        ' apply the changes.
        p.UnitsInStock = unitsInStock
        p.UnitsOnOrder = unitsOnOrder

        Try
            ' Optional: Specify a ConflictMode value
            ' in call to SubmitChanges.
            db.SubmitChanges()

        Catch ex As Exception
            ' Handle conflict based on options provided.
            ' See MSDN article "How to: Manage Change Conflicts
            ' (LINQ to SQL)".
        End Try
    End Using
End Sub
public void UpdateProductInventory(Product p, short? unitsInStock, short? unitsOnOrder)
{
    using (NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString))
    {
        // p is the original unmodified product
        // that was obtained from the database.
        // The client kept a copy and returns it now.
        db.Products.Attach(p, false);

        // Now that the original values are in the data context, apply the changes.
        p.UnitsInStock = unitsInStock;
        p.UnitsOnOrder = unitsOnOrder;
        try
        {
             // Optional: Specify a ConflictMode value
             // in call to SubmitChanges.
             db.SubmitChanges();
        }
        catch (ChangeConflictException e)
        {
            // Handle conflict based on provided options.
            // See MSDN article How to: Manage Change Conflicts
            // (LINQ to SQL).
        }
    }
}

Dengan Entitas Lengkap

Public Sub UpdateProductInfo(ByVal newProd As Product, ByVal _
    originalProd As Product)

    Using db As New NorthwindClasses1DataContext(connectionString)
        db.Products.Attach(newProd, originalProd)

        Try
            ' Optional: Specify a ConflictMode value
            ' in call to SubmitChanges.
            db.SubmitChanges()

        Catch ex As Exception
            ' Handle potential change conflict in whatever way
            ' is appropriate for your application.
            ' For more information, see the MSDN article
            ' "How to: Manage Change Conflicts (LINQ to
            ' SQL)".
        End Try

    End Using
End Sub
public void UpdateProductInfo(Product newProd, Product originalProd)
{
     using (NorthwindClasses1DataContext db = new
        NorthwindClasses1DataContext(connectionString))
     {
         db.Products.Attach(newProd, originalProd);
         try
         {
               // Optional: Specify a ConflictMode value
               // in call to SubmitChanges.
               db.SubmitChanges();
         }
        catch (ChangeConflictException e)
        {
            // Handle potential change conflict in whatever way
            // is appropriate for your application.
            // For more information, see the MSDN article
            // How to: Manage Change Conflicts (LINQ to SQL)/
        }
    }
}

Untuk memperbarui koleksi, panggil AttachAll alih-alih Attach.

Anggota Entitas yang Diharapkan

Seperti yang dinyatakan sebelumnya, hanya anggota tertentu dari objek entitas yang harus diatur sebelum Anda memanggil metode Attach. Anggota entitas yang harus ditetapkan harus memenuhi kriteria berikut:

  • Jadilah bagian dari identitas entitas.
  • Diharapkan untuk dimodifikasi.
  • Jadilah stempel waktu atau atur atribut UpdateChecknya ke sesuatu selain Never.

Jika tabel menggunakan stempel waktu atau nomor versi untuk pemeriksaan konkurensi optimis, Anda harus mengatur anggota tersebut sebelum memanggil Attach. Anggota didedikasikan untuk pemeriksaan konkurensi optimis saat properti IsVersion diatur ke benar pada atribut Kolom tersebut. Setiap pembaruan yang diminta akan dikirimkan hanya jika nomor versi atau nilai stempel waktu sama pada database.

Anggota juga digunakan dalam pemeriksaan konkurensi optimis selama anggota tidak memilki UpdateCheck diatur ke Never. Nilai defaultnya adalah Always jika tidak ada nilai lain yang ditentukan.

Jika salah satu anggota yang diperlukan ini hilang, ChangeConflictException ditampilkan selama SubmitChanges ("Baris tidak ditemukan atau diubah").

Status

Setelah objek entitas dilampirkan ke instans DataContext, objek dianggap dalam status PossiblyModified. Ada tiga cara untuk memaksa objek terlampir untuk dipertimbangkan Modified.

  1. Lampirkan sebagai tidak dimodifikasi, lalu ubah bidang secara langsung.

  2. Lampirkan dengan kelebihan beban Attach yang mengambil instans objek saat ini dan asli. Ini memasok pelacak perubahan dengan nilai lama dan baru sehingga akan secara otomatis mengetahui bidang mana yang telah berubah.

  3. Lampirkan dengan kelebihan beban Attach yang mengambil parameter Boolean kedua (diatur ke true). Ini akan memberi tahu pelacak perubahan untuk mempertimbangkan objek yang dimodifikasi tanpa harus menyediakan nilai asli apa pun. Dalam pendekatan ini, objek harus memiliki bidang versi/tanda waktu.

Untuk informasi selengkapnya, lihat Status Objek dan Pelacakan Perubahan.

Jika objek entitas sudah terjadi di Cache ID dengan identitas yang sama dengan objek yang dilampirkan, DuplicateKeyException ditampilkan.

Saat Anda melampirkan serangkaian IEnumerable dari objek, DuplicateKeyException ditampilkan saat kunci yang sudah ada hadir. Objek yang tersisa tidak terpasang.

Lihat juga