Öğretici: Gelişmiş senaryolar hakkında bilgi edinin - ile MVC ASP.NET EF Core
Önceki öğreticide hiyerarşi başına tablo devralmayı 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ı yaptınız:
- Ham SQL sorgularını gerçekleştirme
- Varlıkları döndürmek için sorgu çağırma
- Diğer türleri döndürmek için sorgu çağırma
- Güncelleştirme sorgusu çağırma
- SQL sorgularını inceleme
- Soyutlama katmanı oluşturma
- Otomatik değişiklik algılama hakkında bilgi edinin
- Kaynak kodu ve geliştirme planları hakkında EF Core bilgi edinin
- Kodu basitleştirmek için dinamik LINQ kullanmayı öğrenin
Ön koşullar
Ham SQL sorgularını 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. 1.0'da EF Core aşağıdaki seçeneklere sahipsiniz:
DbSet.FromSql
Varlık türleri döndüren sorgular için yöntemini kullanın. Döndürülen nesneler nesne tarafındanDbSet
beklenen türde olmalıdır ve izlemeyi kapatmadığınız sürece veritabanı bağlamı tarafından otomatik olarak izlenir.Sorgu olmayan komutlar için komutunu
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
sınıfı, DbSet<TEntity>
türünde TEntity
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 yöntemindeki Details
kodu değiştireceksiniz.
DepartmentsController.cs
yöntemindeDetails
, aşağıdaki vurgulanmış kodda gösterildiği gibi, bir departmanı alan kodu bir FromSql
yöntem ç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 ve ardından departmanlardan birinin Ayrıntıları'nı seçin.
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
) veri aldınız ve sonuçları bir görünüm modeli nesneleri listesine EnrollmentDateGroup
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. 1.0'da EF Core bunu yapmanızın bir yolu ADO.NET kodu yazmak ve EF'den veritabanı bağlantısını almaktır.
içinde HomeController.cs
yöntemini aşağıdaki kodla değiştirin About
:
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. Daha önce görüntülediği 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:
içinde CoursesController.cs
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, iç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ğeri gösterir. Kod daha sonra kursları güncelleştiren SQL'i yürütür ve etkilenen satır sayısını içindeki ViewData
görünümüne döndürür. Görünüm bir RowsAffected
değer aldığında güncelleştirilen satır sayısını görüntüler.
Çözüm Gezgini Görünümler/Kurslar klasörüne sağ tıklayın ve ardından Yeni Öğe Ekle'ye > tıklayın.
Yeni Öğe Ekle iletişim kutusunda, sol bölmede Yüklü altında ASP.NET Core'a tıklayın, Görünüm'e tıklayın Razor ve yeni görünümü UpdateCourseCredits.cshtml
adlandırın.
içinde Views/Courses/UpdateCourseCredits.cshtml
ş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 ardından tarayıcının UpdateCourseCredits
adres çubuğunda url'nin sonuna "/UpdateCourseCredits" ekleyerek yöntemini çalıştırın (örneğin: http://localhost:5813/Courses/UpdateCourseCredits
). Metin kutusuna bir sayı girin:
Güncelleştir'i tıklatın. Etkilenen satır sayısını görürsünüz:
Düzeltilmiş kredi sayısına sahip kursların listesini görmek için Listeye Geri Dön'e 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, 5'ten büyük sayılarla sonuçlanan kredi sayısını yeterince çarpabilir. (Özelliğin Credits
bir [Range(0, 5)]
özniteliği vardır.) 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 . Ham 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 tarafından sorgular ve güncelleştirmeler için SQL içeren günlükler yazmak için otomatik olarak kullanılır EF Core . Bu bölümde SQL günlüğü örnekleri göreceksiniz.
yönteminde Details
öğesini açın StudentsController.cs
ve deyiminde if (student == null)
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 çıkışını gösteren Çıkış 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 sunucudaki 1 satıra çözümlenmiyor. 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.
Çıkış penceresinde günlüğe kaydetme çıkışı almak için hata ayıklama modunu kullanmanız ve kesme noktasında durmanız gerekmeyen bir işlem olduğunu unutmayın. Yalnızca çıkışa bakmak istediğiniz noktada günlüğe kaydetmeyi durdurmanın kullanışlı bir yoludur. Bunu yapmazsanız günlük kaydı devam eder ve ilgilendiğiniz bölümleri bulmak için geri kaydırmanız gerekir.
Soyutlama katmanı oluşturma
Birçok geliştirici, depoyu ve iş desenleri birimini Entity Framework ile çalışan bir kod çevresinde sarmalayıcı olarak 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.
Depoyu ve iş birimi desenlerini uygulama hakkında bilgi için bu öğretici serisinin Entity Framework 5 sürümüne bakın.
Entity Framework Core, test için kullanılabilecek bir bellek içi veritabanı sağlayıcısı uygular. Daha fazla bilgi için bkz . InMemory ile test.
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
ChangeTracker.Entries
Çok sayıda varlığı izliyorsanız ve bu yöntemlerden birini döngüde birçok kez çağırıyorsanız, özelliğini kullanarak otomatik değişiklik algılamayı ChangeTracker.AutoDetectChangesEnabled
geçici olarak kapatarak önemli performans iyileştirmeleri alabilirsiniz. Örnek:
_context.ChangeTracker.AutoDetectChangesEnabled = false;
EF Core kaynak kodu ve geliştirme planları
Entity Framework Core kaynağı konumundadır https://github.com/dotnet/efcore. Depoda EF Core gecelik derlemeler, sorun izleme, özellik özellikleri, tasarım toplantısı notları ve gelecekteki geliştirmeler için yol haritası yer alır. 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 bir veritabanından varlık sınıfları dahil olmak üzere bir veri modeline ters mühendislik uygulamak için scaffold-dbcontext komutunu kullanın. Başlarken öğreticisine bakın.
Kodu basitleştirmek için dinamik LINQ kullanma
Bu serideki üçüncü öğreticide, bir switch
deyimde sütun adlarını sabit olarak kodlayarak LINQ kodu yazma işlemi gösterilmektedir. 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 yöntemini kullanarak EF.Property
özelliğin adını dize olarak belirtebilirsiniz. Bu yaklaşımı denemek için içindeki yöntemini StudentsController
aşağıdaki kodla değiştirinIndex
.
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));
}
İlgili kaynaklar
Tom Dykstra ve Rick Anderson (twitter @RickAndMSFT) bu öğreticiyi 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ı.
Sık karşılaşılan hataları giderme
Başka bir işlem tarafından kullanılan ContosoUniversity.dll
Hata iletisi:
'... açılamıyor yazmak için bin\Debug\netcoreapp1.0\ContosoUniversity.dll' -- 'İşlem başka bir işlem tarafından kullanıldığından '...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll' dosyasına erişemiyor.
Çözüm:
IIS Express'te siteyi durdurun. Windows Sistem Tepsisi'ne gidin, IIS Express'i bulun ve simgesine sağ tıklayın, Contoso Üniversitesi sitesini seçin ve ardından Siteyi Durdur'a tıklayın.
Yukarı ve Aşağı yöntemlerinde kod olmadan iskelesi oluşturulmuş geçiş
Olası neden:
EF CLI komutları otomatik olarak kapatılıp kod dosyalarını kaydetmez. Komutu çalıştırdığınızda migrations add
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 komutu yeniden çalıştırınmigrations add
.
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ı dizesi veritabanı adını değiştirebilir veya veritabanını silebilirsiniz. Yeni bir veritabanıyla, geçirecek veri yoktur ve update-database komutunun hatasız tamamlanma olasılığı çok daha yüksektir.
En basit yaklaşım, içindeki appsettings.json
veritabanını yeniden adlandırmaktır. bir sonraki çalıştırmanızda database update
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 Varolan bağlantıları kapat'ı seçin ve Tamam'a tıklayın.
CLI kullanarak veritabanını silmek için CLI komutunu çalıştırın database drop
:
dotnet ef database drop
SQL Server örneğini bulma hatası
Hata İletisi:
SQL Server ile bağlantı kurulmaya çalışılırken ağ ile ilişkili veya örneğe özgü bir hata oluştu. Sunucu bulunamadı veya erişilebilir değildi. Örnek adının doğru olduğundan ve SQL Server'ın uzak bağlantılara izin verecek şekilde yapılandırıldığından emin olun. (sağlayıcı: SQL Ağ Arabirimleri, hata: 26 - Belirtilen Sunucuyu/Örneği Bulma Hatası)
Çö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 alma
Tamamlanan uygulamayı indirin veya görüntüleyin.
Ek kaynaklar
hakkında EF Coredaha fazla bilgi için Entity Framework Core belgelerine bakın. Bir kitap da mevcuttur: Entity Framework Core in Action.
Web uygulamasını dağıtma 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 bilgi için bkz . ASP.NET Core'a Genel Bakış.
Sonraki adımlar
Bu öğreticide şunları yaptınız:
- 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
- Kaynak kodu ve geliştirme planları hakkında bilgi EF Core edinildi
- 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 bir veritabanından modele ters mühendislik uygulamak da kullanılabilir.
ASP.NET Core