C#.NET ORM FreeSql 讀取使用 US7ASCII 的 Oracle 資料庫中文顯示亂碼問題


💻 前言

關於 Oracle US7ASCII 中文亂碼的問題,Ado.Net 和 Odbc 無法解決。包括最新的.Net Core、.NET6、.NET7 都無法解決這個問題。

FreeSql 對 Oracle 支援非常友好,是 c#.net ORM 不二之選,提供了 Ado.net 實現包 FreeSql.Provider.Oracle,Odbc 實現包 FreeSql.Provider.Odbc,Oledb 實現包 FreeSql.Provider.OracleOledb,他們都支援 .NETCore2.1+、.NET4.0+ 等最新或較低的 .NETFramework 版本。

FreeSql 訪問 Oracle 只需要引用 FreeSql.Provider.Oracle/FreeSql.Provider.Odbc/FreeSql.Provider.OracleOledb 任意一個包即可。

若想以 Ado.net 驅動訪問資料庫,請安裝:

dotnet add package FreeSql.Provider.Oracle

若想以 ODBC 驅動訪問資料庫,請安裝:

dotnet add package FreeSql.Provider.Odbc

若想以 Oledb 驅動訪問資料庫,請安裝:

dotnet add package FreeSql.Provider.OracleOledb


🌳 解決辦法

安裝 FreeSql.Provider.OracleOledb 使用 Oledb 驅動解決讀取使用 US7ASCII 的 Oracle 資料庫中文顯示亂碼問題。

有關 US7ASCII Oracle 參考了其他解決方法:

C#處理讀取使用US7ASCII的oracle資料庫中文顯示亂碼問題
//blog.csdn.net/guhun_shmily/article/details/83064225
public class DB
{
    static Lazy<IFreeSql> oracleLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder()
        .UseConnectionString(FreeSql.DataType.Oracle, "Provider=OraOLEDB.Oracle;user id=9user;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=2")
        //.UseConnectionString(FreeSql.DataType.OdbcOracle, "...") //ODBC 特定類型
        .UseAutoSyncStructure(true)
        .UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper)
        .UseNoneCommandParameter(true)
        .UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText))
        .Build());
    public static IFreeSql oracle => oracleLazy.Value;
}

定義 DB.cs 類之後就可以快樂的 CRUD 了。FreeSql 提供多種 CRUD 使用習慣,請根據實際情況選擇團隊合適的一種:

  • 要麼 FreeSql,原始用法;
  • 要麼 FreeSql.Repository,倉儲+工作單元習慣;
  • 要麼 FreeSql.DbContext,很像 EFCore 的使用習慣;
  • 要麼 FreeSql.BaseEntity,充血模式;
  • 要麼 直接像 dapper 那樣使用 OracleConnection 擴展方法;

🦄 CRUD 模式一:原始用法 API

DB.oracle.Select<T>(); //查詢
DB.oracle.Insert<T>(); //插入
DB.oracle.Update<T>(); //更新
DB.oracle.Delete<T>(); //刪除
DB.oracle.InsertOrUpdate<T>()// 插入或更新
DB.oracle.Transaction(..); //事務

DB.oracle.CodeFirst; //CodeFirst 對象
DB.oracle.DbFirst; //DbFirst 對象
DB.oracle.Ado; //Ado 對象
DB.oracle.Aop; //Aop 對象
DB.oracle.GlobalFilter; //全局過濾器對象


var blogs = DB.Oracle.Select<Blog>()
    .Where(b => b.Rating > 3)
    .OrderBy(b => b.Url)
    .Page(2, 10)
    .ToList();

var blog = new Blog { Url = "//sample.com" };
blog.BlogId = (int)DB.Oracle.Insert(blog).ExecuteIdentity();

DB.Oracle.Update<Blog>()
    .Set(b => b.Url, "//sample2222.com")
    .Where(b => b.Url == "//sample.com")
    .ExecuteAffrows();

DB.Oracle.Delete<Blog>()
    .Where(b => b.Url == "//sample.com")
    .ExecuteAffrows();

// 等等等。。

⛳ CRUD 模式二:倉儲 + 工作單元

FreeSql.Repository 作為擴展,實現了通用倉儲層功能。與其他規範標準一樣,倉儲層也有相應的規範定義。FreeSql.Repository 參考 abp vnext 介面,定義和實現基礎的倉儲層(CURD),應該算比較通用的方法吧。

  • Select/Attach 快照對象,Update 只更新變化的欄位;
  • Insert 插入數據,適配各資料庫優化執行 ExecuteAffrows/ExecuteIdentity/ExecuteInserted;
  • InsertOrUpdate 插入或更新;
  • SaveMany 方法快速保存導航對象(一對多、多對多);
  • 工作單元管理事務
