Eğitim: Gelişmiş Senaryoları Öğrenin - ASP.NET MVC ile EF Core
Önceki derste hiyerarşi başına tablo kalıtımını uyguladınız. Bu öğreticide, Entity Framework Core kullanan ASP.NET Core web uygulamaları geliştirmenin temellerini aştığınızda dikkat etmeniz gereken birkaç konu açıklanmaktadır.
Bu öğreticide şunları yapacaksınız:
- Ham SQL sorguları gerçekleştirme
- Varlıkları döndürmek için sorgu çağırma
- Diğer türleri döndürmek için bir sorgu çağırın
- Güncelleştirme sorgusu çağırma
- SQL sorgularını inceleme
- Soyutlama katmanı oluşturma
- Otomatik değişiklik algılama hakkında bilgi edinin
- EF Core kaynak kodu ve geliştirme planları hakkında bilgi edinin
- Kodu basitleştirmek için dinamik LINQ kullanmayı öğrenin
Önkoşullar
Ham SQL sorguları gerçekleştirme
Entity Framework kullanmanın avantajlarından biri, kodunuzu belirli bir veri depolama yöntemine çok yakın bağlamaktan kaçınmasıdır. Bunu, sizin için SQL sorguları ve komutları oluşturarak yapar ve bu da bunları kendiniz yazmak zorunda kalmadan sizi serbesttir. Ancak el ile oluşturduğunuz belirli SQL sorgularını çalıştırmanız gerektiğinde olağanüstü senaryolar vardır. Bu senaryolar için Entity Framework Code First API'sinde SQL komutlarını doğrudan veritabanına geçirmenizi sağlayan yöntemler bulunur. EF Core 1.0'da aşağıdaki seçeneklere sahipsiniz:
Varlık türlerini döndüren sorgular için
DbSet.FromSql
yöntemini kullanın. Döndürülen nesneler,DbSet
nesnesi tarafından beklenen türde olmalıdır ve izleme özelliğini kapatmadığınız sürece veritabanı bağlamı tarafından otomatik olarak izlenir.Sorgu olmayan komutlar için
Database.ExecuteSqlCommand
kullanın.
Varlık olmayan türler döndüren bir sorgu çalıştırmanız gerekiyorsa, EF tarafından sağlanan veritabanı bağlantısıyla ADO.NET kullanabilirsiniz. Varlık türlerini almak için bu yöntemi kullansanız bile, döndürülen veriler veritabanı bağlamı tarafından izlenmez.
Bir web uygulamasında SQL komutlarını yürütürken her zaman olduğu gibi, sitenizi SQL ekleme saldırılarına karşı korumak için önlemler almanız gerekir. Bunun bir yolu, bir web sayfası tarafından gönderilen dizelerin SQL komutları olarak yorumlanamaz olduğundan emin olmak için parametreli sorgular kullanmaktır. Bu öğreticide, kullanıcı girişini sorguyla tümleştirirken parametreli sorgular kullanacaksınız.
Varlıkları döndürmek için sorgu çağırma
DbSet<TEntity>
sınıfı, TEntity
türünde bir varlık döndüren bir sorguyu yürütmek için kullanabileceğiniz bir yöntem sağlar. Bunun nasıl çalıştığını görmek için Bölüm denetleyicisinin Details
yöntemindeki kodu değiştireceksiniz.
DepartmentsController.cs
içinde, Details
yönteminde, aşağıdaki vurgulanan kodda gösterildiği gibi departmanı getiren kodu bir FromSql
yöntemi çağrısıyla değiştirin.
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
var department = await _context.Departments
.FromSql(query, id)
.Include(d => d.Administrator)
.AsNoTracking()
.FirstOrDefaultAsync();
if (department == null)
{
return NotFound();
}
return View(department);
}
Yeni kodun düzgün çalıştığını doğrulamak için Departmanlar sekmesini seçin ve ardından departmanlardan biri için Ayrıntılar .
Diğer türleri döndürmek için sorgu çağırma
Daha önce, Hakkında sayfası için her kayıt tarihi için öğrenci sayısını gösteren bir öğrenci istatistikleri kılavuzu oluşturdunuz. Öğrenciler varlık kümesinden (_context.Students
) verileri aldınız ve sonuçları EnrollmentDateGroup
görünüm modeli nesnelerinin listesine yansıtmak için LINQ kullandınız. LINQ kullanmak yerine SQL'in kendisini yazmak istediğinizi varsayalım. Bunu yapmak için varlık nesneleri dışında bir şey döndüren bir SQL sorgusu çalıştırmanız gerekir.
EF Core 1.0'da bunu yapmanızın bir yolu ADO.NET kodu yazmak ve EF'den veritabanı bağlantısını almaktır.
HomeController.cs
içinde About
yöntemini aşağıdaki kodla değiştirin:
public async Task<ActionResult> About()
{
List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
var conn = _context.Database.GetDbConnection();
try
{
await conn.OpenAsync();
using (var command = conn.CreateCommand())
{
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
command.CommandText = query;
DbDataReader reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
groups.Add(row);
}
}
reader.Dispose();
}
}
finally
{
conn.Close();
}
return View(groups);
}
Using deyimi ekleyin:
using System.Data.Common;
Uygulamayı çalıştırın ve Hakkında sayfasına gidin. Önceden görüntülediği aynı verileri görüntüler.
Güncelleştirme sorgusu çağırma
Contoso Üniversitesi yöneticilerinin veritabanında her kursun kredi sayısını değiştirme gibi genel değişiklikler yapmak istediğini varsayalım. Üniversitenin çok sayıda dersi varsa, bunların tümünü varlık olarak almak ve tek tek değiştirmek verimsiz olacaktır. Bu bölümde, kullanıcının tüm kurslar için kredi sayısını değiştireceği bir faktör belirtmesine olanak tanıyan bir web sayfası uygulayacak ve bir SQL UPDATE deyimi yürüterek değişikliği yapacaksınız. Web sayfası aşağıdaki çizim gibi görünür:
Kurs Kredileri sayfasını güncelle
CoursesController.cs
içinde HttpGet ve HttpPost için UpdateCourseCredits yöntemlerini ekleyin:
public IActionResult UpdateCourseCredits()
{
return View();
}
[HttpPost]
public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
{
if (multiplier != null)
{
ViewData["RowsAffected"] =
await _context.Database.ExecuteSqlCommandAsync(
"UPDATE Course SET Credits = Credits * {0}",
parameters: multiplier);
}
return View();
}
Denetleyici bir HttpGet isteğini işlediğinde, ViewData["RowsAffected"]
hiçbir şey döndürülmüyor ve görünümde, önceki çizimde gösterildiği gibi boş bir metin kutusu ve gönder düğmesi görüntüleniyor.
Güncelleştir düğmesine tıklandığında HttpPost yöntemi çağrılır ve çarpan metin kutusuna girilen değere sahiptir. Kod daha sonra dersleri güncelleyen SQL kodunu yürütür ve etkilenen satır sayısını ViewData
görünümüne döndürür. Görünüm bir RowsAffected
değeri aldığında, güncelleştirilen satır sayısını görüntüler.
Çözüm Gezgini'nde, Görünümler/Kurslar klasörüne sağ tıklayın ve ardından Yeni Öğe > Ekle'yitıklayın.
Yeni Öğe Ekle iletişim kutusunda, sol bölmedeki Yüklü altındaki Çekirdek ' ASP.NET tıklayın, Razor Görünüm'e tıklayın ve yeni görünümü UpdateCourseCredits.cshtml
olarak adlandırın.
Views/Courses/UpdateCourseCredits.cshtml
içinde şablon kodunu aşağıdaki kodla değiştirin:
@{
ViewBag.Title = "UpdateCourseCredits";
}
<h2>Update Course Credits</h2>
@if (ViewData["RowsAffected"] == null)
{
<form asp-action="UpdateCourseCredits">
<div class="form-actions no-color">
<p>
Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
</p>
<p>
<input type="submit" value="Update" class="btn btn-default" />
</p>
</div>
</form>
}
@if (ViewData["RowsAffected"] != null)
{
<p>
Number of rows updated: @ViewData["RowsAffected"]
</p>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Kurslar sekmesini seçip tarayıcının adres çubuğundaki URL'nin sonuna "/UpdateCourseCredits" ekleyerek UpdateCourseCredits
yöntemini çalıştırın (örneğin: http://localhost:5813/Courses/UpdateCourseCredits
). Metin kutusuna bir sayı girin:
Kurs Kredileri sayfasını
Güncelleştir'itıklayın. Etkilenen satır sayısını görürsünüz:
Düzeltilmiş kredi sayısına sahip derslerin listesini görmek için Listeye Geri Dön'ne tıklayın.
Üretim kodunun güncelleştirmelerin her zaman geçerli verilerle sonuçlanmasını sağlayacağını unutmayın. Burada gösterilen basitleştirilmiş kod, kredi sayısını 5'ten büyük sayılar üretmek için yeterli şekilde artırabilir. (Credits
özelliği bir [Range(0, 5)]
özniteliğine sahiptir.) Güncelleştirme sorgusu çalışır ancak geçersiz veriler sistemin diğer bölümlerinde kredi sayısının 5 veya daha az olduğunu varsayarsa beklenmeyen sonuçlara neden olabilir.
Ham SQL sorguları hakkında daha fazla bilgi için bkz. Raw SQL Sorguları.
SQL sorgularını inceleme
Bazen veritabanına gönderilen gerçek SQL sorgularını görebilmek yararlı olabilir. ASP.NET Core için yerleşik günlüğe kaydetme işlevi, EF Core tarafından sorgular ve güncelleştirmeler için SQL içeren günlükleri yazmak için otomatik olarak kullanılır. Bu bölümde SQL günlüğü örnekleri göreceksiniz.
StudentsController.cs
açın ve Details
yönteminde if (student == null)
deyiminde bir kesme noktası ayarlayın.
Uygulamayı hata ayıklama modunda çalıştırın ve öğrencinin Ayrıntılar sayfasına gidin.
Hata ayıklama çıktısını gösteren Çıktı penceresine gidin ve sorguyu görürsünüz:
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (56ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [s].[ID], [s].[Discriminator], [s].[FirstName], [s].[LastName], [s].[EnrollmentDate]
FROM [Person] AS [s]
WHERE ([s].[Discriminator] = N'Student') AND ([s].[ID] = @__id_0)
ORDER BY [s].[ID]
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (122ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT [s.Enrollments].[EnrollmentID], [s.Enrollments].[CourseID], [s.Enrollments].[Grade], [s.Enrollments].[StudentID], [e.Course].[CourseID], [e.Course].[Credits], [e.Course].[DepartmentID], [e.Course].[Title]
FROM [Enrollment] AS [s.Enrollments]
INNER JOIN [Course] AS [e.Course] ON [s.Enrollments].[CourseID] = [e.Course].[CourseID]
INNER JOIN (
SELECT TOP(1) [s0].[ID]
FROM [Person] AS [s0]
WHERE ([s0].[Discriminator] = N'Student') AND ([s0].[ID] = @__id_0)
ORDER BY [s0].[ID]
) AS [t] ON [s.Enrollments].[StudentID] = [t].[ID]
ORDER BY [t].[ID]
Burada sizi şaşırtabilecek bir şey göreceksiniz: SQL, Kişi tablosundan en çok 2 satır (TOP(2)
) seçer.
SingleOrDefaultAsync
yöntemi sunucuda 1 satıra indirgenmez. Bunun nedeni şu şekildedir:
- Sorgu birden çok satır döndürüyorsa, yöntemi null döndürür.
- Sorgunun birden çok satır döndürip döndürmeyeceğini belirlemek için EF'nin en az 2 satır döndürip döndürmediğini denetlemesi gerekir.
Unutmayın ki Çıkış penceresinde log çıktısı almak için hata ayıklama modunu kullanmanız ve bir kesme noktasında durmanız gerekmiyor. Çıkışı görmek istediğiniz noktada kayıt tutmayı durdurmanın yalnızca kullanışlı bir yoludur. Bunu yapmazsanız günlük kaydı devam eder ve ilgilendiğiniz bölümleri bulmak için geriye kaydırmanız gerekir.
Soyutlama katmanı oluşturma
Birçok geliştirici, Entity Framework ile çalışan kodun etrafında bir katman olarak depo ve iş birimi desenlerini uygulamak için kod yazar. Bu desenler, bir uygulamanın veri erişim katmanı ile iş mantığı katmanı arasında bir soyutlama katmanı oluşturmaya yöneliktir. Bu desenleri uygulamak, uygulamanızı veri deposundaki değişikliklerden yalıtmanıza yardımcı olabilir ve otomatik birim testini veya test temelli geliştirmeyi (TDD) kolaylaştırabilir. Ancak, bu desenleri uygulamak için ek kod yazmak, EF kullanan uygulamalar için her zaman en iyi seçenek değildir. Bunun çeşitli nedenleri vardır:
EF bağlam sınıfı, kodunuzu veri deposuna özgü koddan yalıtmaktadır.
EF bağlam sınıfı, EF kullanarak yaptığınız veritabanı güncelleştirmeleri için bir iş birimi sınıfı görevi görebilir.
EF, depo kodu yazmadan TDD'yi uygulamaya yönelik özellikler içerir.
Bu öğretici serisinin Entity Framework 5 versiyonu 'yi ve depo ile çalışma birimi desenlerini nasıl uygulayacağınız hakkında bilgi için, bkz..
Entity Framework Core, test için kullanılabilecek bir bellek içi veritabanı sağlayıcısı uygular. Daha fazla bilgi için bkz. Test InMemoryile.
Otomatik değişiklik algılama
Entity Framework, bir varlığın geçerli değerlerini özgün değerlerle karşılaştırarak varlığın nasıl değiştiğini (ve bu nedenle veritabanına hangi güncelleştirmelerin gönderilmesi gerektiğini) belirler. Özgün değerler, varlık sorgulandığında veya eklendiğinde depolanır. Otomatik değişiklik algılamaya neden olan yöntemlerden bazıları şunlardır:
DbContext.SaveChanges
DbContext.Entry
Değişiklik İzleyici. Girdileri
Çok sayıda varlığı izliyorsanız ve bu yöntemlerden birini döngü içinde birçok kez çağırıyorsanız, ChangeTracker.AutoDetectChangesEnabled
özelliğini kullanarak otomatik değişiklik algılamayı geçici olarak kapatarak önemli performans iyileştirmeleri alabilirsiniz. Örneğin:
_context.ChangeTracker.AutoDetectChangesEnabled = false;
EF Core kaynak kodu ve geliştirme planları
Entity Framework Core kaynağı https://github.com/dotnet/efcorekonumundadır. EF Core deposu gecelik derlemeler, sorun izleme, özellik özellikleri, tasarım toplantı notları ve gelecekteki geliştirme için yol haritasıiçerir. Hataları dosyalayabilir veya bulabilir ve katkıda bulunabilirsiniz.
Kaynak kodu açık olsa da, Entity Framework Core bir Microsoft ürünü olarak tam olarak desteklenir. Microsoft Entity Framework ekibi, hangi katkıların kabul edildiği üzerinde denetim sahibidir ve her sürümün kalitesini sağlamak için tüm kod değişikliklerini test eder.
Mevcut veritabanından tersine mühendislik
Var olan veritabanından varlık sınıfları dahil olmak üzere bir veri modeline ters mühendislik uygulamak için scaffold-dbcontext komutunu kullanın. kullanmaya başlama öğreticisine bakın.
Kodu basitleştirmek için dinamik LINQ kullanma
Bu serideki üçüncü öğretici, switch
deyiminde sütun adlarını sabit olarak kodlayarak LINQ kodu yazmayı gösterir. Aralarından seçim yapabileceğiniz iki sütunla bu işlem düzgün çalışır, ancak çok sayıda sütuna sahipseniz kod ayrıntılı olabilir. Bu sorunu çözmek için EF.Property
yöntemini kullanarak özelliğin adını dize olarak belirtebilirsiniz. Bu yaklaşımı denemek için, StudentsController
içindeki Index
yöntemini aşağıdaki kodla değiştirin.
public async Task<IActionResult> Index(
string sortOrder,
string currentFilter,
string searchString,
int? pageNumber)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] =
String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
ViewData["DateSortParm"] =
sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
ViewData["CurrentFilter"] = searchString;
var students = from s in _context.Students
select s;
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}
if (string.IsNullOrEmpty(sortOrder))
{
sortOrder = "LastName";
}
bool descending = false;
if (sortOrder.EndsWith("_desc"))
{
sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
descending = true;
}
if (descending)
{
students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
}
else
{
students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
}
int pageSize = 3;
return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(),
pageNumber ?? 1, pageSize));
}
Teşekkür
Tom Dykstra ve Rick Anderson (twitter @RickAndMSFT)) bu kılavuzu yazdı. Rowan Miller, Diego Vega ve Entity Framework ekibinin diğer üyeleri kod incelemelerine yardımcı oldu ve öğreticiler için kod yazarken ortaya çıkarılan sorunların hatalarını ayıklamaya yardımcı oldu. John Parente ve Paul Goldman, ASP.NET Core 2.2 için öğreticiyi güncelleştirmeye çalıştı.
Yaygın hataları giderme
başka bir işlem tarafından ContosoUniversity.dll kullanılıyor
Hata mesajı:
'...bin\Debug\netcoreapp1.0\ContosoUniversity.dll' for writing -- 'The process cannot access the file '...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll' açılamıyor çünkü başka bir işlem tarafından kullanılıyor.
Çözüm:
IIS Express'te siteyi durdurun. Windows Sistem Tepsisi'ne gidin, IIS Express'in simgesini bulun ve sağ tıklayın, Contoso University sitesini seçin ve ardından Siteyi Durdurtıklayın.
Kod yazılmadan oluşturulmuş Yukarı ve Aşağı yöntem iskeletleri ile geçiş
Olası neden:
EF CLI komutları otomatik olarak kapatılıp kod dosyalarını kaydetmez.
migrations add
komutunu çalıştırdığınızda kaydedilmemiş değişiklikleriniz varsa EF değişikliklerinizi bulamaz.
Çözüm:
migrations remove
komutunu çalıştırın, kod değişikliklerinizi kaydedin ve migrations add
komutunu yeniden çalıştırın.
Veritabanı güncelleştirmesini çalıştırırken oluşan hatalar
Mevcut verileri olan bir veritabanında şema değişiklikleri yaparken başka hatalar almak mümkündür. Çözemediğiniz geçiş hataları alırsanız, bağlantı dizesindeki veritabanı adını değiştirebilir veya veritabanını silebilirsiniz. Yeni bir veritabanı kullanıldığında, aktarılacak veri yoktur ve "update-database" komutunun hatasız tamamlanma ihtimali oldukça yüksektir.
En basit yaklaşım, appsettings.json
veritabanını yeniden adlandırmaktır.
database update
bir sonraki çalıştırmanızda yeni bir veritabanı oluşturulur.
SSOX'ta bir veritabanını silmek için veritabanına sağ tıklayın, Sil'e tıklayın ve Veritabanını Sil iletişim kutusunda Mevcut bağlantıları kapat seçin ve Tamam'a tıklayın.
CLI kullanarak veritabanını silmek için database drop
CLI komutunu çalıştırın:
dotnet ef database drop
SQL Server örneğini bulma hatası
Hata mesajı:
SQL Server bağlantısı kurulurken ağ ile ilgili veya örneğe özgü bir hata oluştu. Sunucu bulunamadı veya erişilebilir değil. Örnek adının doğru olduğunu ve SQL Server'ın uzak bağlantılara izin verecek şekilde yapılandırıldığını doğrulayın. (sağlayıcı: SQL Ağ Arabirimleri, hata: 26 - Sunucu/Örnek Bulma Hatası Belirtildi)
Çözüm:
Bağlantı dizesini denetleyin. Veritabanı dosyasını el ile sildiyseniz, yeni bir veritabanıyla baştan başlamak için yapı dizesindeki veritabanının adını değiştirin.
Kodu al
Tamamlanan uygulamayı indirin veya görüntüleyin.
Ek kaynaklar
EF Corehakkında daha fazla bilgi için Entity Framework Core belgelerine bakın. Bir kitap da mevcuttur: Entity Framework Core in Action.
Web uygulamasının dağıtımı hakkında bilgi için bkz. ASP.NET Core'u barındırma ve dağıtma.
kimlik doğrulaması ve yetkilendirme gibi ASP.NET Core MVC ile ilgili diğer konular hakkında daha fazla bilgi için bkz. ASP.NET Coregenel bakış .
Güvenilir, güvenli, performanslı, test edilebilir ve ASP.NET ölçeklenebilir bir Core uygulaması oluşturma yönergeleri için bkz. Enterprise web uygulaması desenleri. Desenleri uygulayan eksiksiz bir üretim kalitesinde örnek web uygulaması mevcuttur.
Sonraki adımlar
Bu öğreticide siz:
- Gerçekleştirilen ham SQL sorguları
- Varlıkları döndürmek için sorgu çağrıldı
- Diğer türleri döndürmek için sorgu çağrıldı
- Güncelleştirme sorgusu olarak adlandırılıyor
- sql sorguları incelendi
- Soyutlama katmanı oluşturuldu
- Otomatik değişiklik algılama hakkında bilgi edindi
- EF Core kaynak kodu ve geliştirme planları hakkında bilgi edindi
- Kodu basitleştirmek için dinamik LINQ kullanmayı öğrendin
Bu işlem, ASP.NET Core MVC uygulamasında Entity Framework Core'un kullanılmasıyla ilgili bu öğretici dizisini tamamlar. Bu seri yeni bir veritabanıyla çalıştı; alternatif olarak, mevcut veritabanından bir modele ters mühendislik.
ASP.NET Core