以往操作 AS400 首選用 Dapper 控制,在把玩 linq2db 時無意間發現有好心人實作了 AS400 的 Provider,立馬來用用看
本文連結
開發環境
VS 2017 15.9.5
.NET Framework 4.7.2
安裝 IBM i Access Client Solutions , V7R1 Service Pack SI64724,這是 AS400 的執行環境,裡面會有 IBM.Data.DB2.iSeries.dll
前置條件
安裝套件
Install-Package linq2db4iSeries
https://github.com/LinqToDB4iSeries/Linq2DB4iSeries
修改組態
裝完之後會在 app.config/web.config 加入以下內容,這裡需要調整一下,0.0.0.0-2.6.4.0 改為 2.6.0.0
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="linq2db" publicKeyToken="e41013125f9e410a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.4.0" newVersion="2.6.4.4" />
</dependentAssembly>
</assemblyBinding>
</runtime>
然後,加入以下組態設定
<configSections>
<section name="linq2db" type="LinqToDB.Configuration.LinqToDBSection, linq2db" requirePermission="false" />
</configSections>
<linq2db>
<dataProviders>
<add name="iSeriesProvider"
type="LinqToDB.DataProvider.DB2iSeries.DB2iSeriesFactory, LinqToDB.DataProvider.DB2iSeries"
default="true" />
</dataProviders>
</linq2db>
別忘了連線字串
<connectionStrings>
<add name="MemberDb"
connectionString="Data Source= your server;User ID=your id;Password=your password;Default Collection=YAO01"
providerName="iSeriesProvider" />
</connectionStrings>
建立AS400資料表
DROP YAO01.MEMBER
CREATE TABLE YAO01.MEMBER
(
ID INTEGER NOT NULL,
NAME VARCHAR(100) NOT NULL,
AGE INTEGER NOT NULL,
REMARK VARCHAR(300)
);
ALTER TABLE YAO01.MEMBER
ADD CONSTRAINT MEMBER_PK
PRIMARY KEY (ID);
COMMIT;
DROP YAO01.IDENTITY
CREATE TABLE YAO01.IDENTITY
(
MEMBER_ID INTEGER NOT NULL,
ACCOUNT VARCHAR(100) NOT NULL,
PASSWORD VARCHAR(100) NOT NULL,
REMARK VARCHAR(300) NOT NULL
);
ALTER TABLE YAO01.IDENTITY
ADD CONSTRAINT IDENTITY_PK
PRIMARY KEY (MEMBER_ID);
COMMIT;
建立 POCO
因為還沒有找到相關的工具可以幫助產生 POCO,所以得手動建立,我定義了兩個類別,這兩個類別的關係是一對一
/// <summary>
/// </summary>
[Table("MEMBER")]
public class Member
{
/// <summary>
/// ID
/// </summary>
[PrimaryKey]
[Column]
public int ID { get; set; }
/// <summary>
/// NAME
/// </summary>
[Column]
public string NAME { get; set; }
/// <summary>
/// AGE
/// </summary>
[Column]
public int AGE { get; set; }
/// <summary>
/// REMARK
/// </summary>
[Column]
[Nullable]
public string REMARK { get; set; }
[Association(ThisKey = "ID", OtherKey = "MEMBER_ID", CanBeNull = true)]
public Identity IDENTITY { get; set; }
}
/// <summary>
/// </summary>
[Table("IDENTITY")]
public class Identity
{
/// <summary>
/// MEMBER_ID
/// </summary>
[Column]
[PrimaryKey]
public int MEMBER_ID { get; set; }
/// <summary>
/// ACCOUNT
/// </summary>
[Column]
public string ACCOUNT { get; set; }
/// <summary>
/// PASSWORD
/// </summary>
[Column]
public string PASSWORD { get; set; }
/// <summary>
/// REMARK
/// </summary>
[Column]
[Nullable]
public string REMARK { get; set; }
[Association(ThisKey = "MEMBER_ID", OtherKey = "ID", CanBeNull = false)]
public Member MEMBER { get; set; }
}
這裡跟 EF 很像,要有一個連線物件來管理資料表
public class MemberDb : DataConnection { public MemberDb() : base("MemberDb") { } public ITable<Member> Members { get { return this.GetTable<Member>(); } } public ITable<Identity> Identities { get { return this.GetTable<Identity>(); } } }
還原資料表
在測試開始之前,結束之後復原 DB,我這裡使用 REMARK 欄位來當成過濾條件
[TestInitialize]
public void Before()
{
TestHook.Delete();
}
[TestCleanup]
public void After()
{
TestHook.Delete();
}
只要是塞 TestData 的內容我就把他幹掉
[TestClass]
public class TestHook
{
public static readonly string TestData = "出發吧,跟我一起進入偉大的航道";
public static void Delete()
{
using (var db = new MemberDb())
{
db.BeginTransaction();
try
{
db.Members.Where(p => p.REMARK == TestData).Delete();
db.Identities.Where(p => p.REMARK == TestData).Delete();
db.CommitTransaction();
}
catch (Exception e)
{
Console.WriteLine(db.LastQuery);
Console.WriteLine(e);
db.RollbackTransaction();
throw;
}
}
}
}
原本是打算組合 Delete 命令一次刪多個資料表,但是一直不成功,只好一筆一筆刪
實作
- 我的驗證方式只是判斷資料是不是異動成功,記得到了真正開發的時候,要根據你的情境驗證欄位資料
- 使用 db.LastQuery 觀察渲染出來的語法
新增
[TestMethod]
public void 新增()
{
var memberToDb = new Member
{
ID = 1,
NAME = "yao",
AGE = 20,
REMARK = TestHook.TestData
};
using (var db = new MemberDb())
{
var insertCount = db.Insert(memberToDb);
Console.WriteLine(db.LastQuery);
Assert.AreEqual(1, insertCount);
}
}
修改
以下的案例都要先插一筆我可以預期的資料,測完之後,刪除他
[TestMethod]
public void 修改一整筆()
{
var fromDb = this.Insert();
var updateDb = new Member
{
ID = fromDb.ID,
NAME = "yao1",
REMARK = TestHook.TestData
};
using (var db = new MemberDb())
{
var updateCount = db.Update(updateDb);
Console.WriteLine(db.LastQuery);
Assert.AreEqual(1, updateCount);
}
}
[TestMethod]
public void 修改部分()
{
var fromDb = this.Insert();
var name = "yao2";
using (var db = new MemberDb())
{
var updateCount = db.Members
.Where(p => p.ID == fromDb.ID)
.Set(p => p.NAME, name)
.Update();
Console.WriteLine(db.LastQuery);
Assert.AreEqual(1, updateCount);
}
}
Join
查詢的案例兩張表各別新增 10 筆,才進入查詢
- 能在 Select 使用導覽屬性是因為有在兩張表定義關聯
- 尾巴 Join 頭,渲染的語法是用 InnerJoin,尾巴有資料頭一定有
- 不想用導覽的方式,也可以自己寫 Join
[TestMethod]
public void 查詢IDENTITY_InnerJoin()
{
this.Inserts();
using (var db = new MemberDb())
{
var filter = db.Identities
.Select(p => new
{
p.MEMBER.ID,
p.MEMBER.AGE,
p.MEMBER.NAME,
p.PASSWORD,
p.ACCOUNT
})
.Where(p => p.ID > 0)
.ToList()
;
Console.WriteLine(db.LastQuery);
}
}
頭 Join 尾巴,渲染的語法是用 OuterJoin,頭有資料,尾巴不一定有
[TestMethod]
public void 查詢MEMBER_OuterJoin()
{
this.Inserts();
using (var db = new MemberDb())
{
var filter = db.Members
.Select(p => new
{
p.ID,
p.AGE,
p.NAME,
p.IDENTITY.PASSWORD,
p.IDENTITY.ACCOUNT
})
.Where(p => p.ID > 0)
.ToList()
;
Console.WriteLine(db.LastQuery);
}
}
交易
linq2db 也有實作自己的交易,使用起來也是相當容易
這看起來很簡單的功能,但我今天第一次成功
[TestMethod]
public void 交易()
{
this.Inserts();
var memberToDb = new Member
{
ID = 1,
NAME = "yao",
AGE = 20,
REMARK = TestHook.TestData
};
using (var db = new MemberDb())
{
db.BeginTransaction();
try
{
db.Insert(memberToDb);
throw new Exception();
db.CommitTransaction();
}
catch (Exception e)
{
db.RollbackTransaction();
}
}
}
要能使用交易需要有 log(journal),當使用CREATE SCHEMA 會有 log
上圖出自
https://github.com/LinqToDB4iSeries/Linq2DB4iSeries
專案位置
https://github.com/yaochangyu/sample.dotblog/tree/master/ORM/Linq2Db/Lab.As400.CRUD1
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET