淺講EF高級用法之自定義函數


介紹

好久沒給大家更新文章了,前2個月因家庭原因跳槽回到青島,前段時間比較忙所以沒有什么時間,這2個月在和同事一起做項目,發現了很多好意思的東西拿出來給大家講一講。

正文

大家先來下面這幅圖,這是我司一個老項目的代碼,你可能會好奇為啥給我看SQL說好的講EF哪?

![SQL]](https://raw.githubusercontent.com/MrChuJiu/BlogImageBed/main/BlogVnextCore/67.png)

大家看這個我框出來的部分,這里調用了一個SQL的函數,雖然我們都在使用EF的過程中每天喊着不要使用存儲過程、函數、觸發器等SQL相關的東西,但是其實真實落地到體積足夠龐大的項目后,
我們會發現,很多東西不是我們能夠左右的。當客戶執意一些東西的時候我們只能想辦法設計的更好,當然上面的圖是一個錯誤的寫法。

這個項目中有很多實體的查詢、添加、修改需要調用加密、解密函數,所以這部分代碼都采用原生SQL來做的,這非常破壞我們項目整體的代碼結構。

其實EF本身是支持我們調用SQL函數的。

現在就給大家上代碼

首先新建3個實體


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; }
}


  public class ApplicationDbContext : DbContext
    {

        public DbSet<Blog> Blog { get; set; }

        public DbSet<Post> Post { get; set; }

        public DbSet<Comment> Comment { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Blog>()
                .HasMany(b => b.Posts)
                .WithOne(p => p.Blog);

            modelBuilder.Entity<Post>()
                .HasMany(p => p.Comments)
                .WithOne(c => c.Post);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);

            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFCoreDbFunction.Disconnected;Trusted_Connection=True;ConnectRetryCount=0");
        }
    }

將方法映射到自定義 SQL

先創建一個自定義函數


CREATE FUNCTION dbo.CommentedPostCountForBlog(@id int)
RETURNS int
AS
BEGIN
    RETURN (SELECT COUNT(*)
        FROM [Post] AS [p]
        WHERE ([p].[BlogId] = @id) AND ((
            SELECT COUNT(*)
            FROM [Comment] AS [c]
            WHERE [p].[PostId] = [c].[PostId]) > 0));
END

然后在DbContext中新增下面代碼,

// CLR 方法的主體並不重要。 不會在客戶端調用此方法,除非 EF Core 不能轉換其參數。 如果可以轉換參數,EF Core 只關心方法簽名。
public int ActivePostCountForBlog(int blogId)
    => throw new NotSupportedException();


// 此函數定義現在可以與模型配置中用戶定義的函數關聯
modelBuilder.HasDbFunction(typeof(BloggingContext).GetMethod(nameof(ActivePostCountForBlog), new[] { typeof(int) }))
    .HasName("CommentedPostCountForBlog");


我們執行下面代碼

var query1 = from b in context.Blogs
             where context.ActivePostCountForBlog(b.BlogId) > 1
             select b;

// 對應SQL語句

SELECT [b].[BlogId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
WHERE [dbo].[CommentedPostCountForBlog]([b].[BlogId]) > 1

將可查詢函數映射到表值函數

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
)
public IQueryable<Post> PostsWithPopularComments(int likeThreshold)
    => FromExpression(() => PostsWithPopularComments(likeThreshold));


modelBuilder.HasDbFunction(typeof(BloggingContext).GetMethod(nameof(PostsWithPopularComments), new[] { typeof(int) }));

執行下面代碼

var likeThreshold = 3;
var query1 = from p in context.PostsWithPopularComments(likeThreshold)
             orderby p.Rating
             select p;

// 對應SQL語句

SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [dbo].[PostsWithPopularComments](@likeThreshold) AS [p]
ORDER BY [p].[Rating]

結語

最后歡迎各位讀者關注我的博客, https://github.com/MrChuJiu/Dppt 歡迎大家Star

聯系作者:加群:867095512 @MrChuJiu


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM