.Net 5 Ef Core Kullanımı
12-08-20211. Eager Loading ve Where Kullanımı:
IQueryable<Category> cats = db.Categories .Include(c => c.Products.Where(p => p.Stock >= stock));
Not: RawSql görmek için ToQueryString() metodu kullanılır.
2. SQL Yorumu:
IQueryable<Product> prods = db.Products
.TagWith("Products filtered by price and sorted.")
.Where(product => product.Cost > price)
.OrderByDescending(product => product.Cost);
Şu şekilde SQL yorumu ekler:
-- Products filtered by price and sorted.
3. Like Query:
IQueryable<Product> prods = db.Products
.Where(p => EF.Functions.Like(p.ProductName, $"%{input}%"));
4. LoggerFactory Kullanımı
using (var db = new NorthwindContext())
{
var loggerFactory = db.GetService<ILoggerFactory>();
loggerFactory.AddProvider(new ConsoleLoggerProvider());
}
5. Global Filter:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// example of using Fluent API instead of attributes
// to limit the length of a category name to under 15
modelBuilder.Entity<Category>()
.Property(category => category.CategoryName)
.IsRequired() // NOT NULL
.HasMaxLength(15);
// added to "fix" the lack of decimal support in SQLite
modelBuilder.Entity<Product>()
.Property(product => product.Cost)
.HasConversion<double>();
// global filter to remove discontinued products
modelBuilder.Entity<Product>().HasQueryFilter(p => !p.Discontinued);
}
6. Lazy Loading için:
-<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="5.0.0" />
- optionsBuilder.UseLazyLoadingProxies().UseSqlite($"Filename={path}");
- Include metodu kullanılmaz. Category içerisindeki products loop ile çağrıldığı zaman lazy olarak yüklenecektir.
- Lazy disable yapmak için:db.ChangeTracker.LazyLoadingEnabled = false;
7. Explicitly Loading:
var products = db.Entry(c).Collection(c2 => c2.Products); //buradaki c bir Category nesnesi
if (!products.IsLoaded) products.Load();
WriteLine($"{c.CategoryName} has {c.Products.Count} products.");
8. Veri Ekleme
static bool AddProduct(
int categoryID, string productName, decimal? price)
{
using (var db = new Northwind())
{
var newProduct = new Product
{
CategoryID = categoryID,
ProductName = productName,
Cost = price
};
// mark product as added in change tracking
db.Products.Add(newProduct);
// save tracked change to database
int affected = db.SaveChanges();
return (affected == 1);
}
}
9. Veri Güncelleme
static bool IncreaseProductPrice(string name, decimal amount)
{
using (var db = new Northwind())
{
// get first product whose name starts with name
Product updateProduct = db.Products.First(
p => p.ProductName.StartsWith(name));
updateProduct.Cost += amount;
int affected = db.SaveChanges();
return (affected == 1);
}
}
10. Veri Silme
static int DeleteProducts(string name)
{
using (var db = new Northwind())
{
IEnumerable<Product> products = db.Products.Where(p => p.ProductName.StartsWith(name));
db.Products.RemoveRange(products); // tek kayıt silmek için Remove kullanılır
int affected = db.SaveChanges();
return affected;
}
}
11. Connection Pooling
Normalde DbContext sınıfı disposable özelliğe sahiptir. using keywordu ile kullanıldığı zaman işlem bitince disposable metodu otomatik olarak çağrılır. Connection Pooling için şu kodu kullanabiliriz:
services.AddDbContextPool<BloggingContext>(
options => options.UseSqlServer(connectionString));
Bu kod kullanıldığı zaman disposable metodu çağrıldığı zaman bağlantı havuzuna geri döner. Fakat burada dikkat edilmesi gereken husus şudur: Eğer OnConfiguring içerisinde ortak paylaşılamayacak bir private field varsa bu mekanizma kullanılmamalıdır.
12. Transactions Kullanımı
- Öncelikle IDbContextTransaction sınıfını kullanmak için "using Microsoft.EntityFrameworkCore.Storage;" statement eklenir.
- Database Context sınıfının Database property si kullanılır.
- Örnek:
static int DeleteProducts(string name)
{
using (var db = new Northwind())
{
using (IDbContextTransaction t = db.Database.BeginTransaction())
{
WriteLine("Transaction isolation level: {0}",
t.GetDbTransaction().IsolationLevel);
var products = db.Products.Where(
p => p.ProductName.StartsWith(name));
db.Products.RemoveRange(products);
int affected = db.SaveChanges();
t.Commit();
return affected;
}
}
}
Bu kod çalıştığı zaman şu output olur: Transaction isolation level: Serializable yazar. Bu leveli değiştirmek için DbContext.Database.BeginTransaction(IsolationLevel.Snapshot); metodu kullanılır.
13. Linq Kullanımı
DbSet<T> sınıfı IQueryable<T> interface'ini, bu interface ise IEnumerable<T> interface'ini implement ettiği için Linq kütüphanesi EF Core ile kullanılabilmektedir.
Memory üzerinde LINQ provider ile query'ler oluşturulmaktadır. Bu işlem expression tree mantığı ile yapılmaktadır. "Expression tree" Kodu ağaç benzeri bir veri yapısında temsil ederler ve SQLite gibi harici veri sağlayıcıları için LINQ sorguları oluşturmak için yararlı olan dinamik sorguların oluşturulmasını sağlarlar.
a. Sadece belli sütunları çekmek
Projection yapısı kullanılmaktadır.
var query = db.Products
// query is a DbSet<Product>
.Where(product => product.UnitPrice < 10M)
// query is now an IQueryable<Product>
.OrderByDescending(product => product.UnitPrice)
// query is now an IOrderedQueryable<Product>
.Select(product => new // anonymous type projection usage
{
product.ProductID,
product.ProductName,
product.UnitPrice
});
b. Include kullanmadan iki tabloyu birleştirmek
Aşağıdaki gibi JOIN metodunu kullanabiliriz. Bu metod 4 parametre almaktadır.birleştirmek istediğiniz dizi, eşleştirilecek sol dizideki özellik veya özellikler, eşleştirilecek sağ dizideki özellik veya özellikler ve bir projection.
static void JoinCategoriesAndProducts()
{
using (var db = new Northwind())
{
// join every product to its category to return 77 matches
var queryJoin = db.Categories.Join(
inner: db.Products,
outerKeySelector: category => category.CategoryID,
innerKeySelector: product => product.CategoryID,
resultSelector: (c, p) =>
new { c.CategoryName, p.ProductName, p.ProductID });
foreach (var item in queryJoin)
{
WriteLine("{0}: {1} is in {2}.",
arg0: item.ProductID,
arg1: item.ProductName,
arg2: item.CategoryName);
}
}
}
c. Grup ve Join kullanımı ise aşağıdaki gibi yapılabilir:
static void GroupJoinCategoriesAndProducts()
{
using (var db = new Northwind())
{
// group all products by their category to return 8 matches
var queryGroup = db.Categories.AsEnumerable().GroupJoin(
inner: db.Products,
outerKeySelector: category => category.CategoryID,
innerKeySelector: product => product.CategoryID,
resultSelector: (c, matchingProducts) => new {
c.CategoryName,
Products = matchingProducts.OrderBy(p => p.ProductName)
});
foreach (var item in queryGroup)
{
WriteLine("{0} has {1} products.",
arg0: item.CategoryName,
arg1: item.Products.Count());
foreach (var product in item.Products)
{
WriteLine($" {product.ProductName}");
}
}
}
}
AsEnumerable metodu kullanılmasaydı RuntimeException meydana gelirdi.
Bu iki yöntemin kullanılma amacı birbiriyle foreign keyi bağlantısı olmayan tabloların birleştirilmesini sağlamaktır.
d. Kendi LINQ metodumuzu oluşturmak:
using System.Collections.Generic;
namespace System.Linq // extend Microsoft's namespace
{
public static class MyLinqExtensions
{
// this is a chainable LINQ extension method
public static IEnumerable<T> ProcessSequence<T>(
this IEnumerable<T> sequence)
{
// you could do some processing here
return sequence;
}
// these are scalar LINQ extension methods
public static int? Median(this IEnumerable<int?> sequence)
{
var ordered = sequence.OrderBy(item => item);
int middlePosition = ordered.Count() / 2;
return ordered.ElementAt(middlePosition);
}
public static int? Median<T>(
this IEnumerable<T> sequence, Func<T, int?> selector)
{
return sequence.Select(selector).Median();
}
public static decimal? Median(
this IEnumerable<decimal?> sequence)
{
var ordered = sequence.OrderBy(item => item);
int middlePosition = ordered.Count() / 2;
return ordered.ElementAt(middlePosition);
}
public static decimal? Median<T>(
this IEnumerable<T> sequence, Func<T, decimal?> selector)
{
return sequence.Select(selector).Median();
}
public static int? Mode(this IEnumerable<int?> sequence)
{
var grouped = sequence.GroupBy(item => item);
var orderedGroups = grouped.OrderByDescending(
group => group.Count());
return orderedGroups.FirstOrDefault().Key;
}
public static int? Mode<T>(
this IEnumerable<T> sequence, Func<T, int?> selector)
{
return sequence.Select(selector).Mode();
}
public static decimal? Mode(
this IEnumerable<decimal?> sequence)
{
var grouped = sequence.GroupBy(item => item);
var orderedGroups = grouped.OrderByDescending(
group => group.Count());
return orderedGroups.FirstOrDefault().Key;
}
public static decimal? Mode<T>(
this IEnumerable<T> sequence, Func<T, decimal?> selector)
{
return sequence.Select(selector).Mode();
}
}
}
Daha sonra aşağıdaki gibi kullanabiliriz:
var query = db.Products
// query is a DbSet<Product>
.ProcessSequence()
.Where(product => product.UnitPrice < 10M)
// query is now an IQueryable<Product>
.OrderByDescending(product => product.UnitPrice)
// query is now an IOrderedQueryable<Product>
.Select(product => new // anonymous type
{
product.ProductID,
product.ProductName,
product.UnitPrice
});
Median, Average metodlarının kullanımı:
static void CustomExtensionMethods()
{
using (var db = new Northwind())
{
WriteLine("Mean units in stock: {0:N0}",
db.Products.Average(p => p.UnitsInStock));
WriteLine("Mean unit price: {0:$#,##0.00}",
db.Products.Average(p => p.UnitPrice));
WriteLine("Median units in stock: {0:N0}",
db.Products.Median(p => p.UnitsInStock));
WriteLine("Median unit price: {0:$#,##0.00}",
db.Products.Median(p => p.UnitPrice));
WriteLine("Mode units in stock: {0:N0}",
db.Products.Mode(p => p.UnitsInStock));
WriteLine("Mode unit price: {0:$#,##0.00}",
db.Products.Mode(p => p.UnitPrice));
}
}