bölüm 2, Razor ASP.NET Core EF Core olan sayfalar-crud

Tom Dykstra, Jeremy Liklikve Jon P Smith tarafından

Contoso Üniversitesi web uygulaması, Razor EF Core ve Visual Studio kullanarak nasıl sayfa Web uygulamaları oluşturacağınızı gösterir. Öğretici serisi hakkında daha fazla bilgi için ilk öğreticiyebakın.

Çözemediğiniz sorunlarla karşılaşırsanız, Tamamlanmış uygulamayı indirin ve öğreticiyi izleyerek bu kodu oluşturduğunuz şekilde karşılaştırın.

Bu öğreticide, scafkatan CRUD (oluşturma, okuma, güncelleştirme, silme) kodu incelenir ve özelleştirilir.

Depo yok

Bazı geliştiriciler, Kullanıcı arabirimi ( Razor Sayfalar) ve veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için bir hizmet katmanı veya depo deseninin kullanılmasını sağlar. Bu öğretici bunu yapmaz. Karmaşıklığı en aza indirmek ve öğreticiyi EF Core odaklanmasını sağlamak için EF Core kodu doğrudan sayfa modeli sınıflarına eklenir.

Ayrıntılar sayfasını Güncelleştir

Öğrenciler sayfaları için yapı iskelesi kodu kayıt verilerini içermez. Bu bölümde, kayıtlar Details sayfaya eklenir.

Kayıtları oku

Sayfada öğrenciye ait kayıt verilerini göstermek için kayıt verilerinin okunmalıdır. Sayfalardaki/öğrencilerle/details. cshtml. cs içindeki scafkatlanmış kod, Student verileri olmayan yalnızca verileri okur Enrollment :

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

Yöntemi, OnGetAsync Seçili öğrenci için kayıt verilerini okumak üzere aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students
        .Include(s => s.Enrollments)
        .ThenInclude(e => e.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

Include ve thenınclude yöntemleri, içeriğin Student.Enrollments gezinti özelliğini yüklemesine ve her kaydın gezinti özelliği içine olmasına neden olur Enrollment.Course . Bu yöntemler, ilgili verileri oku öğreticisinde ayrıntılı olarak incelendi.

Asnotracking yöntemi, döndürülen varlıkların geçerli bağlamda güncelleştirilmediği senaryolarda performansı geliştirir. AsNoTracking Bu öğreticinin ilerleyen kısımlarında ele alınmıştır.

Kayıtları görüntüle

Sayfalar/öğrenciler/details. cshtml içindeki kodu aşağıdaki kodla değiştirin ve kayıtlar listesini görüntüleyin. Değişiklikler vurgulanır.

@page
@model ContosoUniversity.Pages.Students.DetailsModel

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.EnrollmentDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.Enrollments)
        </dt>
        <dd class="col-sm-10">
            <table class="table">
                <tr>
                    <th>Course Title</th>
                    <th>Grade</th>
                </tr>
                @foreach (var item in Model.Student.Enrollments)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Course.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Grade)
                        </td>
                    </tr>
                }
            </table>
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.Student.ID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>

Yukarıdaki kod, Gezinti özelliğindeki varlıklar aracılığıyla döngü başlatır Enrollments . Her kayıt için kurs başlığını ve sınıfı görüntüler. Kurs başlığı, kayıt Course Course varlığının gezinti özelliğinde depolanan varlıktan alınır.

Uygulamayı çalıştırın, öğrenciler sekmesini seçin ve bir öğrenci için Ayrıntılar bağlantısına tıklayın. Seçili öğrenci için Kurslar ve notlar listesi görüntülenir.

Bir varlığı okuma yolları

Oluşturulan kod, bir varlığı okumak için Firstordefaultasync kullanır. Bu yöntem, hiçbir şey bulunamazsa null değerini döndürür; Aksi takdirde, sorgu filtresi ölçütlerine uyan bulunan ilk satırı döndürür. FirstOrDefaultAsync genellikle aşağıdaki alternatiflere göre daha iyi bir seçimdir:

  • Singleordefaultasync -sorgu filtresini karşılayan birden fazla varlık varsa bir özel durum oluşturur. Sorgu tarafından birden fazla satır döndürülüp döndürülmeyeceğini anlamak için SingleOrDefaultAsync birden çok satır getirmeyi dener. Sorgu yalnızca bir varlık döndürebiliyorsanız ve benzersiz bir anahtarda arama yaptığında bu ek çalışma gereksizdir.
  • Findadsync -birincil ANAHTARLA (PK) bir varlık bulur. PK 'ye sahip bir varlık bağlam tarafından izleniyorsa, veritabanına bir istek olmadan döndürülür. Bu yöntem tek bir varlık aramak için en iyi duruma getirilmiştir, ancak ile çağrılamaz Include FindAsync . Bu nedenle, ilgili veriler gerekliyse, FirstOrDefaultAsync daha iyi bir seçimdir.

Veri yönlendirme ve sorgu dizesi karşılaştırması

Ayrıntılar sayfasının URL 'SI https://localhost:<port>/Students/Details?id=1 . Varlığın birincil anahtar değeri, sorgu dizesinde bulunur. Bazı geliştiriciler anahtar değerini rota verilerinde geçirmeye tercih eder: https://localhost:<port>/Students/Details/1 . Daha fazla bilgi için bkz. oluşturulan kodu güncelleştirme.

Oluştur sayfasını Güncelleştir

OnPostAsyncOluşturma sayfası için yapı iskelesi kodu, aşırı nakmeaçıktır. OnPostAsync Pages/öğrenciler/Create. cshtml. cs içindeki yöntemi aşağıdaki kodla değiştirin.

