主頁 > .NET開發 > EF7創建模型入門篇

EF7創建模型入門篇

2023-03-07 09:30:04 .NET開發

在EF7中,創建一個模型是非常重要的步驟,本文將使用微軟官方檔案中的指南,來學習EF7中的創建模型篇,外加一點點個人理解,

物體型別

在 EF7 中,你需要使用 modelBuilder.Entity() 方法來告訴 EF7 你要包含哪些型別,默認情況下,EF7 會將物體型別的名稱設定為表的名稱,但是,你可以使用 ToTable() 方法來覆寫默認行為,
如果你的資料庫中有多個模式(schema),你可以使用 ToTable() 方法的另一個多載版本來指定表所屬的架構,如果你想要為生成的表添加注釋,可以使用 HasComment() 方法,如果你不想將某個類映射到資料庫中的表,我們可以使用 modelBuilder.Entity().Ignore() 來排除它,

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
		.ToTable("Blogs", schema: "dbo") //.ToTable("Blogs");
		.HasComment("This table contains blog posts.");
	modelBuilder.Ignore<Address>();
}

共享型別物體型別

在 EF7 中,你可以將一個型別映射到多個表中,這種情況通常發生在你有一組具有相似屬性的型別,這些屬性在不同的表中都需要使用,在這種情況下,你可以使用 ModelBuilder.SharedTypeEntity() 方法來創建一個物體型別,并將其映射到多個表中,

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var addressEntity = modelBuilder.SharedTypeEntity<Address>("Address");
    addressEntity.ToTable("CustomerBillingAddresses");
    addressEntity.ToTable("CustomerShippingAddresses");

    modelBuilder.Entity<Customer>()
        .OwnsOne(c => c.BillingAddress, b =>
        {
            b.WithOwner().HasForeignKey("BillingAddressId");
            b.ToTable("CustomerBillingAddresses");
        });

    modelBuilder.Entity<Customer>()
        .OwnsOne(c => c.ShippingAddress, b =>
        {
            b.WithOwner().HasForeignKey("ShippingAddressId");
            b.ToTable("CustomerShippingAddresses");
        });
}

在上面的代碼中,我們首先使用 ModelBuilder.SharedTypeEntity() 方法創建一個名為 Address 的物體型別,然后,我們使用 ToTable() 方法將該物體型別映射到多個表中,接下來,我們使用 OwnsOne() 方法來將 BillingAddressShippingAddress 屬性映射到具有相應名稱的表中,注意,我們還使用了 HasForeignKey() 方法來指定外鍵的名稱,
使用共享型別物體型別可以使你的代碼更加簡潔,并提高可維護性,通過使用共享型別物體型別,你可以將一個型別映射到多個表中,而不必在每個物體型別中都定義相同的映射代碼,

物體屬性

如果要排除物體屬性,可以使用Ignore()方法,

modelBuilder.Entity<Person>().Ignore(p => p.Age)

定義列名,例如,下面的代碼將為Person類中的LastName屬性定義列名,

modelBuilder.Entity<Person>().Property(p => p.LastName).HasColumnName("Last_Name")

定義列注釋,例如,下面的代碼將為Person類中的LastName屬性定義注釋,

modelBuilder.Entity<Person>().Property(p => p.LastName).HasComment("The last name of the person")

定義列排序規則,例如,下面的代碼將為Person類中的LastName屬性定義排序規則,

modelBuilder.Entity<Person>().Property(p => p.LastName).UseCollation("SQL_Latin1_General_CP1_CI_AS");

定義列的資料型別(和資料庫一致即可),例如,下面的代碼將為Person類中的Age屬性定義為int型別:

modelBuilder.Entity<Person>().Property(p => p.Age).HasColumnType("int");

定義列的最大長度,例如,下面的代碼將為Person類中的FirstName屬性定義為50個字符的最大長度:

modelBuilder.Entity<Person>().Property(p => p.FirstName).HasMaxLength(50);

定義列的精度和小數位數,例如,下面的代碼將為Person類中的Height屬性定義為2位小數的精度:

modelBuilder.Entity<Person>().Property(p => p.Height).HasPrecision(5, 2);

定義是否為必需或可選屬性,例如,下面的代碼將為Person類中的FirstName屬性定義為必需屬性:

modelBuilder.Entity<Person>().Property(p => p.FirstName).IsRequired();

定義列在表中的順序,例如,下面的代碼將為Person類中的FirstName屬性定義為表中第二個列:

modelBuilder.Entity<Person>().Property(p => p.Id).HasColumnOrder(1);
modelBuilder.Entity<Person>().Property(p => p.FirstName).HasColumnOrder(2);

主鍵

定義主鍵,根據約定,名為 Id 或 <型別名稱>Id 的屬性將被配置為物體的主鍵,

internal class User
{
    public string Id { get; set; } // 主鍵
    public string Name { get; set; }
}

如果我們不想使用默認約定規則,可以自定義規則,下面的代碼將指定User類的Id屬性作為主鍵并且重寫設定主鍵的名稱:

modelBuilder.Entity<Person>().HasKey(p => p.Id).HasName("UserId");
注意主鍵Id應是有序的

