ASP.NET MVC Uygulamasında Entity Framework ile Temel CRUD İşlevselliği Uygulama (2/10)

tarafından Tom Dykstra

Contoso University örnek web uygulaması, Entity Framework 5 Code First ve Visual Studio 2012 kullanarak ASP.NET MVC 4 uygulamalarının nasıl oluşturulacağını gösterir. Öğretici serisi hakkında bilgi için serideki ilk öğreticiye bakın.

Not

Çözemediğiniz bir sorunla karşılaşırsanız tamamlanmış bölümü indirin ve sorununuzu yeniden oluşturmayı deneyin. Genellikle kodunuzu tamamlanmış kodla karşılaştırarak sorunun çözümünü bulabilirsiniz. Bazı yaygın hatalar ve bunların nasıl çözüldüğü için bkz . Hatalar ve Geçici Çözümler.

Önceki öğreticide Entity Framework ve SQL Server LocalDB kullanarak verileri depolayan ve görüntüleyen bir MVC uygulaması oluşturdunuz. Bu öğreticide, MVC iskelesinin denetleyicilerde ve görünümlerde sizin için otomatik olarak oluşturduğu CRUD (oluşturma, okuma, güncelleştirme, silme) kodunu gözden geçirecek ve özelleştireceksiniz.

Not

Denetleyicinizle veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için depo desenini uygulamak yaygın bir uygulamadır. Bu öğreticileri basit tutmak için, bu serinin sonraki bir öğreticisine kadar bir depo uygulamayacaksınız.

Bu öğreticide aşağıdaki web sayfalarını oluşturacaksınız:

Contoso Üniversitesi Öğrenci Ayrıntıları sayfasını gösteren ekran görüntüsü.

Contoso Üniversitesi Öğrenci Düzenleme sayfasını gösteren ekran görüntüsü.

Contoso University Öğrenci Oluştur sayfasını gösteren ekran görüntüsü.

Öğrenci Silme sayfasını gösteren ekran görüntüsü.

Ayrıntılar Sayfası Oluşturma

Öğrenciler Index sayfasının yapı iskelesi oluşturulmuş kodu özelliğin dışında Enrollments bırakıldı çünkü bu özellik bir koleksiyon barındırıyor. Details Sayfada, koleksiyonun içeriğini bir HTML tablosunda görüntülersiniz.

Controllers\StudentController.cs dosyasında, görünümün Details eylem yöntemi tek Student bir varlığı almak için yöntemini kullanırFind.

public ActionResult Details(int id = 0)
{
    Student student = db.Students.Find(id);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

Anahtar değeri yöntemine parametresi olarak id geçirilir ve Dizin sayfasındaki Ayrıntılar köprüsündeki yol verilerinden gelir.

  1. Views\Student\Details.cshtml dosyasını açın. Her alan, aşağıdaki örnekte gösterildiği gibi bir DisplayFor yardımcı kullanılarak görüntülenir:

    <div class="display-label">
             @Html.DisplayNameFor(model => model.LastName)
        </div>
        <div class="display-field">
            @Html.DisplayFor(model => model.LastName)
        </div>
    
  2. Alandan EnrollmentDate sonra ve kapanış fieldset etiketinden hemen önce, aşağıdaki örnekte gösterildiği gibi kayıt listesini görüntülemek için kod ekleyin:

    <div class="display-label">
            @Html.LabelFor(model => model.Enrollments)
        </div>
        <div class="display-field">
            <table>
                <tr>
                    <th>Course Title</th>
                    <th>Grade</th>
                </tr>
                @foreach (var item in Model.Enrollments)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Course.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Grade)
                        </td>
                    </tr>
                }
            </table>
        </div>
    </fieldset>
    <p>
        @Html.ActionLink("Edit", "Edit", new { id=Model.StudentID }) |
        @Html.ActionLink("Back to List", "Index")
    </p>
    

    Bu kod, gezinti özelliğindeki varlıklar arasında Enrollments döngü oluşturur. Özelliğindeki her Enrollment varlık için kurs başlığını ve notunu görüntüler. Kurs başlığı, varlığın Course gezinti özelliğinde Course depolanan varlıktan Enrollments alınır. Bu verilerin tümü gerektiğinde veritabanından otomatik olarak alınır. (Başka bir deyişle, burada yavaş yükleme kullanıyorsunuz. Gezinti özelliği için Courseshevesle yüklemeyi belirtmediniz, bu nedenle bu özelliğe ilk kez erişmeye çalıştığınızda, verileri almak için veritabanına bir sorgu gönderilir. Bu serinin devamında yer alan İlgili Verileri Okuma öğreticisinde gecikmeli yükleme ve hızlı yükleme hakkında daha fazla bilgi edinebilirsiniz.)

  3. Öğrenciler sekmesini seçip Alexander Carson'ın Ayrıntılar bağlantısına tıklayarak sayfayı çalıştırın. Seçilen öğrencinin ders ve not listesini görürsünüz:

    Student_Details_page