public async Task<IActionResult> OnPostAsync()
{
    var emptyStudent = new Student();

    if (await TryUpdateModelAsync<Student>(
        emptyStudent,
        "student",   // Prefix for form value.
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        _context.Students.Add(emptyStudent);
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

TryUpdateModelAsync

Yukarıdaki kod bir öğrenci nesnesi oluşturur ve ardından, öğrenci nesnesinin özelliklerini güncelleştirmek için, postalanan form alanlarını kullanır. Tryupdatemodelasync yöntemi:

  • PagemodelIçindeki pagecontext özelliğinden gönderilen form değerlerini kullanır.
  • Yalnızca listelenen () özellikleri güncelleştirir s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate .
  • "Öğrenci" ön ekine sahip form alanlarını arar. Örneğin, Student.FirstMidName. Büyük/küçük harfe duyarlı değildir.
  • , Dizelerdeki form değerlerini modeldeki türlere dönüştürmek için model bağlama sistemini kullanır Student . Örneğin, EnrollmentDate öğesine dönüştürülür DateTime .

Uygulamayı çalıştırın ve oluştur sayfasını test etmek için bir öğrenci varlığı oluşturun.

Fazla nakil

TryUpdateModelDeftere nakledilen değerler içeren alanları güncelleştirmek için kullanmak, aşırı nakletmeyi önlediği için en iyi güvenlik yöntemidir. Örneğin, öğrenci varlığının Secret Bu Web sayfasının güncelleştirmesi veya eklemesi gereken bir özelliği içerdiğini varsayalım:

public class Student
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
    public string Secret { get; set; }
}

Uygulamanın, Secret Oluştur veya Güncelleştir sayfasında bir alanı olmasa bile Razor , bir korsan Secret değeri aşırı nakme ile ayarlayabilir. Bir korsan, Fiddler gibi bir araç kullanabilir veya bir form değeri göndermek için bazı JavaScript yazabilir Secret . Özgün kod, bir öğrenci örneği oluştururken model cildin kullandığı alanları sınırlamaz.

Form alanı için belirtilen korsanın hangi değeri Secret veritabanında güncelleştirildiği. Aşağıdaki görüntüde, Secret "OverPost" değeri ile alanı, postalanan form değerlerine ekleyerek Fiddler aracı gösterilmektedir.

Fiddler gizli alanı ekleniyor

"OverPost" değeri Secret eklenen satırın özelliğine başarıyla eklendi. Bu durum, Uygulama Tasarımcısı hiçbir Secret şekilde özelliği oluştur sayfasıyla ayarlamaya yönelik değildir.

Modeli görüntüleme

Model görüntüleme, fazla nakletmeyi önlemenin alternatif bir yolunu sağlar.

Uygulama modeli genellikle etki alanı modeli olarak adlandırılır. Etki alanı modeli genellikle veritabanında ilgili varlık için gereken tüm özellikleri içerir. Görünüm modeli yalnızca UI sayfası için gereken özellikleri içerir, örneğin, Oluştur sayfası.

Görünüm modeline ek olarak, bazı uygulamalar sayfa Razor modeli sınıfı ve tarayıcı arasında veri geçirmek için bir bağlama modeli veya giriş modeli kullanır.

Aşağıdaki görünüm modelini göz önünde bulundurun StudentVM :

public class StudentVM
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
}

Aşağıdaki kod, StudentVM Yeni bir öğrenci oluşturmak için görünüm modelini kullanır:

[BindProperty]
public StudentVM StudentVM { get; set; }

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    var entry = _context.Add(new Student());
    entry.CurrentValues.SetValues(StudentVM);
    await _context.SaveChangesAsync();
    return RedirectToPage("./Index");
}

SetValues yöntemi, başka bir PropertyValues nesnesinden değerleri okuyarak bu nesnenin değerlerini ayarlar. SetValues Özellik adı eşleştirme kullanır. Görünüm modeli türü:

  • Model türüyle ilgili olması gerekmez.
  • Eşleşen özelliklerin olması gerekir.

Kullanmak StudentVM yerine, oluşturma sayfası kullanımını gerektirir StudentVM Student :

@page
@model CreateVMModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Student</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="StudentVM.LastName" class="control-label"></label>
                <input asp-for="StudentVM.LastName" class="form-control" />
                <span asp-validation-for="StudentVM.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="StudentVM.FirstMidName" class="control-label"></label>
                <input asp-for="StudentVM.FirstMidName" class="form-control" />
                <span asp-validation-for="StudentVM.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="StudentVM.EnrollmentDate" class="control-label"></label>
                <input asp-for="StudentVM.EnrollmentDate" class="form-control" />
                <span asp-validation-for="StudentVM.EnrollmentDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Düzenleme sayfasını Güncelleştir

