Aracılığıyla paylaş


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ı, TEntitytü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.csiç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 .

Bölüm 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.csiç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.

sayfası hakkında

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.csiç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ı ViewDatagö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.cshtmlolarak adlandırın.

Views/Courses/UpdateCourseCredits.cshtmliç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

Güncelleştir'itıklayın. Etkilenen satır sayısını görürsünüz:

Etkilenen Kurs Kredileri sayfa satırlarını güncelleştirme

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.jsonveritabanını yeniden adlandırmaktır. database updatebir 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.