Sayfa Oluştur'u Güncelleştirme

  1. Controllers\StudentController.cs dosyasında, yapı iskelesi yöntemine bir try-catch blok ve Bind özniteliği eklemek için eylem yöntemini aşağıdaki kodla değiştirinHttpPost``Create:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(
       [Bind(Include = "LastName, FirstMidName, EnrollmentDate")]
       Student student)
    {
       try
       {
          if (ModelState.IsValid)
          {
             db.Students.Add(student);
             db.SaveChanges();
             return RedirectToAction("Index");
          }
       }
       catch (DataException /* dex */)
       {
          //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
          ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
       }
       return View(student);
    }
    

    Bu kod, ASP.NET MVC model bağlayıcısı tarafından oluşturulan varlığı varlık kümesine Students ekler Student ve sonra değişiklikleri veritabanına kaydeder. (Model bağlayıcısı , form tarafından gönderilen verilerle çalışmanızı kolaylaştıran ASP.NET MVC işlevselliğini ifade eder; model bağlayıcısı, gönderilen form değerlerini CLR türlerine dönüştürür ve bunları parametrelerdeki eylem yöntemine geçirir. Bu durumda model bağlayıcısı, koleksiyondaki özellik değerlerini kullanarak sizin için bir Student varlığın örneğini Form oluşturur.)

    özniteliği siteler ValidateAntiForgeryTokenarası istek sahteciliği saldırılarını önlemeye yardımcı olur.

> [!WARNING]
    > Security - The `Bind` attribute is added to protect against *over-posting*. For example, suppose the `Student` entity includes a `Secret` property that you don't want this web page to update.
    > 
    > [!code-csharp[Main](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/samples/sample5.cs?highlight=7)]
    > 
    > Even if you don't have a `Secret` field on the web page, a hacker could use a tool such as [fiddler](http://fiddler2.com/home), or write some JavaScript, to post a `Secret` form value. Without the [Bind](https://msdn.microsoft.com/library/system.web.mvc.bindattribute(v=vs.108).aspx) attribute limiting the fields that the model binder uses when it creates a `Student` instance*,* the model binder would pick up that `Secret` form value and use it to update the `Student` entity instance. Then whatever value the hacker specified for the `Secret` form field would be updated in your database. The following image shows the fiddler tool adding the `Secret` field (with the value "OverPost") to the posted form values.
    > 
    > ![](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/_static/image6.png)  
    > 
    > The value "OverPost" would then be successfully added to the `Secret` property of the inserted row, although you never intended that the web page be able to update that property.
    > 
    > It's a security best practice to use the `Include` parameter with the `Bind` attribute to *allowed attributes* fields. It's also possible to use the `Exclude` parameter to *blocked attributes* fields you want to exclude. The reason `Include` is more secure is that when you add a new property to the entity, the new field is not automatically protected by an `Exclude` list.
    > 
    > Another alternative approach, and one preferred by many, is to use only view models with model binding. The view model contains only the properties you want to bind. Once the MVC model binder has finished, you copy the view model properties to the entity instance.

    Other than the `Bind` attribute, the `try-catch` block is the only change you've made to the scaffolded code. If an exception that derives from [DataException](https://msdn.microsoft.com/library/system.data.dataexception.aspx) is caught while the changes are being saved, a generic error message is displayed. [DataException](https://msdn.microsoft.com/library/system.data.dataexception.aspx) exceptions are sometimes caused by something external to the application rather than a programming error, so the user is advised to try again. Although not implemented in this sample, a production quality application would log the exception (and non-null inner exceptions ) with a logging mechanism such as [ELMAH](https://code.google.com/p/elmah/).

    The code in *Views\Student\Create.cshtml* is similar to what you saw in *Details.cshtml*, except that `EditorFor` and `ValidationMessageFor` helpers are used for each field instead of `DisplayFor`. The following example shows the relevant code:

    [!code-cshtml[Main](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/samples/sample6.cshtml)]

    *Create.cshtml* also includes `@Html.AntiForgeryToken()`, which works with the `ValidateAntiForgeryToken` attribute in the controller to help prevent [cross-site request forgery](../../security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages.md) attacks.

    No changes are required in *Create.cshtml*.
  1. Öğrenciler sekmesini seçip Yeni Oluştur'a tıklayarak sayfayı çalıştırın.

    Student_Create_page

    Bazı veri doğrulama işlemleri varsayılan olarak çalışır. Adları ve geçersiz bir tarihi girin ve hata iletisini görmek için Oluştur'a tıklayın.

    Students_Create_page_error_message

    Aşağıdaki vurgulanan kod, model doğrulama denetimini gösterir.

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(Student student)
    {
        if (ModelState.IsValid)
        {
            db.Students.Add(student);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    
        return View(student);
    }
    

    Tarihi 1/9/2005 gibi geçerli bir değerle değiştirin ve Yeni öğrencinin Dizin sayfasında görünmesini sağlamak için Oluştur'a tıklayın.

    Students_Index_page_with_new_student

POST Sayfasını Düzenle'yi Güncelleştirme

Controllers\StudentController.csHttpGetEdit dosyasında yöntemi (özniteliği olmayanHttpPost) yönteminde gördüğünüz Details gibi seçili Student varlığı almak için yöntemini kullanırFind. Bu yöntemi değiştirmeniz gerekmez.

Ancak, bir try-catch blok ve Bind özniteliği eklemek için eylem yöntemini aşağıdaki kodla değiştirinHttpPostEdit:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
   [Bind(Include = "StudentID, LastName, FirstMidName, EnrollmentDate")]
   Student student)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Entry(student).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DataException /* dex */)
   {
      //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
      ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
   }
   return View(student);
}