Sayfalar/öğrenciler/Edit. cshtml. cs' de, OnGetAsync ve OnPostAsync yöntemlerini aşağıdaki kodla değiştirin.

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students.FindAsync(id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

public async Task<IActionResult> OnPostAsync(int id)
{
    var studentToUpdate = await _context.Students.FindAsync(id);

    if (studentToUpdate == null)
    {
        return NotFound();
    }

    if (await TryUpdateModelAsync<Student>(
        studentToUpdate,
        "student",
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

Kod değişiklikleri, birkaç özel durum dışında oluşturma sayfasına benzerdir:

  • FirstOrDefaultAsync , Findadsyncile değiştirilmiştir. İlgili verileri dahil etmeniz gerekmiyorsa, FindAsync daha etkilidir.
  • OnPostAsync bir id parametreye sahiptir.
  • Geçerli öğrenci boş bir öğrenci oluşturmak yerine veritabanından getirilir.

Uygulamayı çalıştırın ve bir öğrenci oluşturup düzenleyerek test edin.

Varlık durumları

Veritabanı bağlamı, bellekteki varlıkların veritabanında karşılık gelen satırlarıyla eşitlenmiş olup olmadığını izler. Bu izleme bilgileri, Savechangesasync çağrıldığında ne olacağını belirler. Örneğin, yeni bir varlık Addadsync yöntemine geçirildiğinde, bu varlığın durumu eklendiolarak ayarlanır. SaveChangesAsyncçağrıldığında, veritabanı bağlamı bir SQL INSERT komutu yayınlar.

Bir varlık aşağıdaki durumlardanbirinde olabilir:

  • Added: Varlık veritabanında henüz yok. SaveChangesYöntemi bir ifadesini yayınlar INSERT .

  • Unchanged: Bu varlığa hiçbir değişiklik kaydedilene gerek yoktur. Bir varlık, veritabanından okunurken bu durumda olur.

  • Modified: Varlığın özellik değerlerinin bazıları veya hepsi değiştirildi. yöntemi SaveChanges bir UPDATE deyimini kullanır.

  • Deleted: Varlık silinmek üzere işaretlendi. yöntemi SaveChanges bir DELETE deyimini kullanır.

  • Detached: Varlık veritabanı bağlamı tarafından izlenmmektedir.

Bir masaüstü uygulamasında durum değişiklikleri genellikle otomatik olarak ayarlanır. Varlık okunur, değişiklikler yapılır ve varlık durumu otomatik olarak olarak Modified değiştirilir. çağrısı, SaveChanges yalnızca SQL özellikleri UPDATE güncelleştirmeye ilişkin bir SQL deyimi üretir.

Bir web uygulamasında, DbContext bir varlığı okur ve verileri görüntüleyen, sayfa işlenen sonra at olur. Bir sayfanın yöntemi çağrıldı olduğunda, yeni bir web isteği yapılır ve OnPostAsync yeni bir örneğiyle birlikte DbContext . Varlığın bu yeni bağlamda yeniden okunarak masaüstü işleme benzetimi gerçek anlamda gerçek hale gelir.

Sil sayfasını güncelleştirme

Bu bölümde çağrısı başarısız olduğunda özel bir hata iletisi SaveChanges uygulanır.

Pages/Students/Delete.cshtml.cs'de yer alan kodu aşağıdaki kodla değiştirin:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Students
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;
        private readonly ILogger<DeleteModel> _logger;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context,
                           ILogger<DeleteModel> logger)
        {
            _context = context;
            _logger = logger;
        }

        [BindProperty]
        public Student Student { get; set; }
        public string ErrorMessage { get; set; }

        public async Task<IActionResult> OnGetAsync(int? id, bool? saveChangesError = false)
        {
            if (id == null)
            {
                return NotFound();
            }

            Student = await _context.Students
                .AsNoTracking()
                .FirstOrDefaultAsync(m => m.ID == id);

            if (Student == null)
            {
                return NotFound();
            }

            if (saveChangesError.GetValueOrDefault())
            {
                ErrorMessage = String.Format("Delete {ID} failed. Try again", id);
            }

            return Page();
        }

        public async Task<IActionResult> OnPostAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var student = await _context.Students.FindAsync(id);

            if (student == null)
            {
                return NotFound();
            }

            try
            {
                _context.Students.Remove(student);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            catch (DbUpdateException ex)
            {
                _logger.LogError(ex, ErrorMessage);

                return RedirectToAction("./Delete",
                                     new { id, saveChangesError = true });
            }
        }
    }
}

Yukarıdaki kod:

  • Günlüğe Kaydetme ekler.
  • Yöntem imzasını isteğe saveChangesError bağlı OnGetAsync parametresini ekler. saveChangesError , öğrenci nesnesini silme hatasının ardından yönteminin çağrılıp çağrılma olmadığını gösterir.

Geçici ağ sorunları nedeniyle silme işlemi başarısız olabilir. Veritabanı bulutta olduğunda geçici ağ hataları daha olasıdır. saveChangesErrorparametresi, false Kullanıcı Arabiriminden Sil sayfası çağrıldı OnGetAsync mı? silme OnGetAsync işlemi başarısız olduğundan tarafından çağrıldı mı OnPostAsync saveChangesError true parametresidir.

yöntemi OnPostAsync seçili varlığı aldı ve ardından Varlığın durumunu olarak ayarlamak için Remove yöntemini Deleted çağırıyor. çağrıldı SaveChanges mı, SQL komutu DELETE oluşturulur. Başarısız Remove olursa:

  • Veritabanı özel durumu yakalır.
  • Sayfaları sil yöntemi OnGetAsync ile çağrılır. saveChangesError=true

Pages/Students/Delete.cshtml'ye hata iletisi ekleyin:

@page
@model ContosoUniversity.Pages.Students.DeleteModel

@{
    ViewData["Title"] = "Delete";
}

<h1>Delete</h1>

<p class="text-danger">@Model.ErrorMessage</p>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.EnrollmentDate)
        </dd>
    </dl>

    <form method="post">
        <input type="hidden" asp-for="Student.ID" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Uygulamayı çalıştırın ve Sil sayfasını test etmek için bir öğrenciyi silin.

Sonraki adımlar

Bu öğreticide, iskelesi yapılan CRUD (oluşturma, okuma, güncelleştirme, silme) kodu gözden geçirildi ve özelleştirilebilir.

Depo yok

Bazı geliştiriciler kullanıcı arabirimi ( Sayfalar) ile veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için bir hizmet Razor katmanı veya depo deseni kullanır. Bu öğretici bunu yapmaz. Karmaşıklığı en aza indirmek ve öğreticiyi EF Core tutmak EF Core doğrudan sayfa modeli sınıflarına eklenir.

Ayrıntılar sayfasını güncelleştirme

Öğrenciler sayfaları için iskelesi olan kod kayıt verilerini içermemektedir. Bu bölümde kayıtlar Ayrıntılar sayfasına eklenir.

Kayıtları okuma

Bir öğrencinin kayıt verilerini sayfada görüntülemek için kayıt verilerini okumanız gerekir. Pages/Students/Details.cshtml.cs içinde iskelesi yapılan kod, Kayıt verileri olmadan yalnızca Öğrenci verilerini okur:

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

