Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
EF Core, sorgularda kullanıcı tanımlı SQL işlevlerinin kullanılmasına olanak tanır. Bunu yapmak için, model yapılandırması sırasında işlevlerin bir CLR yöntemine eşlenmesi gerekir. LINQ sorgusunu SQL'e çevirirken, eşleştirildiği CLR işlevi yerine kullanıcı tanımlı işlev çağrılır.
Bir yöntemi SQL işlevine eşleme
Kullanıcı tanımlı işlev eşlemesinin nasıl çalıştığını göstermek için aşağıdaki varlıkları tanımlayalım:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int? Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int Rating { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentId { get; set; }
public string Text { get; set; }
public int Likes { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
Ve aşağıdaki model yapılandırması:
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne(p => p.Blog);
modelBuilder.Entity<Post>()
.HasMany(p => p.Comments)
.WithOne(c => c.Post);
Blogda birçok gönderi olabilir ve her gönderinin birçok yorumu olabilir.
Ardından, blogu temel alarak CommentedPostCountForBlogbelirli bir blog için en az bir yorum içeren gönderi sayısını döndüren kullanıcı tanımlı işlevini Idoluşturun:
CREATE FUNCTION dbo.CommentedPostCountForBlog(@id int)
RETURNS int
AS
BEGIN
RETURN (SELECT COUNT(*)
FROM [Posts] AS [p]
WHERE ([p].[BlogId] = @id) AND ((
SELECT COUNT(*)
FROM [Comments] AS [c]
WHERE [p].[PostId] = [c].[PostId]) > 0));
END
EF Core'da bu işlevi kullanmak için, kullanıcı tanımlı işlevle eşlediğimiz aşağıdaki CLR yöntemini tanımlarız:
public int ActivePostCountForBlog(int blogId)
=> throw new NotSupportedException();
CLR yönteminin gövdesi önemli değildir. EF Core bağımsız değişkenlerini çeviremedikçe yöntemi istemci tarafında çağrılmaz. Bağımsız değişkenler çevrilebiliyorsa EF Core yalnızca yöntem imzasını önemser.
Uyarı
Örnekte, yöntem DbContext üzerinde tanımlanır, ancak diğer sınıfların içinde de statik bir yöntem olarak tanımlanabilir.
Bu işlev tanımı artık model yapılandırmasında kullanıcı tanımlı işlevle ilişkilendirilebilir:
modelBuilder.HasDbFunction(typeof(BloggingContext).GetMethod(nameof(ActivePostCountForBlog), [typeof(int)]))
.HasName("CommentedPostCountForBlog");
VARSAYıLAN olarak EF Core, CLR işlevini aynı ada sahip kullanıcı tanımlı bir işlevle eşlemeye çalışır. Adlar farklıysa, eşlemek istediğimiz kullanıcı tanımlı işlev için doğru adı sağlamak için kullanabiliriz HasName .
Şimdi aşağıdaki sorguyu yürütüyoruz:
var query1 = from b in context.Blogs
where context.ActivePostCountForBlog(b.BlogId) > 1
select b;
Bu SQL'i oluşturur:
SELECT [b].[BlogId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
WHERE [dbo].[CommentedPostCountForBlog]([b].[BlogId]) > 1
Bir yöntemi özel SQL'e eşleme
EF Core, belirli bir SQL'e dönüştürülen kullanıcı tanımlı işlevlere de olanak tanır. SQL ifadesi, kullanıcı tanımlı işlev yapılandırması sırasında yöntemi kullanılarak HasTranslation sağlanır.
Aşağıdaki örnekte, iki tamsayı arasındaki yüzde farkını hesaplayan bir işlev oluşturacağız.
CLR yöntemi aşağıdaki gibidir:
public double PercentageDifference(double first, int second)
=> throw new NotSupportedException();
İşlev tanımı aşağıdaki gibidir:
// 100 * ABS(first - second) / ((first + second) / 2)
modelBuilder.HasDbFunction(
typeof(BloggingContext).GetMethod(nameof(PercentageDifference), [typeof(double), typeof(int)]))
.HasTranslation(
args =>
new SqlBinaryExpression(
ExpressionType.Multiply,
new SqlConstantExpression(100, new IntTypeMapping("int", DbType.Int32)),
new SqlBinaryExpression(
ExpressionType.Divide,
new SqlFunctionExpression(
"ABS",
[
new SqlBinaryExpression(
ExpressionType.Subtract,
args.First(),
args.Skip(1).First(),
args.First().Type,
args.First().TypeMapping)
],
nullable: true,
argumentsPropagateNullability: [true, true],
type: args.First().Type,
typeMapping: args.First().TypeMapping),
new SqlBinaryExpression(
ExpressionType.Divide,
new SqlBinaryExpression(
ExpressionType.Add,
args.First(),
args.Skip(1).First(),
args.First().Type,
args.First().TypeMapping),
new SqlConstantExpression(2, new IntTypeMapping("int", DbType.Int32)),
args.First().Type,
args.First().TypeMapping),
args.First().Type,
args.First().TypeMapping),
args.First().Type,
args.First().TypeMapping));
İşlevi tanımladıktan sonra sorguda kullanılabilir. EF Core, veritabanı işlevini çağırmak yerine, HasTranslation'dan derlenen SQL ifade ağacını temel alarak yöntem gövdesini doğrudan SQL'e çevirir. Aşağıdaki LINQ sorgusu:
var query2 = from p in context.Posts
select context.PercentageDifference(p.BlogId, 3);
Aşağıdaki SQL'i üretir:
SELECT 100 * (ABS(CAST([p].[BlogId] AS float) - 3) / ((CAST([p].[BlogId] AS float) + 3) / 2))
FROM [Posts] AS [p]
Kullanıcı tanımlı işlevin bağımsız değişkenlerine göre null kabul edebilirliğini yapılandırma.
Kullanıcı tanımlı işlev yalnızca null döndürebiliyorsa ve bir veya daha fazla bağımsız değişkeni null olduğunda, EFCore bunun belirtilmesi için bir yol sağlar ve bu da daha yüksek performanslı SQL elde edilmesini sağlar. İlgili işlev parametreleri model yapılandırmasına bir PropagatesNullability() çağrı eklenerek yapılabilir.
Bunu göstermek için kullanıcı işlevini ConcatStringstanımlayın:
CREATE FUNCTION [dbo].[ConcatStrings] (@prm1 nvarchar(max), @prm2 nvarchar(max))
RETURNS nvarchar(max)
AS
BEGIN
RETURN @prm1 + @prm2;
END
ve söz konusu yöntemle eşleşen iki CLR yöntemi:
public string ConcatStrings(string prm1, string prm2)
=> throw new InvalidOperationException();
public string ConcatStringsOptimized(string prm1, string prm2)
=> throw new InvalidOperationException();
Model yapılandırması (OnModelCreating yönteminin içinde) aşağıdaki gibidir:
modelBuilder
.HasDbFunction(typeof(BloggingContext).GetMethod(nameof(ConcatStrings), [typeof(string), typeof(string)]))
.HasName("ConcatStrings");
modelBuilder.HasDbFunction(
typeof(BloggingContext).GetMethod(nameof(ConcatStringsOptimized), [typeof(string), typeof(string)]),
b =>
{
b.HasName("ConcatStrings");
b.HasParameter("prm1").PropagatesNullability();
b.HasParameter("prm2").PropagatesNullability();
});
İlk işlev standart şekilde yapılandırılır. İkinci işlev, null olabilirlik yayılım optimizasyonunun avantajından yararlanacak şekilde yapılandırılır ve fonksiyonun null parametrelerle etkileşimini daha doğru bir şekilde izah eder.
Aşağıdaki sorguları oluştururken:
var query3 = context.Blogs.Where(e => context.ConcatStrings(e.Url, e.Rating.ToString()) != "https://mytravelblog.com/4");
var query4 = context.Blogs.Where(
e => context.ConcatStringsOptimized(e.Url, e.Rating.ToString()) != "https://mytravelblog.com/4");
Şu SQL'i alacağız:
SELECT [b].[BlogId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
WHERE ([dbo].[ConcatStrings]([b].[Url], CONVERT(VARCHAR(11), [b].[Rating])) <> N'Lorem ipsum...') OR [dbo].[ConcatStrings]([b].[Url], CONVERT(VARCHAR(11), [b].[Rating])) IS NULL
SELECT [b].[BlogId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
WHERE ([dbo].[ConcatStrings]([b].[Url], CONVERT(VARCHAR(11), [b].[Rating])) <> N'Lorem ipsum...') OR ([b].[Url] IS NULL OR [b].[Rating] IS NULL)
İkinci sorgunun null atanabilirliğini test etmek için işlevin kendisini yeniden değerlendirmesi gerekmez.
Uyarı
Bu iyileştirme yalnızca işlevin yalnızca parametreleri nullolduğunda döndürebildiği null durumlarda kullanılmalıdır.
Sorgulanabilir bir işlevi tablo değeri döndüren bir işlevle dönüştürme
EF Core ayrıca, varlık türleri topluluğunu döndüren kullanıcı tanımlı CLR yöntemi kullanarak tablo-değerli bir IQueryable fonksiyona eşlemeyi de destekler ve EF Core'un TVF'leri parametrelerle eşlemesine olanak sağlar. İşlem, skaler kullanıcı tanımlı bir işlevi SQL işleviyle eşlemeye benzer: Veritabanında TVF, LINQ sorgularında kullanılan bir CLR işlevi ve ikisi arasındaki eşleme gerekir.
Örneğin, belirli bir "Beğen" eşiğine uyan en az bir açıklamaya sahip tüm gönderileri döndüren tablo değerli bir işlev kullanacağız:
CREATE FUNCTION dbo.PostsWithPopularComments(@likeThreshold int)
RETURNS TABLE
AS
RETURN
(
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Posts] AS [p]
WHERE (
SELECT COUNT(*)
FROM [Comments] AS [c]
WHERE ([p].[PostId] = [c].[PostId]) AND ([c].[Likes] >= @likeThreshold)) > 0
)
CLR yöntemi imzası aşağıdaki gibidir:
public IQueryable<Post> PostsWithPopularComments(int likeThreshold)
=> FromExpression(() => PostsWithPopularComments(likeThreshold));
Tavsiye
FromExpression CLR işlev gövdesindeki çağrı, işlevin normal bir DbSet yerine kullanılmasına olanak tanır.
İşte aşağıdaki eşleme:
modelBuilder.Entity<Post>().ToTable("Posts");
modelBuilder.HasDbFunction(typeof(BloggingContext).GetMethod(nameof(PostsWithPopularComments), [typeof(int)]));
Uyarı
Sorgulanabilir bir işlev, tablo değerli bir işlevle eşleştirilmelidir ve HasTranslation kullanılamaz.
İşlev eşlendiğinde aşağıdaki sorgu:
var likeThreshold = 3;
var query5 = from p in context.PostsWithPopularComments(likeThreshold)
orderby p.Rating
select p;
Üretir:
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [dbo].[PostsWithPopularComments](@likeThreshold) AS [p]
ORDER BY [p].[Rating]