Öğ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ından DbSet 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 TEntitybir 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.csyö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.

Department Details

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.csyö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.

About page

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:

Update Course Credits page

içinde CoursesController.csHttpGet 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 ViewDatagö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.cshtmladlandı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 UpdateCourseCreditsadres ç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:

Update Course Credits page

Güncelleştir'i tıklatın. Etkilenen satır sayısını görürsünüz:

Update Course Credits page rows affected

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.jsonveritabanını yeniden adlandırmaktır. bir sonraki çalıştırmanızda database updateyeni 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.