Seçilen OnGetAsync öğrencinin kayıt verilerini okumak için yöntemini aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students
        .Include(s => s.Enrollments)
        .ThenInclude(e => e.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

Include ve ThenInclude yöntemleri, bağlamın gezinti özelliğini ve her kayıt içinde Student.Enrollments gezinti özelliğini Enrollment.Course yüklemesini sağlar. Bu yöntemler, İlgili verileri okuma öğreticisinde ayrıntılı olarak incelendi.

AsNoTracking yöntemi, döndürülen varlıkların geçerli bağlamda güncelleştirilmeyen senaryolarda performansı artırır. AsNoTracking bu öğreticinin sonraki adımlarında ele alınmıştır.

Kayıtları görüntüleme

Kayıtların listesini görüntülemek için Pages/Students/Details.cshtml kodunu aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

@page
@model ContosoUniversity.Pages.Students.DetailsModel

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.EnrollmentDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.Enrollments)
        </dt>
        <dd class="col-sm-10">
            <table class="table">
                <tr>
                    <th>Course Title</th>
                    <th>Grade</th>
                </tr>
                @foreach (var item in Model.Student.Enrollments)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Course.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Grade)
                        </td>
                    </tr>
                }
            </table>
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.Student.ID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>

Yukarıdaki kod gezinti özelliğinde varlıklar arasında Enrollments döngüye alar. Her kayıt için kurs başlığı ve not görüntülenir. Kurs başlığı, Kayıtlar varlığının gezinti özelliğinde depolanan Course Kurs varlığından alınır.

Uygulamayı çalıştırın, Öğrenciler sekmesini seçin ve bir öğrencinin Ayrıntılar bağlantısına tıklayın. Seçilen öğrenciye yönelik kurslar ve notlar listesi görüntülenir.

Bir varlığı okumanın yolları

Oluşturulan kod, bir varlığı okumak için FirstOrDefaultAsync kullanır. Bu yöntem, hiçbir şey bulunamazsa null döndürür; aksi takdirde, sorgu filtresi ölçütlerini yerine getiren ilk satırı döndürür. FirstOrDefaultAsync genellikle aşağıdaki alternatiflerden daha iyi bir seçimdir:

  • SingleOrDefaultAsync - Sorgu filtresini karşılar birden fazla varlık varsa bir özel durum oluşturur. Sorgu tarafından birden fazla satırın döndürülenin olup olmadığını belirlemek için birden SingleOrDefaultAsync çok satırı getirmeye çalışır. Sorgu benzersiz bir anahtarda arama yaptığında olduğu gibi yalnızca bir varlık iade ediyorsa bu ek çalışma gereksizdir.
  • FindAsync - Birincil anahtara (PK) sahip bir varlık bulur. PK'ye sahip bir varlık bağlam tarafından iz ediliyorsa veritabanına istek olmadan döndürülür. Bu yöntem tek bir varlığı aramak için iyileştirilmiştir, ancak ile çağrısı yapmak Include mümkün FindAsync değildir. Bu nedenle ilgili veriler gerekli ise FirstOrDefaultAsync daha iyi bir seçenektir.

Verileri yönlendirme ve sorgu dizesi karşılaştırması

Ayrıntılar sayfasının URL'si şu https://localhost:<port>/Students/Details?id=1 şekildedir: . Varlığın birincil anahtar değeri sorgu dizesindedir. Bazı geliştiriciler, yol verisinde anahtar değerini geçmeyi tercih eder: https://localhost:<port>/Students/Details/1 . Daha fazla bilgi için bkz. Oluşturulan kodu güncelleştirme.

Oluştur sayfasını güncelleştirme

Create (Oluştur) OnPostAsync sayfasının iskelesi olan kod, üzerinepostaya eklemeye karşı savunmasızdır. OnPostAsync Pages/Students/Create.cshtml.cs içinde yöntemini aşağıdaki kodla değiştirin.

public async Task<IActionResult> OnPostAsync()
{
    var emptyStudent = new Student();

    if (await TryUpdateModelAsync<Student>(
        emptyStudent,
        "student",   // Prefix for form value.
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        _context.Students.Add(emptyStudent);
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

TryUpdateModelAsync

Yukarıdaki kod bir Student nesnesi oluşturur ve ardından Student nesnesinin özelliklerini güncelleştirmek için gönderilen form alanlarını kullanır. TryUpdateModelAsync yöntemi:

  • PageModel içinde PageContext özelliğinden gönderilen form değerlerini kullanır.
  • Yalnızca listelenen özellikleri ( ) s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate ler.
  • "Öğrenci" ön ekli form alanlarını arayın. Örneğin, Student.FirstMidName. Büyük/büyük/büyük harfe duyarlı değildir.
  • Form değerlerini dizelerden model türüne dönüştürmek için model bağlama sistemini Student kullanır. Örneğin, EnrollmentDate DateTime'a dönüştürülmesi gerekmektedir.

Uygulamayı çalıştırın ve Oluştur sayfasını test etmek için bir öğrenci varlığı oluşturun.

Fazla Postalama

Alanları TryUpdateModel gönderilen değerlerle güncelleştirmek için kullanmak, aşırı postayı önleyene kadar güvenlik için en iyi yöntemdir. Örneğin, Öğrenci varlığının bu Secret web sayfasının güncelleştirmeması veya eklemesi gereken bir özelliği olduğunu varsayalım:

public class Student
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
    public string Secret { get; set; }
}

Uygulamanın Oluşturma veya Güncelleştirme Sayfasında bir alanı yoksa bile, bir bilgisayar korsanı fazla posta Secret Razor ile değeri Secret oluşturabilir. Bir bilgisayar korsanı, Form değeri oluşturmak için Fiddler gibi bir araç kullanabilir veya javaScript Secret yazabilir. Özgün kod, model bağlayıcısı bir Student örneği oluşturduğunda kullandığı alanları sınırlamaz.

Bilgisayar korsanı form alanı için Secret belirtilen değer veritabanında güncelleştirilir. Aşağıdaki görüntüde, gönderilen form değerlerine alanını ("OverPost" değeriyle) ekleyen Fiddler Secret aracı yer alır.

Fiddler Gizli Değer ekleme alanı

Eklenen satırın özelliğine "OverPost" Secret değeri başarıyla eklenir. Bu durum, uygulama tasarımcısı özelliğin Oluştur Secret sayfasıyla ayarlansa bile hiç amaçlanmaz.

Modeli görüntüleme

Model görüntüleme, fazla nakletmeyi önlemenin alternatif bir yolunu sağlar.

Uygulama modeli genellikle etki alanı modeli olarak adlandırılır. Etki alanı modeli genellikle veritabanında ilgili varlık için gereken tüm özellikleri içerir. Görünüm modeli yalnızca için kullanılan Kullanıcı arabirimi için gereken özellikleri içerir (örneğin, oluşturma sayfası).

Görünüm modeline ek olarak, bazı uygulamalar sayfa Razor modeli sınıfı ve tarayıcı arasında veri geçirmek için bir bağlama modeli veya giriş modeli kullanır.

Aşağıdaki görünüm modelini göz önünde bulundurun Student :

using System;

namespace ContosoUniversity.Models
{
    public class StudentVM
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
    }
}

Aşağıdaki kod, StudentVM Yeni bir öğrenci oluşturmak için görünüm modelini kullanır:

[BindProperty]
public StudentVM StudentVM { get; set; }

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    var entry = _context.Add(new Student());
    entry.CurrentValues.SetValues(StudentVM);
    await _context.SaveChangesAsync();
    return RedirectToPage("./Index");
}