Bu kod, yönteminde HttpPostCreate gördüklerinize benzer. Ancak, model bağlayıcısı tarafından oluşturulan varlığı varlık kümesine eklemek yerine, bu kod varlıkta değiştirildiğini belirten bir bayrak ayarlar. SaveChanges yöntemi çağrıldığında, Modified bayrağı Entity Framework'ün veritabanı satırını güncelleştirmek için SQL deyimleri oluşturmasına neden olur. Kullanıcının değiştirmemiş olduğu sütunlar da dahil olmak üzere veritabanı satırının tüm sütunları güncelleştirilir ve eşzamanlılık çakışmaları yoksayılır. (Bu serinin sonraki öğreticilerinde eşzamanlılığı işlemeyi öğreneceksiniz.)

Varlık Durumları ve Ekleme ve SaveChanges Yöntemleri

Veritabanı bağlamı, bellekteki varlıkların veritabanındaki karşılık gelen satırlarıyla eşitlenip eşitlenmediğini izler ve bu bilgiler yöntemini çağırdığınızda SaveChanges ne olacağını belirler. Örneğin, Add yöntemine yeni bir varlık geçirdiğinizde bu varlığın durumu olarak Addedayarlanır. Ardından SaveChanges yöntemini çağırdığınızda veritabanı bağlamı bir SQL INSERT komutu döndürür.

Bir varlıkaşağıdaki durumlardan birinde olabilir:

  • Added. Varlık henüz veritabanında yok. yönteminin SaveChanges bir INSERT deyim vermesi gerekir.
  • Unchanged. yöntemiyle bu varlıkla SaveChanges hiçbir şey yapılmasına gerek yoktur. Veritabanından bir varlığı okuduğunuzda varlık bu durumla başlar.
  • Modified. Varlığın özellik değerlerinin bir kısmı veya tümü değiştirildi. yönteminin SaveChanges bir UPDATE deyim vermesi gerekir.
  • Deleted. Varlık silinmek üzere işaretlendi. yönteminin SaveChanges bir DELETE deyimi vermesi gerekir.
  • Detached. Varlık veritabanı bağlamı tarafından izlenmiyor.

Bir masaüstü uygulamasında durum değişiklikleri genellikle otomatik olarak ayarlanır. Masaüstü uygulama türünde bir varlığı okur ve bazı özellik değerlerinde değişiklik yaparsınız. Bu, varlık durumunun otomatik olarak olarak olarak değiştirilmesine Modifiedneden olur. Ardından çağırdığınızda SaveChanges, Entity Framework yalnızca değiştirdiğiniz gerçek özellikleri güncelleştiren bir SQL UPDATE deyimi oluşturur.