在 MySQL 中,主鍵 Id 不是有順序的時候,可能會導致新增性能下降的原因是,MySQL 默認使用 B-tree 索引來實作主鍵索引,如果主鍵 Id 是無序的,那么在插入資料時,MySQL 需要不斷尋找合適的位置來插入新資料,這可能會導致 B-tree 索引不斷被調整,從而影響插入性能,相反,如果主鍵 Id 是有序的,MySQL 可以更快速地找到要插入的位置,從而提高插入性能,

注意主鍵Id應該是最后生成的

有些程式可能會有延遲,導致資料庫插入是非有序的,場景:假如我們先生成Id再處理業務邏輯,有兩個執行緒,同時并發請求,第一個執行緒生成好Id 是 3,處理下面業務邏輯時發生了大概五毫秒的延遲,第二個執行緒也生成好了Id是 4,處理下面業務邏輯時沒有延遲,就通過了,所以第二個執行緒,先進資料庫插入Id為4,第一個執行緒因為有延遲來慢了一步,插入的Id是3,資料庫Id就變成無序的了,

使用基于時間戳的有序Guid作為主鍵

對于非復合數字和 GUID 主鍵,EF Core 根據約定設定值生成,

EF7中 Guid 是基于時間的演算法精確到納秒,因為一毫秒等于一百萬納秒,所以EF7的Guid一毫秒可以產生一百萬的Id,
優點:不可預測、有序(添加性能高)、在支持Guid(uuid)的資料庫中(查詢性能高)、開箱即用,
缺點:(這是可以忽略不計的事情)并發中在同一納秒內,產生的Id是會重復的,有時鐘回撥問題,太長,
EF7中的Guid有序演算法比雪花演算法更好,

  1. 雪花演算法在并發時,也會重復,因為序列號和時間戳,即使我們配置正確了WorkId,不信你可以寫個例子,思路是:10個并發同時生成Id,保存到安全執行緒字典中,重復就報個錯,
  2. 雪花演算法需要額外維護WorkId的作業,
  3. 有時鐘回撥問題,

Guid生成原始碼地址:MySqlSequentialGuidValueGenerator.cs SqlServerSequentialGuidValueGenerator.cs
使用方式:EF7中:創建模型,生成的值,(***后面會繼續講到)

復合鍵

復合鍵是指將多個列作為主鍵的一種設計模式,這些列在組合起來時才能唯一標識一條記錄,相對于單一鍵,復合鍵更加靈活,可以更準確地描述物體之間的關系,例如,在一個訂單系統中,一個訂單可能由多個產品組成,此時可以使用復合鍵來標識訂單編號和產品編號的組合,以確保每個訂單中的每個產品都是唯一的,
使用復合鍵的優點主要有兩點,首先,它提供了更準確的資料描述,特別是在處理多對多關系時,可以更準確地表示關系的唯一性,其次,使用復合鍵可以提高查詢效率,因為復合鍵可以利用資料庫的索引機制,快速定位和訪問資料,
然而,復合鍵也存在一些缺點,首先,它增加了開發的復雜性,需要更多的設計和規劃,其次,在使用ORM框架時,如EF7,復合鍵的使用需要特殊的處理,例如在Fluent API中進行配置,最后,復合鍵在某些情況下可能會導致性能問題,例如在大型資料庫中,使用復合鍵可能會影響查詢性能,
在使用EF7時,可以通過Fluent API來配置復合鍵,以一個用戶角色表為例,可以使用以下代碼定義一個由用戶Id和角色Id的復合主鍵:

internal class UserRole
{
    public string UserId { get; set; } 
    public string RoleId { get; set; }
}
public class MyContext : DbContext
{
    public DbSet<Person> People { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserRole>().HasKey(c => new { c.UserId, c.RoleId });
    }
}

在這個示例中,使用Fluent API中的HasKey方法來定義復合主鍵,由于復合鍵是由多個屬性組成的,因此需要將它們放在一個匿名型別中作為引數傳遞給HasKey方法,

注意合理的復合鍵會提高性能,

MySql為例:當使用復合鍵并且確保關聯資料都已經存在時,插入新資料時可能不會對性能產生太大影響,這是因為在 MySQL 中,使用復合鍵時,MySQL 會同時使用所有列生成 B-tree 索引,從而提高查詢和插入的性能,在插入資料時,如果已經確保了關聯資料的存在,那么 MySQL 可以更快速地插入新資料并生成新的索引,從而提高插入性能,
但是,需要注意,如果你的表結構復雜,或者在插入資料時沒有正確使用索引,那么復合鍵仍然可能會影響插入性能,

備選鍵

為什么要使用EF7備選鍵?
在資料庫中,主鍵通常用于唯一標識和檢索物體物件,但是,有些情況下可能需要使用備選鍵來標識物體物件,例如,當主鍵不適合用作某些查詢時,使用備選鍵可以提高資料庫的性能和靈活性,
優點:

  1. 提高性能:使用備選鍵可以減少復雜查詢中的連接數量和查詢時間,從而提高資料庫的性能,
  2. 增強靈活性:備選鍵允許使用其他屬性作為查詢條件,這樣就可以更靈活地查詢資料庫中的物體物件,
  3. 減少沖突:使用備選鍵可以避免主鍵沖突的情況,尤其是在多個物體物件使用同一主鍵的情況下,