SetValues yöntemi, başka bir PropertyValues nesnesinden değerleri okuyarak bu nesnenin değerlerini ayarlar. SetValues Özellik adı eşleştirme kullanır. Görünüm modeli türünün model türüyle ilgili olması gerekmez, yalnızca eşleşen özellikleri olması gerekir.

Kullanılması StudentVM Için Create. cshtml kullanılması gerekir StudentVM Student .

Düzenleme sayfasını Güncelleştir

Sayfalar/öğrenciler/Edit. cshtml. cs' de, OnGetAsync ve OnPostAsync yöntemlerini aşağıdaki kodla değiştirin.

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students.FindAsync(id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

public async Task<IActionResult> OnPostAsync(int id)
{
    var studentToUpdate = await _context.Students.FindAsync(id);

    if (studentToUpdate == null)
    {
        return NotFound();
    }

    if (await TryUpdateModelAsync<Student>(
        studentToUpdate,
        "student",
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

Kod değişiklikleri, birkaç özel durum dışında oluşturma sayfasına benzerdir:

  • FirstOrDefaultAsync , Findadsyncile değiştirilmiştir. Dahil edilen veriler gerekmiyorsa, FindAsync daha etkilidir.
  • OnPostAsync bir id parametreye sahiptir.
  • Geçerli öğrenci boş bir öğrenci oluşturmak yerine veritabanından getirilir.

Uygulamayı çalıştırın ve bir öğrenci oluşturup düzenleyerek test edin.

Varlık durumları

Veritabanı bağlamı, bellekteki varlıkların veritabanında karşılık gelen satırlarıyla eşitlenmiş olup olmadığını izler. Bu izleme bilgileri, Savechangesasync çağrıldığında ne olacağını belirler. Örneğin, yeni bir varlık Addadsync yöntemine geçirildiğinde, bu varlığın durumu eklendiolarak ayarlanır. SaveChangesAsyncçağrıldığında, veritabanı bağlamı SQL bir ınsert komutu yayınlar.

Bir varlık aşağıdaki durumlardanbirinde olabilir:

  • Added: Varlık veritabanında henüz yok. SaveChangesYöntemi BIR INSERT ifadesini yayınlar.

  • Unchanged: Bu varlıkla birlikte hiçbir değişiklik kaydedilmesi gerekmiyor. Bir varlık veritabanından okurken bu durumu içerir.

  • Modified: Varlığın özellik değerlerinin bazıları veya tümü değiştirildi. SaveChangesYöntemi BIR Update ifadesini yayınlar.

  • Deleted: Varlık silinmek üzere işaretlendi. SaveChangesYöntemi BIR DELETE ifadesini yayınlar.

  • Detached: Varlık veritabanı bağlamı tarafından izlenmiyor.

Bir masaüstü uygulamasında durum değişiklikleri genellikle otomatik olarak ayarlanır. Bir varlık okundu, değişiklikler yapılır ve varlık durumu otomatik olarak olarak değiştirilir Modified . çağırma SaveChanges , yalnızca değiştirilen özellikleri güncelleştiren bir SQL UPDATE bildirisi oluşturur.

Bir Web uygulamasında, bir DbContext varlığı okur ve bir sayfa işlendikten sonra verileri görüntüler. Bir sayfanın OnPostAsync yöntemi çağrıldığında, yeni bir Web isteği oluşturulur ve yeni bir örneğine sahiptir DbContext . Okuyarak bu yeni bağlamdaki varlığı, masaüstü işlemesini benzetir.

Silme sayfasını Güncelleştir

Bu bölümde, çağrısı başarısız olduğunda özel bir hata iletisi uygulayacağınızı görürsünüz SaveChanges .

Pages/öğrenciler/delete. cshtml. cs dosyasındaki kodu aşağıdaki kodla değiştirin. Değişiklikler vurgulanır (deyimler temizliği dışında using ).

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Students
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Student Student { get; set; }
        public string ErrorMessage { get; set; }

        public async Task<IActionResult> OnGetAsync(int? id, bool? saveChangesError = false)
        {
            if (id == null)
            {
                return NotFound();
            }

            Student = await _context.Students
                .AsNoTracking()
                .FirstOrDefaultAsync(m => m.ID == id);

            if (Student == null)
            {
                return NotFound();
            }

            if (saveChangesError.GetValueOrDefault())
            {
                ErrorMessage = "Delete failed. Try again";
            }

            return Page();
        }

        public async Task<IActionResult> OnPostAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var student = await _context.Students.FindAsync(id);

            if (student == null)
            {
                return NotFound();
            }

            try
            {
                _context.Students.Remove(student);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            catch (DbUpdateException /* ex */)
            {
                //Log the error (uncomment ex variable name and write a log.)
                return RedirectToAction("./Delete",
                                     new { id, saveChangesError = true });
            }
        }
    }
}

Yukarıdaki kod, isteğe bağlı parametresini saveChangesError OnGetAsync Yöntem imzasına ekler. saveChangesError öğrenci nesnesini silme hatasından sonra yöntemin çağrılıp çağrılmadığını gösterir. Geçici ağ sorunları nedeniyle silme işlemi başarısız olabilir. Geçici ağ hataları, veritabanı bulutta olduğunda daha olasıdır. saveChangesErrorSil sayfası kullanıcı arabiriminden çağrıldığında parametre false 'tur OnGetAsync . OnGetAsyncTarafından çağrıldığında OnPostAsync (silme işlemi başarısız olduğundan), saveChangesError parametresi true olur.

OnPostAsyncYöntemi seçili varlığı alır, ardından varlığın durumunu olarak ayarlamak Için Remove yöntemini çağırır Deleted . SaveChangesçağrıldığında, bir SQL DELETE komutu oluşturulur. RemoveBaşarısız olursa:

  • Veritabanı özel durumu yakalandı.
  • Delete sayfasının OnGetAsync yöntemi ile çağrılır saveChangesError=true .

Silme sayfasına bir hata iletisi ekleyin Razor (Sayfalar/öğrenciler/delete. cshtml):

@page
@model ContosoUniversity.Pages.Students.DeleteModel

@{
    ViewData["Title"] = "Delete";
}

<h1>Delete</h1>

<p class="text-danger">@Model.ErrorMessage</p>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.EnrollmentDate)
        </dd>
    </dl>

    <form method="post">
        <input type="hidden" asp-for="Student.ID" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Uygulamayı çalıştırın ve Sil sayfasını test etmek için bir öğrenci silin.

Sonraki adımlar

Bu öğreticide, scafkatan CRUD (oluşturma, okuma, güncelleştirme, silme) kodu incelenir ve özelleştirilir.

Depo yok

Bazı geliştiriciler, Kullanıcı arabirimi ( Razor Sayfalar) ve veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için bir hizmet katmanı veya depo deseninin kullanılmasını sağlar. Bu öğretici bunu yapmaz. Karmaşıklığı en aza indirmek ve öğreticiyi EF Core odaklanmasını sağlamak için EF Core kodu doğrudan sayfa modeli sınıflarına eklenir.

Ayrıntılar sayfasını Güncelleştir

Öğrenciler sayfaları için yapı iskelesi kodu kayıt verilerini içermez. Bu bölümde, kayıtlar Details sayfaya eklenir.

Kayıtları oku

Sayfada öğrenciye ait kayıt verilerini göstermek için kayıt verilerinin okunmalıdır. Sayfalardaki/öğrencilerle/details. cshtml. cs içindeki scafkatlanmış kod, Student verileri olmayan yalnızca verileri okur Enrollment :

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

Yöntemi, OnGetAsync Seçili öğrenci için kayıt verilerini okumak üzere aşağıdaki kodla değiştirin. Değişiklikler vurgulanır.

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students
        .Include(s => s.Enrollments)
        .ThenInclude(e => e.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

Include ve thenınclude yöntemleri, içeriğin Student.Enrollments gezinti özelliğini yüklemesine ve her kaydın gezinti özelliği içine olmasına neden olur Enrollment.Course . Bu yöntemler, ilgili verileri oku öğreticisinde ayrıntılı olarak incelendi.

Asnotracking yöntemi, döndürülen varlıkların geçerli bağlamda güncelleştirilmediği senaryolarda performansı geliştirir. AsNoTracking Bu öğreticinin ilerleyen kısımlarında ele alınmıştır.

Kayıtları görüntüle

Sayfalar/öğrenciler/details. cshtml içindeki kodu aşağıdaki kodla değiştirin ve kayıtlar listesini görüntüleyin. Değişiklikler vurgulanır.

@page
@model ContosoUniversity.Pages.Students.DetailsModel

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.EnrollmentDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.Enrollments)
        </dt>
        <dd class="col-sm-10">
            <table class="table">
                <tr>
                    <th>Course Title</th>
                    <th>Grade</th>
                </tr>
                @foreach (var item in Model.Student.Enrollments)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Course.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Grade)
                        </td>
                    </tr>
                }
            </table>
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.Student.ID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>