Web uygulamalarının bağlantısız yapısı bu sürekli diziye izin vermez. Bir varlığı okuyan DbContext , bir sayfa işlendikten sonra atılır. HttpPostEdit Eylem yöntemi çağrıldığında, yeni bir istek yapılır ve DbContext'in yeni bir örneğine sahip olursunuz, bu nedenle çağırdığınızda SaveChangesvarlık durumunu Modified. Sonra olarak el ile ayarlamanız gerekir. Bağlamın hangi özellikleri değiştirdiğinize dair bir yolu olmadığından Entity Framework veritabanı satırının tüm sütunlarını güncelleştirir.

SQL Update deyiminin yalnızca kullanıcının gerçekten değiştirdiği alanları güncelleştirmesini istiyorsanız, özgün değerleri bir şekilde (gizli alanlar gibi) kaydederek yöntem çağrıldığında HttpPostEdit kullanılabilir olmasını sağlayabilirsiniz. Ardından özgün değerleri kullanarak bir Student varlık oluşturabilir, varlığın Attach özgün sürümüyle yöntemini çağırabilir, varlığın değerlerini yeni değerlerle güncelleştirebilir ve daha fazla bilgi için MSDN Veri Geliştirici Merkezi'nde Varlık durumları ve SaveChanges ve Yerel Veriler'i çağırabilirsinizSaveChanges..

Views\Student\Edit.cshtml içindeki kod, Create.cshtml dosyasında gördüklerinize benzer ve hiçbir değişiklik gerekmez.

Öğrenciler sekmesini seçip düzenle köprüsüne tıklayarak sayfayı çalıştırın.

Student_Edit_page

Verilerin bazılarını değiştirin ve Kaydet'e tıklayın. Değiştirilen verileri Dizin sayfasında görürsünüz.

Students_Index_page_after_edit

Silme Sayfasını Güncelleştirme

Controllers\StudentController.cs dosyasında yönteminin HttpGetDelete şablon kodu, ve Edit yöntemlerinde Details gördüğünüz gibi seçili Student varlığı almak için yöntemini kullanırFind. Ancak çağrısı başarısız olduğunda özel bir hata iletisi uygulamak için SaveChanges bu yönteme ve buna karşılık gelen görünüme bazı işlevler ekleyeceksiniz.

Güncelleştirme ve oluşturma işlemlerini gördüğünüz gibi silme işlemleri için iki eylem yöntemi gerekir. GET isteğine yanıt olarak çağrılan yöntem, kullanıcıya silme işlemini onaylama veya iptal etme şansı veren bir görünüm görüntüler. Kullanıcı onaylarsa bir POST isteği oluşturulur. Bu durumda yöntemi HttpPostDelete çağrılır ve bu yöntem silme işlemini gerçekleştirir.