缺點:

  1. 增加復雜性:使用備選鍵會增加代碼的復雜性,因為需要額外的配置和代碼來實作備選鍵的功能,
  2. 增加維護成本:使用備選鍵會增加資料庫的維護成本,因為需要更多的索引和查詢,以及更多的代碼來處理備選鍵,
  3. 影響資料完整性:如果備選鍵沒有正確配置,可能會導致資料完整性的問題,因為重復的備選鍵可能會導致資料重復或丟失,

為什么有這些優點和缺點?
優點是因為備選鍵可以提供更靈活、更高效的查詢和更少的主鍵沖突,缺點是因為使用備選鍵需要更多的配置和代碼,并且可能會影響資料完整性,
以下是一個簡單的示例,演示如何使用備選鍵:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public class MyContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
		// 將Email屬性指定為備選鍵
        modelBuilder.Entity<Person>().HasAlternateKey(p => p.Email); 
    }
}

復合備選鍵

為什么要使用復合備選鍵?
使用復合備選鍵的一個重要原因是在某些情況下,單個列可能不足以唯一標識表中的每一行,例如,在一個電影資料庫中,如果只使用電影名稱作為主鍵,則會出現多個電影名稱相同的情況,因此,需要使用復合備選鍵,通過多個列來確定每個電影的唯一性,
優點

  1. 更加靈活的資料建模:復合備選鍵使得資料建模更加靈活,可以在表中使用多個列來確定唯一性,這使得資料建模更加符合實際情況,可以更好地支持復雜的業務場景,
  2. 更好的性能:使用復合備選鍵可以提高資料庫的性能,這是因為使用多個列來唯一標識行,可以減少在表中的掃描次數,從而提高查詢性能,
  3. 更好的資料完整性:使用復合備選鍵可以更好地保證資料完整性,在使用復合備選鍵的表中,每個行的唯一性都由多個列決定,這使得在資料插入和更新時,更難發生資料沖突和錯誤,

缺點

  1. 復雜性:使用復合備選鍵會增加表的復雜性,需要在表中定義多個列,以確定唯一性,此外,在查詢時,需要指定多個列作為條件,以獲取唯一的行,這可能會增加代碼的復雜性,需要更加謹慎地撰寫代碼,
  2. 不支持自動增長:使用復合備選鍵時,不支持自動增長功能,這是因為每個行的唯一性都是由多個列來決定的,如果一個列是自動增長的,就不能保證每一行都是唯一的,

使用方式
使用Fluent API是定義復合備選鍵的最佳方式,以下是使用Fluent API定義復合備選鍵的示例:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
}

public class PersonContext : DbContext
{
    public DbSet<Person> Persons { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
			// 將多個屬性配置為備選鍵(即復合備選鍵)
			.HasAlternateKey(p => new { p.FirstName, p.LastName, p.DateOfBirth })
			// 可配置備選鍵的索引和唯一約束的名稱:
			.HasName("FirstName_LastName_DateOfBirth");
    }
}

分組配置模型

可以使用分組配置,這樣可以將物體和關系的配置組織在一起,使代碼更具可讀性,例如:

public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder.HasKey(x => x.BlogId);
        builder.Property(x => x.Name).IsRequired();
    }
}

然后在DbContext中使用以下代碼將此配置應用于Blog物體:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfiguration(new BlogConfiguration());
}

使用資料注釋來配置模型

可以使用資料注釋來配置EF7模型,資料注釋是一種以屬性和類注釋的方式來提供元資料的方法,例如:

public class Blog
{
    [Key]
    public int BlogId { get; set; }
    [Required]
    public string Name { get; set; }
}

在這個示例中,我們使用了Key和Required注釋來指定BlogId和Name屬性的主鍵和IsRequired標志,

內置約定

除了手動配置外,EF7還提供了一些內置約定,可以根據慣例自動推斷模型,例如,如果物體中有一個名為Id的屬性,EF7會將其作為主鍵,如果物體之間具有參考關系,EF7會自動創建外鍵,

洗掉現有約定

如果不想使用內置約定,可以通過呼叫ModelBuilder.Conventions.Remove方法來洗掉它們,例如,以下代碼洗掉了為外鍵列創建索引的約定:

modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();

總結:

在本文中,介紹了如何告訴EF7使用物體型別和過濾型別,并且還說了共享物體型別,我們還介紹了物體屬性的配置,還介紹了四種鍵,介紹了使用Fluent API和資料注釋來配置EF7模型,我們還了解了EF7的內置約定,并學習了如何洗掉現有約定,使用這些技術,可以輕松地創建和配置EF7模型,并更好地管理資料庫訪問代碼,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/546010.html

標籤:Entity Framework

上一篇:MAUI Blazor Android 輸入框軟鍵盤遮擋問題

下一篇:ASP.NET Core - 配置系統之配置添加

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more