Yukarıdaki kod, Gezinti özelliğindeki varlıklar aracılığıyla döngü başlatır Enrollments . Her kayıt için kurs başlığını ve sınıfı görüntüler. Kurs başlığı, kayıt Course Course varlığının gezinti özelliğinde depolanan varlıktan alınır.

Uygulamayı çalıştırın, öğrenciler sekmesini seçin ve bir öğrenci için Ayrıntılar bağlantısına tıklayın. Seçili öğrenci için Kurslar ve notlar listesi görüntülenir.

Bir varlığı okuma yolları

Oluşturulan kod, bir varlığı okumak için Firstordefaultasync kullanır. Bu yöntem, hiçbir şey bulunamazsa null değerini döndürür; Aksi takdirde, sorgu filtresi ölçütlerine uyan bulunan ilk satırı döndürür. FirstOrDefaultAsync genellikle aşağıdaki alternatiflere göre daha iyi bir seçimdir:

  • Singleordefaultasync -sorgu filtresini karşılayan birden fazla varlık varsa bir özel durum oluşturur. Sorgu tarafından birden fazla satır döndürülüp döndürülmeyeceğini anlamak için SingleOrDefaultAsync birden çok satır getirmeyi dener. Sorgu yalnızca bir varlık döndürebiliyorsanız ve benzersiz bir anahtarda arama yaptığında bu ek çalışma gereksizdir.
  • Findadsync -birincil ANAHTARLA (PK) bir varlık bulur. PK 'ye sahip bir varlık bağlam tarafından izleniyorsa, veritabanına bir istek olmadan döndürülür. Bu yöntem tek bir varlık aramak için en iyi duruma getirilmiştir, ancak ile çağrılamaz Include FindAsync . Bu nedenle, ilgili veriler gerekliyse, FirstOrDefaultAsync daha iyi bir seçimdir.

