Bölüm 2, Razor ASP.NET Core'da bulunan EF Core sayfalar - CRUD
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Tom Dykstra, Jeremy Likness ve Jon P Smith tarafından
Contoso University web uygulaması, ve Visual Studio kullanarak EF Core Sayfalar web uygulamalarının nasıl oluşturulacağını Razor gösterir. Öğretici serisi hakkında bilgi için ilk öğreticiye bakın.
Çözemediğiniz sorunlarla karşılaşırsanız, tamamlanmış uygulamayı indirin ve öğreticiyi izleyerek bu kodu oluşturduğunuz kodla karşılaştırın.
Bu öğreticide, iskelesi oluşturulmuş CRUD (oluşturma, okuma, güncelleştirme, silme) kodu gözden geçirilir ve özelleştirilir.
Depo yok
Bazı geliştiriciler, kullanıcı arabirimi (Razor Sayfalar) ile veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için bir hizmet katmanı veya depo deseni kullanır. Bu öğretici bunu yapmaz. Karmaşıklığı en aza indirmek ve öğreticinin üzerinde EF CoreEF Core odaklanmasını sağlamak için kod doğrudan sayfa modeli sınıflarına eklenir.
Ayrıntılar sayfasını güncelleştirme
Öğrenciler sayfaları için yapı iskelesi oluşturulmuş kod kayıt verilerini içermez. Bu bölümde, kayıtlar sayfaya Details
eklenir.
Kayıtları okuma
Öğrencinin kayıt verilerini sayfada görüntülemek için kayıt verilerinin okunması gerekir. içindeki iskelelenmiş kodPages/Students/Details.cshtml.cs
, veriler olmadan Enrollment
yalnızca Student
verileri 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();
}
OnGetAsync
Seçilen öğ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 yüklemesine Student.Enrollments
ve her kayıtta gezinti özelliğinin Enrollment.Course
içine neden olur. Bu yöntemler, İlgili verileri okuma öğreticisinde ayrıntılı olarak incelenir.
yöntemi, AsNoTracking döndürülen varlıkların geçerli bağlamda güncelleştirilmediği senaryolarda performansı artırır. AsNoTracking
bu öğreticinin ilerleyen bölümlerinde ele alınmalıdır.
Kayıtları görüntüleme
Kayıtların listesini görüntülemek için içindeki Pages/Students/Details.cshtml
kodu 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ğindeki Enrollments
varlıklar arasında döngü oluşturur. Her kayıt için kurs başlığını ve notu görüntüler. Kurs başlığı, Kayıtlar varlığının Course
gezinti özelliğinde Course
depolanan varlıktan 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 öğrencinin ders ve not listesi görüntülenir.
Bir varlığı okumanın yolları
Oluşturulan kod, bir varlığı okumak için FirstOrDefaultAsync kullanır. Hiçbir şey bulunmazsa bu yöntem null döndürür; aksi takdirde, sorgu filtresi ölçütlerini karşılayan ilk satırı döndürür. FirstOrDefaultAsync
genellikle aşağıdaki alternatiflerden 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ülebilir olup olmadığını belirlemek için birden
SingleOrDefaultAsync
çok satır getirmeye çalışır. Sorgu benzersiz bir anahtarda arama yaparken olduğu gibi yalnızca bir varlık döndürebiliyorsa bu ek iş gereksizdir. - FindAsync - Birincil anahtara (PK) sahip bir varlık bulur. PK içeren bir varlık bağlam tarafından izleniyorsa, veritabanına bir istekte bulunmaksızın döndürülür. Bu yöntem, tek bir varlığı aramak için iyileştirilmiştir, ancak ile
FindAsync
çağrıInclude
yapamazsınız. Bu nedenle, ilgili verilere ihtiyaç duyulduğuFirstOrDefaultAsync
takdirde daha iyi bir seçimdir.
Verileri yönlendirme ve sorgu dizesi karşılaştırması
Ayrıntılar sayfasının URL'si şeklindedir https://localhost:<port>/Students/Details?id=1
. Varlığın birincil anahtar değeri sorgu dizesindedir. Bazı geliştiriciler yol verilerinde anahtar değerini geçirmeyi 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
Oluştur sayfasının yapı iskelesi oluşturulmuş OnPostAsync
kodu, fazla paylaşıma karşı savunmasızdır. OnPostAsync
içindeki Pages/Students/Create.cshtml.cs
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:
- içindeki özelliğinden PageContext PageModelgönderilen form değerlerini kullanır.
- Yalnızca listelenen
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate
() özellikleri güncelleştirir. - "Öğrenci" ön eki olan form alanlarını arar. Örneğin,
Student.FirstMidName
. Büyük/küçük harfe duyarlı değil. - Form değerlerini dizelerden modeldeki
Student
türlere dönüştürmek için model bağlama sistemini kullanır. Örneğin,EnrollmentDate
öğesineDateTime
dönüştürülür.
Uygulamayı çalıştırın ve Oluştur sayfasını test etmek için bir öğrenci varlığı oluşturun.
Üst Paylaşım
Alanları TryUpdateModel
deftere nakledilmiş değerlerle güncelleştirmek için kullanmak, fazla paylaşım yapılmasını önlediği için en iyi güvenlik uygulamasıdır. Örneğin, Öğrenci varlığının bu web sayfasının güncelleştirmemesi veya eklememesi gereken bir Secret
özellik 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 oluşturma veya güncelleştirme Sayfasında bir Secret
alanı olmasa bile, bir bilgisayar korsanı üst paylaşım yaparak değeri ayarlayabilirSecret
.Razor Bir bilgisayar korsanı, form değeri göndermek için Fiddler gibi bir Secret
araç kullanabilir veya javascript yazabilir. Özgün kod, model bağlayıcısının student örneği oluştururken kullandığı alanları sınırlamaz.
Hacker'ın form alanı için Secret
belirttiği değer veritabanında güncelleştirilir. Aşağıdaki görüntüde, "OverPost" değeriyle alanı gönderilen form değerlerine ekleyen Secret
Fiddler aracı gösterilmektedir.
"OverPost" değeri eklenen satırın Secret
özelliğine başarıyla eklenir. Uygulama tasarımcısı özelliğin Oluştur sayfasıyla ayarlanmasını Secret
hiçbir zaman amaçlamasa bile bu durum ortaya çıkar.
Modeli görüntüleme
Görünüm modelleri, fazla göndermeyi önlemek için alternatif bir yol sağlar.
Uygulama modeli genellikle etki alanı modeli olarak adlandırılır. Etki alanı modeli genellikle veritabanında karşılık gelen varlık için gereken tüm özellikleri içerir. Görünüm modeli yalnızca kullanıcı arabirimi sayfası için gereken özellikleri (örneğin Oluştur sayfası) içerir.
Görünüm modeline ek olarak, bazı uygulamalar Sayfalar sayfa modeli sınıfı ile tarayıcı arasında Razor veri geçirmek için bağlama modeli veya giriş modeli kullanır.
Aşağıdaki StudentVM
görünüm modelini göz önünde bulundurun:
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, yeni bir öğrenci oluşturmak için görünüm modelini kullanır StudentVM
:
[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 nesneden değerleri okuyarak bu nesnenin değerlerini ayarlar. SetValues
özellik adı eşleştirmeyi kullanır. Görünüm modeli türü:
- Model türüyle ilgili olması gerekmez.
- Eşleşen özelliklere sahip olması gerekir.
kullanmak StudentVM
için yerine Sayfa oluşturma kullanımı StudentVM
Student
gerekir:
@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üzenle sayfasını güncelleştirme
içinde Pages/Students/Edit.cshtml.cs
ve OnPostAsync
yöntemlerini aşağıdaki kodla değiştirinOnGetAsync
.
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 oluştur sayfasına benzer ve birkaç özel durum vardır:
FirstOrDefaultAsync
ile FindAsyncdeğiştirildi. İlgili verileri dahil etmek zorunda olmadığınızda dahaFindAsync
verimli olur.OnPostAsync
parametresineid
sahiptir.- Geçerli öğrenci boş bir öğrenci oluşturmak yerine veritabanından getirilir.
Uygulamayı çalıştırın ve öğrenci oluşturup düzenleyerek test edin.
Varlık Durumları
Veritabanı bağlamı, bellekteki varlıkların veritabanındaki karşılık gelen satırlarla eşitlenip eşitlenmediğini izler. Bu izleme bilgileri SaveChangesAsync çağrıldığında ne olacağını belirler. Örneğin, yöntemine AddAsync yeni bir varlık geçirildiğinde, bu varlığın durumu olarak Addedayarlanır. Çağrıldığında SaveChangesAsync
, veritabanı bağlamı bir SQL INSERT
komutu döndürür.
Bir varlık aşağıdaki durumlardan birinde olabilir:
Added
: Varlık henüz veritabanında yok.SaveChanges
yöntemi birINSERT
deyimini oluşturur.Unchanged
: Bu varlıkla hiçbir değişikliğin kaydedilmesi gerekmez. Varlık, veritabanından okunduğunda bu duruma sahiptir.Modified
: Varlığın özellik değerlerinin bazıları veya tümü değiştirildi.SaveChanges
yöntemi birUPDATE
deyimini oluşturur.Deleted
: Varlık silinmek üzere işaretlendi.SaveChanges
yöntemi birDELETE
deyimini oluşturur.Detached
: Varlık veritabanı bağlamı tarafından izlenmiyor.
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 değiştirilen özellikleri güncelleştiren bir SQL UPDATE
deyimi oluşturur.
Bir web uygulamasında, DbContext
bir varlığı okuyan ve verileri görüntüleyen, bir sayfa işlendikten sonra atılır. Bir sayfanın OnPostAsync
yöntemi çağrıldığında, yeni bir web isteği yapılır ve yeni bir örneğiyle DbContext
. Bu yeni bağlamda varlığın yeniden okunarak masaüstü işleme benzetimi gerçekleştirilir.
Sil sayfasını güncelleştirme
Bu bölümde, çağrısı SaveChanges
başarısız olduğunda özel bir hata iletisi uygulanır.
Pages/Students/Delete.cshtml.cs
kodunu şu 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.
- İsteğe bağlı parametresini
saveChangesError
yöntem imzasınaOnGetAsync
ekler.saveChangesError
, öğrenci nesnesini silme hatasından sonra yönteminin çağrılıp çağrılmadığı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. saveChangesError
Parametresi, false
Kullanıcı arabiriminden Delete sayfası OnGetAsync
çağrıldığında yapılır. Silme işlemi başarısız olduğundan tarafından çağrıldığında OnGetAsync
parametresidir saveChangesError
true
.OnPostAsync
OnPostAsync
yöntemi seçili varlığı alır, ardından varlığın durumunu Deleted
olarak ayarlamak için Remove yöntemini çağırır. Çağrıldığında SaveChanges
bir SQL DELETE
komutu oluşturulur. Başarısız olursa Remove
:
- Veritabanı özel durumu yakalanmış.
- Delete pages
OnGetAsync
yöntemi ilesaveChangesError=true
çağrılır.
içine Pages/Students/Delete.cshtml
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>
Sil sayfasını test etmek için uygulamayı çalıştırın ve bir öğrenciyi silin.
Sonraki adımlar
Bu öğreticide, iskelesi oluşturulmuş CRUD (oluşturma, okuma, güncelleştirme, silme) kodu gözden geçirilir ve özelleştirilir.
Depo yok
Bazı geliştiriciler, kullanıcı arabirimi (Razor Sayfalar) ile veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için bir hizmet katmanı veya depo deseni kullanır. Bu öğretici bunu yapmaz. Karmaşıklığı en aza indirmek ve öğreticinin üzerinde EF CoreEF Core odaklanmasını sağlamak için kod doğrudan sayfa modeli sınıflarına eklenir.
Ayrıntılar sayfasını güncelleştirme
Öğrenciler sayfaları için yapı iskelesi oluşturulmuş kod kayıt verilerini içermez. Bu bölümde, kayıtlar sayfaya Details
eklenir.
Kayıtları okuma
Öğrencinin kayıt verilerini sayfada görüntülemek için kayıt verilerinin okunması gerekir. içindeki iskelelenmiş kodPages/Students/Details.cshtml.cs
, veriler olmadan Enrollment
yalnızca Student
verileri 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();
}
OnGetAsync
Seçilen öğ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 yüklemesine Student.Enrollments
ve her kayıtta gezinti özelliğinin Enrollment.Course
içine neden olur. Bu yöntemler, İlgili verileri okuma öğreticisinde ayrıntılı olarak incelenir.
yöntemi, AsNoTracking döndürülen varlıkların geçerli bağlamda güncelleştirilmediği senaryolarda performansı artırır. AsNoTracking
bu öğreticinin ilerleyen bölümlerinde ele alınmalıdır.
Kayıtları görüntüleme
Kayıtların listesini görüntülemek için içindeki Pages/Students/Details.cshtml
kodu 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ğindeki Enrollments
varlıklar arasında döngü oluşturur. Her kayıt için kurs başlığını ve notu görüntüler. Kurs başlığı, Kayıtlar varlığının Course
gezinti özelliğinde Course
depolanan varlıktan 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 öğrencinin ders ve not listesi görüntülenir.
Bir varlığı okumanın yolları
Oluşturulan kod, bir varlığı okumak için FirstOrDefaultAsync kullanır. Hiçbir şey bulunmazsa bu yöntem null döndürür; aksi takdirde, sorgu filtresi ölçütlerini karşılayan ilk satırı döndürür. FirstOrDefaultAsync
genellikle aşağıdaki alternatiflerden 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ülebilir olup olmadığını belirlemek için birden
SingleOrDefaultAsync
çok satır getirmeye çalışır. Sorgu benzersiz bir anahtarda arama yaparken olduğu gibi yalnızca bir varlık döndürebiliyorsa bu ek iş gereksizdir. - FindAsync - Birincil anahtara (PK) sahip bir varlık bulur. PK içeren bir varlık bağlam tarafından izleniyorsa, veritabanına bir istekte bulunmaksızın döndürülür. Bu yöntem, tek bir varlığı aramak için iyileştirilmiştir, ancak ile
FindAsync
çağrıInclude
yapamazsınız. Bu nedenle, ilgili verilere ihtiyaç duyulduğuFirstOrDefaultAsync
takdirde daha iyi bir seçimdir.
Verileri yönlendirme ve sorgu dizesi karşılaştırması
Ayrıntılar sayfasının URL'si şeklindedir https://localhost:<port>/Students/Details?id=1
. Varlığın birincil anahtar değeri sorgu dizesindedir. Bazı geliştiriciler yol verilerinde anahtar değerini geçirmeyi 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
Oluştur sayfasının yapı iskelesi oluşturulmuş OnPostAsync
kodu, fazla paylaşıma karşı savunmasızdır. OnPostAsync
içindeki Pages/Students/Create.cshtml.cs
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:
- içindeki özelliğinden PageContext PageModelgönderilen form değerlerini kullanır.
- Yalnızca listelenen
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate
() özellikleri güncelleştirir. - "Öğrenci" ön eki olan form alanlarını arar. Örneğin,
Student.FirstMidName
. Büyük/küçük harfe duyarlı değil. - Form değerlerini dizelerden modeldeki
Student
türlere dönüştürmek için model bağlama sistemini kullanır. Örneğin,EnrollmentDate
öğesineDateTime
dönüştürülür.
Uygulamayı çalıştırın ve Oluştur sayfasını test etmek için bir öğrenci varlığı oluşturun.
Üst Paylaşım
Alanları TryUpdateModel
deftere nakledilmiş değerlerle güncelleştirmek için kullanmak, fazla paylaşım yapılmasını önlediği için en iyi güvenlik uygulamasıdır. Örneğin, Öğrenci varlığının bu web sayfasının güncelleştirmemesi veya eklememesi gereken bir Secret
özellik 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 oluşturma veya güncelleştirme Sayfasında bir Secret
alanı olmasa bile, bir bilgisayar korsanı üst paylaşım yaparak değeri ayarlayabilirSecret
.Razor Bir bilgisayar korsanı, form değeri göndermek için Fiddler gibi bir Secret
araç kullanabilir veya javascript yazabilir. Özgün kod, model bağlayıcısının student örneği oluştururken kullandığı alanları sınırlamaz.
Hacker'ın form alanı için Secret
belirttiği değer veritabanında güncelleştirilir. Aşağıdaki görüntüde, "OverPost" değeriyle alanı gönderilen form değerlerine ekleyen Secret
Fiddler aracı gösterilmektedir.
"OverPost" değeri eklenen satırın Secret
özelliğine başarıyla eklenir. Uygulama tasarımcısı özelliğin Oluştur sayfasıyla ayarlanmasını Secret
hiçbir zaman amaçlamasa bile bu durum ortaya çıkar.
Modeli görüntüleme
Görünüm modelleri, fazla göndermeyi önlemek için alternatif bir yol sağlar.
Uygulama modeli genellikle etki alanı modeli olarak adlandırılır. Etki alanı modeli genellikle veritabanında karşılık gelen varlık için gereken tüm özellikleri içerir. Görünüm modeli yalnızca kullanıcı arabirimi sayfası için gereken özellikleri (örneğin Oluştur sayfası) içerir.
Görünüm modeline ek olarak, bazı uygulamalar Sayfalar sayfa modeli sınıfı ile tarayıcı arasında Razor veri geçirmek için bağlama modeli veya giriş modeli kullanır.
Aşağıdaki StudentVM
görünüm modelini göz önünde bulundurun:
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, yeni bir öğrenci oluşturmak için görünüm modelini kullanır StudentVM
:
[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 nesneden değerleri okuyarak bu nesnenin değerlerini ayarlar. SetValues
özellik adı eşleştirmeyi kullanır. Görünüm modeli türü:
- Model türüyle ilgili olması gerekmez.
- Eşleşen özelliklere sahip olması gerekir.
kullanmak StudentVM
için yerine Sayfa oluşturma kullanımı StudentVM
Student
gerekir:
@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üzenle sayfasını güncelleştirme
içinde Pages/Students/Edit.cshtml.cs
ve OnPostAsync
yöntemlerini aşağıdaki kodla değiştirinOnGetAsync
.
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 oluştur sayfasına benzer ve birkaç özel durum vardır:
FirstOrDefaultAsync
ile FindAsyncdeğiştirildi. İlgili verileri dahil etmek zorunda olmadığınızda dahaFindAsync
verimli olur.OnPostAsync
parametresineid
sahiptir.- Geçerli öğrenci boş bir öğrenci oluşturmak yerine veritabanından getirilir.
Uygulamayı çalıştırın ve öğrenci oluşturup düzenleyerek test edin.
Varlık Durumları
Veritabanı bağlamı, bellekteki varlıkların veritabanındaki karşılık gelen satırlarla eşitlenip eşitlenmediğini izler. Bu izleme bilgileri SaveChangesAsync çağrıldığında ne olacağını belirler. Örneğin, yöntemine AddAsync yeni bir varlık geçirildiğinde, bu varlığın durumu olarak Addedayarlanır. Çağrıldığında SaveChangesAsync
, veritabanı bağlamı bir SQL INSERT
komutu döndürür.
Bir varlık aşağıdaki durumlardan birinde olabilir:
Added
: Varlık henüz veritabanında yok.SaveChanges
yöntemi birINSERT
deyimini oluşturur.Unchanged
: Bu varlıkla hiçbir değişikliğin kaydedilmesi gerekmez. Varlık, veritabanından okunduğunda bu duruma sahiptir.Modified
: Varlığın özellik değerlerinin bazıları veya tümü değiştirildi.SaveChanges
yöntemi birUPDATE
deyimini oluşturur.Deleted
: Varlık silinmek üzere işaretlendi.SaveChanges
yöntemi birDELETE
deyimini oluşturur.Detached
: Varlık veritabanı bağlamı tarafından izlenmiyor.
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 değiştirilen özellikleri güncelleştiren bir SQL UPDATE
deyimi oluşturur.
Bir web uygulamasında, DbContext
bir varlığı okuyan ve verileri görüntüleyen, bir sayfa işlendikten sonra atılır. Bir sayfanın OnPostAsync
yöntemi çağrıldığında, yeni bir web isteği yapılır ve yeni bir örneğiyle DbContext
. Bu yeni bağlamda varlığın yeniden okunarak masaüstü işleme benzetimi gerçekleştirilir.
Sil sayfasını güncelleştirme
Bu bölümde, çağrısı SaveChanges
başarısız olduğunda özel bir hata iletisi uygulanır.
Pages/Students/Delete.cshtml.cs
kodunu şu 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.
- İsteğe bağlı parametresini
saveChangesError
yöntem imzasınaOnGetAsync
ekler.saveChangesError
, öğrenci nesnesini silme hatasından sonra yönteminin çağrılıp çağrılmadığı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. saveChangesError
Parametresi, false
Kullanıcı arabiriminden Delete sayfası OnGetAsync
çağrıldığında yapılır. Silme işlemi başarısız olduğundan tarafından çağrıldığında OnGetAsync
parametresidir saveChangesError
true
.OnPostAsync
OnPostAsync
yöntemi seçili varlığı alır, ardından varlığın durumunu Deleted
olarak ayarlamak için Remove yöntemini çağırır. Çağrıldığında SaveChanges
bir SQL DELETE
komutu oluşturulur. Başarısız olursa Remove
:
- Veritabanı özel durumu yakalanmış.
- Delete pages
OnGetAsync
yöntemi ilesaveChangesError=true
çağrılır.
içine Pages/Students/Delete.cshtml
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>
Sil sayfasını test etmek için uygulamayı çalıştırın ve bir öğrenciyi silin.
Sonraki adımlar
Bu öğreticide, iskelesi oluşturulmuş CRUD (oluşturma, okuma, güncelleştirme, silme) kodu gözden geçirilir ve özelleştirilir.
Depo yok
Bazı geliştiriciler, kullanıcı arabirimi (Razor Sayfalar) ile veri erişim katmanı arasında bir soyutlama katmanı oluşturmak için bir hizmet katmanı veya depo deseni kullanır. Bu öğretici bunu yapmaz. Karmaşıklığı en aza indirmek ve öğreticinin üzerinde EF CoreEF Core odaklanmasını sağlamak için kod doğrudan sayfa modeli sınıflarına eklenir.
Ayrıntılar sayfasını güncelleştirme
Öğrenciler sayfaları için yapı iskelesi oluşturulmuş kod kayıt verilerini içermez. Bu bölümde kayıtlar Ayrıntılar sayfasına eklenir.
Kayıtları okuma
Öğrencinin kayıt verilerini sayfada görüntülemek için kayıt verilerinin okunması gerekir. içindeki yapı iskelesi oluşturulmuş kod Pages/Students/Details.cshtml.cs
, 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();
}
OnGetAsync
Seçilen öğ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 yüklemesine Student.Enrollments
ve her kayıtta gezinti özelliğinin Enrollment.Course
içine neden olur. Bu yöntemler, İlgili verileri okuma öğreticisinde ayrıntılı olarak incelenir.
yöntemi, AsNoTracking döndürülen varlıkların geçerli bağlamda güncelleştirilmediği senaryolarda performansı artırır. AsNoTracking
bu öğreticinin ilerleyen bölümlerinde ele alınmalıdır.
Kayıtları görüntüleme
Kayıtların listesini görüntülemek için içindeki Pages/Students/Details.cshtml
kodu 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ğindeki Enrollments
varlıklar arasında döngü oluşturur. Her kayıt için kurs başlığını ve notu görüntüler. Kurs başlığı, Kayıtlar varlığının gezinti özelliğinde Course
depolanan 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 öğrencinin ders ve not listesi görüntülenir.
Bir varlığı okumanın yolları
Oluşturulan kod, bir varlığı okumak için FirstOrDefaultAsync kullanır. Hiçbir şey bulunmazsa bu yöntem null döndürür; aksi takdirde, sorgu filtresi ölçütlerini karşılayan ilk satırı döndürür. FirstOrDefaultAsync
genellikle aşağıdaki alternatiflerden 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ülebilir olup olmadığını belirlemek için birden
SingleOrDefaultAsync
çok satır getirmeye çalışır. Sorgu benzersiz bir anahtarda arama yaparken olduğu gibi yalnızca bir varlık döndürebiliyorsa bu ek iş gereksizdir. - FindAsync - Birincil anahtara (PK) sahip bir varlık bulur. PK içeren bir varlık bağlam tarafından izleniyorsa, veritabanına bir istekte bulunmaksızın döndürülür. Bu yöntem, tek bir varlığı aramak için iyileştirilmiştir, ancak ile
FindAsync
çağrıInclude
yapamazsınız. Bu nedenle, ilgili verilere ihtiyaç duyulduğuFirstOrDefaultAsync
takdirde daha iyi bir seçimdir.
Verileri yönlendirme ve sorgu dizesi karşılaştırması
Ayrıntılar sayfasının URL'si şeklindedir https://localhost:<port>/Students/Details?id=1
. Varlığın birincil anahtar değeri sorgu dizesindedir. Bazı geliştiriciler yol verilerinde anahtar değerini geçirmeyi 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
Oluştur sayfasının yapı iskelesi oluşturulmuş OnPostAsync
kodu, fazla paylaşıma karşı savunmasızdır. OnPostAsync
içindeki Pages/Students/Create.cshtml.cs
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:
- içindeki özelliğinden PageContext PageModelgönderilen form değerlerini kullanır.
- Yalnızca listelenen
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate
() özellikleri güncelleştirir. - "Öğrenci" ön eki olan form alanlarını arar. Örneğin,
Student.FirstMidName
. Büyük/küçük harfe duyarlı değil. - Form değerlerini dizelerden modeldeki
Student
türlere dönüştürmek için model bağlama sistemini kullanır. Örneğin,EnrollmentDate
DateTime'a dönüştürülmesi gerekir.
Uygulamayı çalıştırın ve Oluştur sayfasını test etmek için bir öğrenci varlığı oluşturun.
Üst Paylaşım
Alanları TryUpdateModel
deftere nakledilmiş değerlerle güncelleştirmek için kullanmak, fazla paylaşım yapılmasını önlediği için en iyi güvenlik uygulamasıdır. Örneğin, Öğrenci varlığının bu web sayfasının güncelleştirmemesi veya eklememesi gereken bir Secret
özellik 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 oluşturma veya güncelleştirme Sayfasında bir Secret
alanı olmasa bile, bir bilgisayar korsanı üst paylaşım yaparak değeri ayarlayabilirSecret
.Razor Bir bilgisayar korsanı, form değeri göndermek için Fiddler gibi bir Secret
araç kullanabilir veya javascript yazabilir. Özgün kod, model bağlayıcısının student örneği oluştururken kullandığı alanları sınırlamaz.
Hacker'ın form alanı için Secret
belirttiği değer veritabanında güncelleştirilir. Aşağıdaki görüntüde, fiddler aracının gönderilen form değerlerine Secret
alanı ("OverPost" değeriyle) eklemesi gösterilmektedir.
"OverPost" değeri eklenen satırın Secret
özelliğine başarıyla eklenir. Uygulama tasarımcısı özelliğin Oluştur sayfasıyla ayarlanmasını Secret
hiçbir zaman amaçlamasa bile bu durum ortaya çıkar.
Modeli görüntüleme
Görünüm modelleri, fazla göndermeyi önlemek için alternatif bir yol sağlar.
Uygulama modeli genellikle etki alanı modeli olarak adlandırılır. Etki alanı modeli genellikle veritabanında karşılık gelen varlık için gereken tüm özellikleri içerir. Görünüm modeli yalnızca kullanıcı arabirimi için gerekli olan özellikleri içerir (örneğin, Oluştur sayfası).
Görünüm modeline ek olarak, bazı uygulamalar Sayfalar sayfa modeli sınıfı ile tarayıcı arasında Razor veri geçirmek için bağlama modeli veya giriş modeli kullanır.
Aşağıdaki Student
görünüm modelini göz önünde bulundurun:
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, yeni bir öğrenci oluşturmak için görünüm modelini kullanır StudentVM
:
[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 nesneden değerleri okuyarak bu nesnenin değerlerini ayarlar. SetValues
özellik adı eşleştirmeyi kullanır. Görünüm modeli türünün model türüyle ilişkili olması gerekmez, yalnızca eşleşen özelliklere sahip olması gerekir.
kullanmak StudentVM
için Create.cshtml dosyasının yerine Student
kullanılacak StudentVM
şekilde güncelleştirilmiş olması gerekir.
Düzenle sayfasını güncelleştirme
içinde Pages/Students/Edit.cshtml.cs
ve OnPostAsync
yöntemlerini aşağıdaki kodla değiştirinOnGetAsync
.
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 oluştur sayfasına benzer ve birkaç özel durum vardır:
FirstOrDefaultAsync
ile FindAsyncdeğiştirildi. dahil edildiğinde ilgili veriler gerekliFindAsync
olmadığında daha verimli olur.OnPostAsync
parametresineid
sahiptir.- Geçerli öğrenci boş bir öğrenci oluşturmak yerine veritabanından getirilir.
Uygulamayı çalıştırın ve öğrenci oluşturup düzenleyerek test edin.
Varlık Durumları
Veritabanı bağlamı, bellekteki varlıkların veritabanındaki karşılık gelen satırlarla eşitlenip eşitlenmediğini izler. Bu izleme bilgileri SaveChangesAsync çağrıldığında ne olacağını belirler. Örneğin, yöntemine AddAsync yeni bir varlık geçirildiğinde, bu varlığın durumu olarak Addedayarlanır. Çağrıldığında SaveChangesAsync
, veritabanı bağlamı bir SQL INSERT komutu döndürür.
Bir varlık aşağıdaki durumlardan birinde olabilir:
Added
: Varlık henüz veritabanında yok.SaveChanges
yöntemi bir INSERT deyimi oluşturur.Unchanged
: Bu varlıkla hiçbir değişikliğin kaydedilmesi gerekmez. Varlık, veritabanından okunduğunda bu duruma sahiptir.Modified
: Varlığın özellik değerlerinin bazıları veya tümü değiştirildi.SaveChanges
yöntemi bir UPDATE deyimi oluşturur.Deleted
: Varlık silinmek üzere işaretlendi.SaveChanges
yöntemi bir DELETE deyimi oluşturur.Detached
: Varlık veritabanı bağlamı tarafından izlenmiyor.
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 değiştirilen özellikleri güncelleştiren bir SQL UPDATE deyimi oluşturur.
Bir web uygulamasında, DbContext
bir varlığı okuyan ve verileri görüntüleyen, bir sayfa işlendikten sonra atılır. Bir sayfanın OnPostAsync
yöntemi çağrıldığında, yeni bir web isteği yapılır ve yeni bir örneğiyle DbContext
. Bu yeni bağlamda varlığın yeniden okunarak masaüstü işleme benzetimi gerçekleştirilir.
Sil sayfasını güncelleştirme
Bu bölümde, çağrısı SaveChanges
başarısız olduğunda özel bir hata iletisi uygulayacaksınız.
Pages/Students/Delete.cshtml.cs
içindeki kodu aşağıdaki kodla değiştirin. Değişiklikler vurgulanır (deyimlerin using
temizlenmesi dışında).
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
yöntem imzasına OnGetAsync
ekler. saveChangesError
, öğrenci nesnesini silme hatasından sonra yönteminin çağrılıp çağrılmadığı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. saveChangesError
Delete sayfası OnGetAsync
kullanıcı arabiriminden çağrıldığında parametresi false değeridir. tarafından OnPostAsync
çağrıldığında OnGetAsync
(silme işlemi başarısız olduğundan), saveChangesError
parametresi true değeridir.
OnPostAsync
yöntemi seçili varlığı alır, ardından varlığın durumunu Deleted
olarak ayarlamak için Remove yöntemini çağırır. Çağrıldığında SaveChanges
bir SQL DELETE komutu oluşturulur. Başarısız olursa Remove
:
- Veritabanı özel durumu yakalanmış.
- Delete sayfasının
OnGetAsync
yöntemi ilesaveChangesError=true
çağrılır.
Sayfayı Sil'e Razor (Pages/Students/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>
Sil sayfasını test etmek için uygulamayı çalıştırın ve bir öğrenciyi silin.
Sonraki adımlar
ASP.NET Core