Veritabanı güncelleştirildiğinde oluşabilecek hataları işlemek için yöntemine bir try-catch blok HttpPostDelete ekleyeceksiniz. Bir hata oluşursa yöntemi HttpPostDelete yöntemini çağırarak HttpGetDelete bir hata oluştuğuna işaret eden bir parametre geçirir. Yöntemi HttpGet Delete daha sonra hata iletisiyle birlikte onay sayfasını yeniden dağıtarak kullanıcıya iptal etme veya yeniden deneme fırsatı verir.

  1. HttpGetDelete eylem yöntemini hata raporlamayı yöneten aşağıdaki kodla değiştirin:

    public ActionResult Delete(bool? saveChangesError=false, int id = 0)
    {
        if (saveChangesError.GetValueOrDefault())
        {
            ViewBag.ErrorMessage = "Delete failed. Try again, and if the problem persists see your system administrator.";
        }
        Student student = db.Students.Find(id);
        if (student == null)
        {
            return HttpNotFound();
        }
        return View(student);
    }
    

    Bu kod, değişiklikleri kaydetme hatasından sonra çağrılıp çağrılmadığını belirten isteğe bağlı bir Boole parametresini kabul eder. Bu parametre, false yöntemin HttpGetDelete önceki bir hata olmadan çağrıldığı durumdur. Veritabanı güncelleştirme hatasına HttpPostDelete yanıt olarak yöntemi tarafından çağrıldığında parametresi olur true ve görünüme bir hata iletisi geçirilir.

  2. HttpPostDelete Eylem yöntemini (adlandırılmışDeleteConfirmed) gerçek silme işlemini gerçekleştiren ve veritabanı güncelleştirme hatalarını yakalayan aşağıdaki kodla değiştirin.

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Delete(int id)
    {
        try
        {
            Student student = db.Students.Find(id);
            db.Students.Remove(student);
            db.SaveChanges();
        }
        catch (DataException/* dex */)
        {
            // uncomment dex and log error. 
            return RedirectToAction("Delete", new { id = id, saveChangesError = true });
        }
        return RedirectToAction("Index");
    }
    

    Bu kod seçili varlığı alır ve varlığın durumunu Deletedolarak ayarlamak için Remove yöntemini çağırır. çağrıldığında SaveChanges bir SQL DELETE komutu oluşturulur. Eylem yöntemi adını DeleteConfirmed olarak da değiştirdiniz Delete. yöntemine benzersiz bir imza vermek için yöntemini DeleteConfirmed adlı HttpPostDelete yapı iskelesi HttpPost oluşturulmuş kod. ( CLR, farklı yöntem parametrelerine sahip olmak için aşırı yüklenmiş yöntemler gerektirir.) artık imzalar benzersiz olduğuna göre, MVC kuralına bağlı kalarak ve HttpGet silme yöntemleri için HttpPost aynı adı kullanabilirsiniz.

    Yüksek hacimli bir uygulamada performansı geliştirmek öncelikliyse, ve Remove yöntemlerini çağıran Find kod satırlarını sarı vurguda gösterildiği gibi aşağıdaki kodla değiştirerek gereksiz bir SQL sorgusunun satırı almasını önleyebilirsiniz:

    Student studentToDelete = new Student() { StudentID = id };
    db.Entry(studentToDelete).State = EntityState.Deleted;
    

    Bu kod yalnızca birincil anahtar değerini kullanarak bir Student varlığın örneğini oluşturur ve varlık durumunu olarak Deletedayarlar. Varlığı silmek için Entity Framework'e gerekenler bunlardır.

    Belirtildiği gibi yöntemi HttpGetDelete verileri silmez. GET isteğine yanıt olarak silme işlemi gerçekleştirmek (veya bu nedenle herhangi bir düzenleme işlemi gerçekleştirmek, işlem oluşturmak veya verileri değiştiren başka bir işlem yapmak) bir güvenlik riski oluşturur. Daha fazla bilgi için bkz. ASP.NET MVC İpucu #46 — Stephen Walther'ın blog'larında Güvenlik Açıkları oluşturdukları için Bağlantıları Sil'i kullanmayın .

  3. Views\Student\Delete.cshtml içinde, aşağıdaki örnekte gösterildiği gibi başlıkla h3 başlık arasına h2 bir hata iletisi ekleyin:

    <h2>Delete</h2>
    <p class="error">@ViewBag.ErrorMessage</p>
    <h3>Are you sure you want to delete this?</h3>
    

    Öğrenciler sekmesini seçip Sil köprüsüne tıklayarak sayfayı çalıştırın:

    Student_Delete_page

  4. Sil'e tıklayın. Dizin sayfası, silinen öğrenci olmadan görüntülenir. (Bu serinin devamında Eşzamanlılık İşleme öğreticisinde hata işleme kodunun bir örneğini göreceksiniz.)

Veritabanı Bağlantılarının Açık Bırakılmamasını Sağlama

veritabanı bağlantıları düzgün bir şekilde kapatıldığından ve barındırdıkları kaynakların boşaltıldığından emin olmak için bağlam örneğinin atıldığını görmeniz gerekir. Bu nedenle, aşağıdaki örnekte gösterildiği gibi, yapı iskelesi oluşturulmuş kod StudentController.cs'de sınıfın StudentController sonunda bir Dispose yöntemi sağlar:

protected override void Dispose(bool disposing)
{
    db.Dispose();
    base.Dispose(disposing);
}

Temel Controller sınıf arabirimini IDisposable zaten uygular, bu nedenle bu kod bağlam örneğini açıkça atmak için Dispose(bool) yöntemine bir geçersiz kılma ekler.

Özet

Artık varlıklar için Student basit CRUD işlemleri gerçekleştiren eksiksiz bir sayfa kümeniz var. Veri alanları için kullanıcı arabirimi öğeleri oluşturmak için MVC yardımcılarını kullandınız. MVC yardımcıları hakkında daha fazla bilgi için bkz . HTML Yardımcılarını Kullanarak Form İşleme (sayfa MVC 3 içindir ancak MVC 4 için hala geçerlidir).

Sonraki öğreticide, sıralama ve sayfalama ekleyerek Dizin sayfasının işlevselliğini genişleteceksiniz.

Diğer Entity Framework kaynaklarına bağlantılar ASP.NET Veri Erişimi İçerik Eşlemesi'nde bulunabilir.