<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【EF Core】FromExpression 方法有什么用?

      比 90% 的人細心的大伙伴一定發現了 DbContext 類有一個方法叫 FromExpression,它到底干嗎用的?官方文檔中沒有專門的介紹(只在表值函數映射的例子中看到)。

      咱們先來看看此方法的簽名:

      IQueryable<TResult> FromExpression<TResult>(Expression<Func<IQueryable<TResult>>> expression)

      看著好像很復雜的樣子。其實不,咱們來拆解一下:

      1、TResult 是類型參數(泛型的知識點沒忘吧),這里其實指的就是實體類型,比如,你的愛狗 Dog。

      2、這個方法返回 IQueryable<T> 類型,說明允許你使用 LINQ 查詢。

      3、重點理解其參數——Expression<TDelegate> 表達有個萬能規律:可以把與 TDelegate 類型兼容的 lambda 表達式直接賦值給 Expression<> 變量。即這個 FromExpression 方法可以使用以下 lambda 表達式作為參數:

      () => [返回 IQueryable<T>]

      這個委托的意思就是:它,單身狗(無參數)一枚,但可以生產 IQueryable<T> 對象。

      哦,說了一大堆,還沒說這個方法到底有啥毛用。它的用處就是你可以指定一個表達式,讓 EF 一開始就返回篩選過的查詢。在 DbContext 的派生類中聲明 DbSet<T> 類型的帶 get + set 的公共屬性這種生成首個查詢(根查詢)是最常用的方案,這個相信大伙們都很熟了,EF Core 最基礎操作,老周就不多介紹了。假如你要對查詢的初始數據做篩選,那么,按照 DbSet 的方案,要先執行一下 SELECT **** FROM #$$#*& 語句,然后再執行 SELECT **** FROM &*^$ WHERE xxxxx,我還沒操作數據呢就執行了兩次 SELECT 語句了。所以說,如果你一開始并不打算提取所有數據,那么直接從一開始就執行 SELECT **** FROM xxxx WHERE yyyy 多好,何必多浪費一條 SQL 語句?

      還有一種使用場景:你的數據不是從某個表 SELECT 出來的,而是從一個表值函數返回的,這種情況也要借助 FromExpression 方法。

      不知道老周以上說明你是否明白?不明白沒關系,咱們實戰一下你就懂了。

      咱們先定義的實體:

      /// <summary>
      /// 妖書實體
      /// </summary>
      public class Book
      {
          /// <summary>
          /// 標識 + 主鍵
          /// </summary>
          public Guid BookId { get; set; }
          /// <summary>
          /// 書名
          /// </summary>
          public string Title { get; set; } = string.Empty;
          /// <summary>
          /// 簡介
          /// </summary>
          public string? Description { get; set; }
          /// <summary>
          /// 作者
          /// </summary>
          public string Author { get; set; } = string.Empty;
      }

      然后,繼承 DbContext 類,常規操作。

      public class MyDbContext : DbContext
      {
          protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
          {
              optionsBuilder.UseSqlServer("Server=.\\TEST;Database=mydb;Integrated Security=True;Trust Server Certificate=True");
                                      // 配置日志
                                      //.LogTo(log => Debug.WriteLine(log));
          }
          protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              modelBuilder.Entity<Book>(t =>
              {
                  t.ToTable("books", tb =>
                  {
                      tb.Property(x => x.BookId).HasColumnName("book_id");
                      tb.Property(z=>z.Title).HasColumnName("title");
                      tb.Property(k => k.Description).HasColumnName("desc");
                      tb.Property(f => f.Author).HasColumnName("author");
                  })
                  .HasKey(t => t.BookId);
              });
          }
      
          // 以下行現在不需要了
          //public DbSet<Book> Books { get; set; }
      
          public IQueryable<Book> MyBooks
              => FromExpression(() => Set<Book>().Where(x => x.Author == "老周"));
      }

      這里不用再定義 DbSet<> 類型的屬性了,因為我們要對數據進行篩選,重點看 MyBooks 屬性的實現:

       public IQueryable<Book> MyBooks
           => FromExpression(() => Set<Book>().Where(x => x.Author == "老周"));

      Set<Book>() 方法的調用會讓 DbContext 自動在緩存字典中添加數據集合,然后一句 Where 做出篩選,上述代碼的意思是只查詢老周寫的妖書,其他作者的不考慮。這時候 DbContext 不會發出 select * from xxx  SQL 語句,所以你不用擔心執行多余的 SQL。調用 FromExpression 方法后,會使初始查詢直接生成 Select * from xxx where ...... 語句,只查詢一次。

      現在往 SQL Server 中新建 mydb 數據庫,并創建 books 表。

      CREATE TABLE [dbo].[books] (
          [book_id] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
          [title]   NVARCHAR (35)    NOT NULL,
          [desc]    NVARCHAR (100)   NULL,
          [author]  NVARCHAR (20)    NOT NULL,
          PRIMARY KEY CLUSTERED ([book_id] ASC)
      );

      順便向表中插入些測試數據。

      insert into books (title, author, [desc])
      values  (N'R語言從盜墓到考古',  N'張法師', N'一套不正經的R語言退隱教程'),
                  (N'瘋言瘋語之HTML 6.0', N'老周', N'提前一個世紀出現的超文本協議'),
                  (N'程序員風水學', N'孫電鷹', N'先匪后將的他,曾有“東陵大盜”之稱,在盜掘過程中他學會了用風水理論去Debug項目'),
                  (N'雞形機器人編程入門', N'老周', N'未來,由于長期不種植作物,人類只能躺在病床上依靠吮吸預制營養液維持生命;后有人提出開發雞形機器人,幫助人類進食')

       

      現在,咱們試一下。

       using var context = new MyDbContext();
       foreach(Book bk in context.MyBooks)
       {
           Console.WriteLine($"{bk.Author,-10}{bk.Title}");
       }

      如果日志啟用,那么,你會看到,DbContext 從初始化到 foreach 循環訪問數據,只生成了一條 SQL 語句。

      SELECT [b].[book_id], [b].[author], [b].[desc], [b].[title]
          FROM [books] AS [b]
          WHERE [b].[author] = N'老周'

       

      下面來看看另一種應用情形——映射表值函數。

      先在 SQL Server 中創建一個內聯表值函數,名為 get_all_books,返回表中所有行。

      CREATE FUNCTION [dbo].[get_all_books]()
          RETURNS TABLE
              RETURN select * from dbo.books;

      回到 .NET 項目,咱們要映射一下函數。

      A、先在 DbContext 的派生類中定義一個方法,用于映射到函數,不需要實現方法體,直接拋異常就行。

      internal IQueryable<Book> GetAllBooksMap()
      {
          throw new NotSupportedException();
      }

      實際上,EF Core 并不會真正調用方法,只是通過生成表達式樹 + 反射出方法名,然后再找到與方法名對應的數據庫中的函數罷了。所以,方法不需要實現代碼。

      B、OnModelCreating 方法要改一下,映射列名的 HasColumnName 方法不能在 ToTable 方法中配置,否則表值函數返回的實體不能正確映射。

      modelBuilder.Entity<Book>(t =>
      {
          t.ToTable("books");
          t.HasKey(t => t.BookId);
          t.Property(x => x.BookId).HasColumnName("book_id");
          t.Property(z => z.Title).HasColumnName("title");
          t.Property(k => k.Description).HasColumnName("desc");
          t.Property(f => f.Author).HasColumnName("author");
      });

      也就是列名映射要在 Property 上配置,不能在 TableBuilder 上配置。

      C、HasDbFunction 映射函數。

       // 注意數據庫中的函數名與類方法不同
       modelBuilder.HasDbFunction(GetType().GetMethod(nameof(GetAllBooksMap), BindingFlags.NonPublic)!).HasName("get_all_books"); 

       

      這里有個誤區:很多大伙伴以為這樣就完事了,然后就開始調用代碼了。

      using var context = new MyDbContext();
      foreach(Book bk in context.GetAllBooksMap())
      {
          Console.WriteLine($"{bk.Author,-10}{bk.Title}");
      }

      你以為這樣是對的,但運行后就是錯的。上面不是說了嗎?GetAllBooksMap 方法是沒有實現的,你不能直接調用它!不能調用,不能調用,不能調用!!

      我們還需要再給 DbContext 的派生類再定義一個方法,使用 FromExpression 方法讓 GetAllBooksMap 轉為表達式樹。

      public IQueryable<Book> GetAllBooks()
      {
         return  this.FromExpression(() => GetAllBooksMap());
      }

      這么一來,GetAllBooksMap() 就成了表達式樹,EF 不會真的調用它,只是獲取相關信息,再翻譯成 SQL。

      然后這樣用:

      using var context = new MyDbContext();
      foreach(Book bk in context.GetAllBooks())
      {
          Console.WriteLine($"{bk.Author,-10}{bk.Title}");
      }

      看,四條記錄就讀出來了。

      image

      可是,你也發現了,這TM太麻煩了,為了表值函數映射,我要封裝兩個方法成員。其實,這里可以把兩個方法合成一個:

      public IQueryable<Book> GetAllBooks()
      {
         return  this.FromExpression(() => GetAllBooks());
      }

      由于是公共方法,OnModelCreating 中的 HasDbFunction 代碼也可以精簡一下。

      modelBuilder.HasDbFunction(GetType().GetMethod(nameof(GetAllBooks))!).HasName("get_all_books"); 

      這時候你又搞不懂了,What?GetAllBooks 方法怎么自己調用了自己?不不不,沒有的事,你又忘了,FromExpression 只是轉換為表達式樹,并不會真的調用它。所以,這樣合并后,其實代碼是這樣走的:

      1、訪問 context.GetAllBooks() ,這時候,GetAllBooks 方法確實被調用了,是你的代碼調用的,不是EF調用;

      2、GetAllBooks 方法被你調用后,FromExpression 方法被調用;

      3、FromExpression 方法參數中,lambda 表達式雖然又引用了一次 GetAllBooks 方法,但這一次它不會被調用,EF Core 只是用來獲取方法名。

      現在明白了嗎?

      對,微軟官方文檔中的示例用的就是這種合并的方法,表面上看好像自己調用了自己,實則不會。

      好了,今天就水到這里。

       

      posted @ 2025-10-14 22:36  東邪獨孤  閱讀(1809)  評論(4)    收藏  舉報
      主站蜘蛛池模板: 日韩亚洲国产激情一区二区| 久久96热人妻偷产精品| 新源县| 色偷偷亚洲女人天堂观看| 亚洲中文字幕无码中字| 婷婷久久香蕉五月综合加勒比 | 九九热在线精品免费视频| 无码日韩精品一区二区三区免费| 男女性高爱潮免费网站| 国产精品va在线观看h| 国产精品先锋资源在线看| 中文字幕丰满乱子无码视频| 日韩无矿砖一线二线卡乱| 色爱综合另类图片av| 欧美人成精品网站播放| 亚洲欧美日本久久网站| 精品国产免费人成在线观看| 日韩黄色av一区二区三区| 欧洲美女黑人粗性暴交视频 | 国产成人午夜一区二区三区| 中文 在线 日韩 亚洲 欧美| 最近中文字幕完整版hd| 国产影片AV级毛片特别刺激| 丰满少妇内射一区| 色综合久久夜色精品国产| 久久综合激情网| 国产精品一区二区三区色| 久久久久成人片免费观看蜜芽| 国内不卡一区二区三区| 国产精品一线二线三线区| 正在播放肥臀熟妇在线视频| 国产成人片无码视频| 国产情侣激情在线对白| 202丰满熟女妇大| 亚洲热无码av一区二区东京热av| 国产熟睡乱子伦视频在线播放 | 国产伦精品一区二区三区妓女| 久久国产成人av蜜臀| 欧美人与动zozo在线播放| 成人午夜视频在线| 天堂网在线.www天堂在线资源|