public class SongService
{
    readonly IBaseRepository<Song> _repoSong;
    readonly IBaseRepository<Detail> _repoDetail;

    public SongService(IBaseRepository<Song> repoSong, IBaseRepository<Detail> repoDetail)
    {
        _repoSong = repoSong;
        _repoDetail = repoDetail;
    }

    [Transactional]
    public virtual void Test1()
    {
        //這裡 _repoSong、_repoDetail 所有操作都是一個工作單元
        this.Test2();
    }

    [Transactional(Propagation = Propagation.Nested)]
    public virtual void Test2() //嵌套事務
    {
        //這裡 _repoSong、_repoDetail 所有操作都是一個工作單元
    }
}
屬性 返回值 說明
EntityType Type 倉儲正在操作的實體類型,注意它不一定是 TEntity
UnitOfWork IUnitOfWork 正在使用的工作單元
Orm IFreeSql 正在使用的 Orm
DbContextOptions DbContextOptions 正在使用的 DbContext 設置,修改設置不影響其他
DataFilter IDataFilter<TEntity> 倉儲過濾器,本對象內生效
UpdateDiy IUpdate<TEntity> 準備更新數據,與倉儲同事務
Select ISelect<TEntity> 準備查詢數據
方法 返回值 參數 說明
AsType void Type 改變倉儲正在操作的實體類型
Get TEntity TKey 根據主鍵,查詢數據
Find TEntity TKey 根據主鍵,查詢數據
Delete int TKey 根據主鍵刪除數據
Delete int Lambda 根據 lambda 條件刪除數據
Delete int TEntity 刪除數據
Delete int IEnumerable<TEntity> 批量刪除數據
DeleteCascadeByDatabase List<object> Lambda 根據導航屬性遞歸資料庫刪除數據
Insert TEntity 插入數據,若實體有自增列,插入後的自增值會填充到實體中
Insert IEnumerable<TEntity> 批量插入數據
Update TEntity 更新數據
Update IEnumerable<TEntity> 批量更新數據
InsertOrUpdate TEntity 插入或更新數據
FlushState 清除狀態管理數據
Attach TEntity 附加實體到狀態管理,可用於不查詢就更新或刪除
Attach IEnumerable<TEntity> 批量附加實體到狀態管理
AttachOnlyPrimary TEntity 只附加實體的主鍵數據到狀態管理
SaveMany TEntity, string 保存實體的指定 ManyToMany/OneToMany 導航屬性(完整對比)
BeginEdit List<TEntity> 準備編輯一個 List 實體
EndEdit int 完成編輯數據,進行保存動作

狀態管理,可實現 Update 只更新變化的欄位(不更新所有欄位),靈活使用 Attach 和 Update 用起來非常舒服。


⚡ CRUD 模式三:DbContext

FreeSql.DbContext 實現類似 EFCore 使用習慣,跟蹤對象狀態,最終通過 SaveChanges 方法提交事務。

FreeSql 可自動識別 EFCore 實體特性 Key/Required/NotMapped/MaxLength/StringLength/DatabaseGenerated/Table/Column。

  • Select/Attach 快照對象,Update 只更新變化的欄位;
  • Add/AddRange 插入數據,適配各資料庫優化執行 ExecuteAffrows/ExecuteIdentity/ExecuteInserted;
  • AddOrUpdate 插入或更新;
  • SaveMany 方法快速保存導航對象(一對多、多對多);
using (var ctx = DB.oracle.CreateDbContext()) {
  //var db1 = ctx.Set<Song>();
  //var db2 = ctx.Set<Tag>();

  var item = new Song { };
  ctx.Add(item);
  ctx.SaveChanges();
}

// 或者