Veri yönlendirme ve sorgu dizesi karşılaştırması

Ayrıntılar sayfasının URL 'SI https://localhost:<port>/Students/Details?id=1 . Varlığın birincil anahtar değeri, sorgu dizesinde bulunur. Bazı geliştiriciler anahtar değerini rota verilerinde geçirmeye tercih eder: https://localhost:<port>/Students/Details/1 . Daha fazla bilgi için bkz. oluşturulan kodu güncelleştirme.

Oluştur sayfasını Güncelleştir

OnPostAsyncOluşturma sayfası için yapı iskelesi kodu, aşırı nakmeaçıktır. OnPostAsync Pages/öğrenciler/Create. cshtml. cs içindeki yöntemi aşağıdaki kodla değiştirin.

public async Task<IActionResult> OnPostAsync()
{
    var emptyStudent = new Student();

    if (await TryUpdateModelAsync<Student>(
        emptyStudent,
        "student",   // Prefix for form value.
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        _context.Students.Add(emptyStudent);
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

TryUpdateModelAsync

Yukarıdaki kod bir öğrenci nesnesi oluşturur ve ardından, öğrenci nesnesinin özelliklerini güncelleştirmek için, postalanan form alanlarını kullanır. Tryupdatemodelasync yöntemi:

  • PagemodelIçindeki pagecontext özelliğinden gönderilen form değerlerini kullanır.
  • Yalnızca listelenen () özellikleri güncelleştirir s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate .
  • "Öğrenci" ön ekine sahip form alanlarını arar. Örneğin, Student.FirstMidName. Büyük/küçük harfe duyarlı değildir.
  • , Dizelerdeki form değerlerini modeldeki türlere dönüştürmek için model bağlama sistemini kullanır Student . Örneğin, EnrollmentDate öğesine dönüştürülür DateTime .

Uygulamayı çalıştırın ve oluştur sayfasını test etmek için bir öğrenci varlığı oluşturun.

Fazla nakil

TryUpdateModelDeftere nakledilen değerler içeren alanları güncelleştirmek için kullanmak, aşırı nakletmeyi önlediği için en iyi güvenlik yöntemidir. Örneğin, öğrenci varlığının Secret Bu Web sayfasının güncelleştirmesi veya eklemesi gereken bir özelliği içerdiğini varsayalım:

public class Student
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
    public string Secret { get; set; }
}

Uygulamanın, Secret Oluştur veya Güncelleştir sayfasında bir alanı olmasa bile Razor , bir korsan Secret değeri aşırı nakme ile ayarlayabilir. Bir korsan, Fiddler gibi bir araç kullanabilir veya bir form değeri göndermek için bazı JavaScript yazabilir Secret . Özgün kod, bir öğrenci örneği oluştururken model cildin kullandığı alanları sınırlamaz.

Form alanı için belirtilen korsanın hangi değeri Secret veritabanında güncelleştirildiği. Aşağıdaki görüntüde, Secret "OverPost" değeri ile alanı, postalanan form değerlerine ekleyerek Fiddler aracı gösterilmektedir.

Fiddler gizli alanı ekleniyor

"OverPost" değeri Secret eklenen satırın özelliğine başarıyla eklendi. Bu durum, Uygulama Tasarımcısı hiçbir Secret şekilde özelliği oluştur sayfasıyla ayarlamaya yönelik değildir.

Modeli görüntüleme

Model görüntüleme, fazla nakletmeyi önlemenin alternatif bir yolunu sağlar.

Uygulama modeli genellikle etki alanı modeli olarak adlandırılır. Etki alanı modeli genellikle veritabanında ilgili varlık için gereken tüm özellikleri içerir. Görünüm modeli yalnızca UI sayfası için gereken özellikleri içerir, örneğin, Oluştur sayfası.

Görünüm modeline ek olarak, bazı uygulamalar sayfa Razor modeli sınıfı ve tarayıcı arasında veri geçirmek için bir bağlama modeli veya giriş modeli kullanır.

Aşağıdaki görünüm modelini göz önünde bulundurun StudentVM :

public class StudentVM
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
}

Aşağıdaki kod, StudentVM Yeni bir öğrenci oluşturmak için görünüm modelini kullanır:

[BindProperty]
public StudentVM StudentVM { get; set; }

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    var entry = _context.Add(new Student());
    entry.CurrentValues.SetValues(StudentVM);
    await _context.SaveChangesAsync();
    return RedirectToPage("./Index");
}

SetValues yöntemi, başka bir PropertyValues nesnesinden değerleri okuyarak bu nesnenin değerlerini ayarlar. SetValues Özellik adı eşleştirme kullanır. Görünüm modeli türü:

  • Model türüyle ilgili olması gerekmez.
  • Eşleşen özelliklerin olması gerekir.

Kullanmak StudentVM yerine, oluşturma sayfası kullanımını gerektirir StudentVM Student :

@page
@model CreateVMModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Student</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="StudentVM.LastName" class="control-label"></label>
                <input asp-for="StudentVM.LastName" class="form-control" />
                <span asp-validation-for="StudentVM.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="StudentVM.FirstMidName" class="control-label"></label>
                <input asp-for="StudentVM.FirstMidName" class="form-control" />
                <span asp-validation-for="StudentVM.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="StudentVM.EnrollmentDate" class="control-label"></label>
                <input asp-for="StudentVM.EnrollmentDate" class="form-control" />
                <span asp-validation-for="StudentVM.EnrollmentDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Düzenleme sayfasını Güncelleştir

Sayfalar/öğrenciler/Edit. cshtml. cs' de, OnGetAsync ve OnPostAsync yöntemlerini aşağıdaki kodla değiştirin.

public async Task<IActionResult> OnGetAsync(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    Student = await _context.Students.FindAsync(id);

    if (Student == null)
    {
        return NotFound();
    }
    return Page();
}