```csharp
public class SongContext : DbContext {

  public DbSet<Song> Songs { get; set; }
  public DbSet<Tag> Tags { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder builder) {
    builder.UseFreeSql(DB.oracle);
  }
  
  //每個 DbContext 只觸發一次
  protected override void OnModelCreating(ICodeFirst codefirst)
  {
    codefirst.Entity<Song>(eb =>
    {
      eb.ToTable("tb_song");
      eb.Ignore(a => a.Field1);
      eb.Property(a => a.Title).HasColumnType("varchar(50)").IsRequired();
      eb.Property(a => a.Url).HasMaxLength(100);
    }
  }
}

🌴 CRUD 模式四:BaseEntity

BaseEntity 是一種極簡單的 CodeFirst 開發方式,特別對單表或多表CRUD,利用繼承節省了每個實體類的重複屬性(創建時間、ID等欄位),軟體刪除等功能,進行 crud 操作時不必時常考慮倉儲的使用;

dotnet add package FreeSql.Extensions.BaseEntity

public class UserGroup : BaseEntity<UserGroup, int>
{
    public string GroupName { get; set; }
}

//添加
var item = new UserGroup { GroupName = "組一" };
item.Insert();

//更新
item.GroupName = "組二";
item.Update();

//添加或更新
item.Save();

//軟刪除
item.Delete();

//恢復軟刪除
item.Restore();

//根據主鍵獲取對象
var item = UserGroup.Find(1);

//查詢數據
var items = UserGroup.Where(a => a.Id > 10).ToList();

📃 CRUD 模式五:OracleConnection 擴展方法(類似 Dapper)

提供了類似 Dapper 的使用方法,FreeSql 增加了 IDbConnection/IDbTransaction 對象的擴展方法 Select/Insert/Update/Delete 實現 CRUD。

using FreeSql;

using (var conn = new OracleConnection(...))
{
  conn.Select<T>().Where(...).ToList();

  conn.Insert(new T {}).ExecuteAffrows();
  conn.Update().SetSource(new T {}).ExecuteAffrows();
  conn.InsertOrUpdate().SetSource(new T {}).ExecuteAffrows();

  conn.Delete<T>().Where(...).ExecuteAffrows();
}
  • 每個 OracleConnection GetFreeSql() 返回的 IFreeSql 實例相同;
  • 可以對 fsql 設置 Aop 事件,比如監視 SQL;
  • IFreeSql 自身的成員 IDbFirst、Transaction 不可用;

利用本功能可以快速將 FreeSql 使用到項目中,只需要處理好實體類的特性。

提示:FreeSql 兼容 EFCore 99% 的實體特性


🌈 功能特性

  • 支援 CodeFirst 模式;
  • 支援 DbFirst 模式,支援從資料庫導入實體類,或使用實體類生成工具生成實體類;
  • 支援 豐富的表達式函數,以及靈活的自定義解析;
  • 支援 導航屬性一對多、多對多貪婪載入,延時載入,級聯保存,級聯刪除;
  • 支援 讀寫分離、分表分庫、過濾器、樂觀鎖、悲觀鎖、分散式事務、多租戶(按欄位/表/庫);
  • 支援 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/達夢/人大金倉/神舟通用/南大通用/翰高/華為高斯/ClickHouse/Access 等資料庫;

FreeSql .NET ORM 支援 .NetFramework4.0+、.NetCore、Xamarin、MAUI、Blazor、以及還有說不出來的運行平台,因為程式碼綠色無依賴,支援新平台非常簡單。目前單元測試數量:8500+,Nuget下載數量:1M+。QQ群:4336577(已滿)、8578575(在線)、52508226(在線)

FreeSql 使用最寬鬆的開源協議 MIT //github.com/dotnetcore/FreeSql ,可以商用,文檔齊全,甚至拿去賣錢也可以。

8500+個單元測試作為基調,支援10多數資料庫,我們提供了通用Odbc理論上支援所有資料庫,目前已知有群友使用 FreeSql 操作華為高斯、mycat、tidb 等資料庫。安裝時只需要選擇對應的資料庫實現包。

輕量化解釋:了解 FreeRedis、FreeSql、csredis 的人都知道,我們發布的開源項目是綠色著稱,零依賴發布後只有一個DLL,不會造成使用者項目依賴衝突,支援 .NET 4.0 堪稱屎山項目的救星。現在還有很多.NET FX4.0 的項目,這些項目因歷史遺留原因或硬體限制,不能更換 .NET Core 版本。因此這些項目很難使用到現有的開源庫,不能使用可靠的開源庫,那麼很多時候都要自行實現,在堆積程式碼的同時,項目也有可能越來越亂,程式碼越來越渣,項目逐漸變得不穩定。


🏁 結束語

有關 US7ASCII Oracle 參考了其他解決方法:

C#處理讀取使用US7ASCII的oracle資料庫中文顯示亂碼問題
//blog.csdn.net/guhun_shmily/article/details/83064225