public async Task<IActionResult> OnPostAsync(int id)
{
    var studentToUpdate = await _context.Students.FindAsync(id);

    if (studentToUpdate == null)
    {
        return NotFound();
    }

    if (await TryUpdateModelAsync<Student>(
        studentToUpdate,
        "student",
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

Kod değişiklikleri, birkaç özel durum dışında oluşturma sayfasına benzerdir:

  • FirstOrDefaultAsync , Findadsyncile değiştirilmiştir. İlgili verileri dahil etmeniz gerekmiyorsa, FindAsync daha etkilidir.
  • OnPostAsync bir id parametreye sahiptir.
  • Geçerli öğrenci boş bir öğrenci oluşturmak yerine veritabanından getirilir.

Uygulamayı çalıştırın ve bir öğrenci oluşturup düzenleyerek test edin.

Varlık durumları

Veritabanı bağlamı, bellekteki varlıkların veritabanında karşılık gelen satırlarıyla eşitlenmiş olup olmadığını izler. Bu izleme bilgileri, Savechangesasync çağrıldığında ne olacağını belirler. Örneğin, yeni bir varlık Addadsync yöntemine geçirildiğinde, bu varlığın durumu eklendiolarak ayarlanır. SaveChangesAsyncçağrıldığında, veritabanı bağlamı bir SQL INSERT komutu yayınlar.

Bir varlık aşağıdaki durumlardanbirinde olabilir:

  • Added: Varlık veritabanında henüz yok. SaveChangesYöntemi bir ifadesini yayınlar INSERT .

  • Unchanged: Bu varlıkla birlikte hiçbir değişiklik kaydedilmesi gerekmiyor. Bir varlık veritabanından okurken bu durumu içerir.

  • Modified: Varlığın özellik değerlerinin bazıları veya tümü değiştirildi. SaveChangesYöntemi bir ifadesini yayınlar UPDATE .

  • Deleted: Varlık silinmek üzere işaretlendi. SaveChangesYöntemi bir ifadesini yayınlar DELETE .

  • Detached: Varlık veritabanı bağlamı tarafından izlenmiyor.

Bir masaüstü uygulamasında durum değişiklikleri genellikle otomatik olarak ayarlanır. Bir varlık okundu, değişiklikler yapılır ve varlık durumu otomatik olarak olarak değiştirilir Modified . çağıran SaveChanges UPDATE yalnızca değiştirilen özellikleri güncelleştiren bir SQL ifade oluşturur.

Bir Web uygulamasında, bir DbContext varlığı okur ve bir sayfa işlendikten sonra verileri görüntüler. Bir sayfanın OnPostAsync yöntemi çağrıldığında, yeni bir Web isteği oluşturulur ve yeni bir örneğine sahiptir DbContext . Okuyarak bu yeni bağlamdaki varlığı, masaüstü işlemesini benzetir.

Silme sayfasını Güncelleştir

Bu bölümde, çağrısı başarısız olduğunda özel bir hata iletisi uygulanır SaveChanges .

Pages/öğrenciler/delete. cshtml. cs dosyasındaki kodu aşağıdaki kodla değiştirin:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Students
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;
        private readonly ILogger<DeleteModel> _logger;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context,
                           ILogger<DeleteModel> logger)
        {
            _context = context;
            _logger = logger;
        }

        [BindProperty]
        public Student Student { get; set; }
        public string ErrorMessage { get; set; }

        public async Task<IActionResult> OnGetAsync(int? id, bool? saveChangesError = false)
        {
            if (id == null)
            {
                return NotFound();
            }

            Student = await _context.Students
                .AsNoTracking()
                .FirstOrDefaultAsync(m => m.ID == id);

            if (Student == null)
            {
                return NotFound();
            }

            if (saveChangesError.GetValueOrDefault())
            {
                ErrorMessage = String.Format("Delete {ID} failed. Try again", id);
            }

            return Page();
        }

        public async Task<IActionResult> OnPostAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var student = await _context.Students.FindAsync(id);

            if (student == null)
            {
                return NotFound();
            }

            try
            {
                _context.Students.Remove(student);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            catch (DbUpdateException ex)
            {
                _logger.LogError(ex, ErrorMessage);

                return RedirectToAction("./Delete",
                                     new { id, saveChangesError = true });
            }
        }
    }
}

Yukarıdaki kod:

  • Günlük kaydıekler.
  • Ekler, isteğe bağlı parametresini saveChangesError OnGetAsync Yöntem imzasına ekler. saveChangesError öğrenci nesnesini silme hatasından sonra yöntemin çağrılıp çağrılmadığını gösterir.

Geçici ağ sorunları nedeniyle silme işlemi başarısız olabilir. Geçici ağ hataları, veritabanı bulutta olduğunda daha olasıdır. saveChangesErrorParametresi, false silme sayfası OnGetAsync kullanıcı arabiriminden çağrıldığında olur. ,, OnGetAsync OnPostAsync Silme işlemi başarısız olduğundan, parametresi olarak çağrılır saveChangesError true .

OnPostAsyncYöntemi seçili varlığı alır, ardından varlığın durumunu olarak ayarlamak Için Remove yöntemini çağırır Deleted . SaveChangesçağrıldığında, bir SQL DELETE komutu oluşturulur. RemoveBaşarısız olursa:

  • Veritabanı özel durumu yakalandı.
  • Sayfaları Sil OnGetAsync yöntemi ile çağırılır saveChangesError=true .

Sayfalara/öğrencilerine/delete. cshtml öğesine bir hata iletisi ekleyin:

@page
@model ContosoUniversity.Pages.Students.DeleteModel

@{
    ViewData["Title"] = "Delete";
}

<h1>Delete</h1>

<p class="text-danger">@Model.ErrorMessage</p>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.LastName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.LastName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.FirstMidName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.FirstMidName)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Student.EnrollmentDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Student.EnrollmentDate)
        </dd>
    </dl>

    <form method="post">
        <input type="hidden" asp-for="Student.ID" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Uygulamayı çalıştırın ve Sil sayfasını test etmek için bir öğrenci silin.

Sonraki adımlar