Hi all, 今天第九天 今天就來讓專案可以 Insert Block吧!
那由於主要內容都是與SQL做互動,所以我們的code 主要也會動在 repository 層的 InsertBlock(BlockDomain)
。
Repository
首先我們必須在 Repository 注入一個與資料庫連線的類別 DbContext
,code 如下
public class ChainRepository(BlockchainDbContext blockchainDbContext): IChainRepository { private DbSet<Block> _blocks = blockchainDbContext.Blocks;
public async Task InsertBlock(BlockDomain newBlockDomain) { await _blocks.AddAsync(newBlockDomain.ToEntity()); await blockchainDbContext.SaveChangesAsync(); } }
|
這樣一來,我們就可以成功在 Table裡頭新增資料了。但還有個問題,那就是在 Service 層的新增區塊的流程 code 如下
public async Task<BlockDomain> GenerateNewBlock(GenerateNewBlockDto dto) { var firstBlock = chainRepository.GetBlockBy(0); var newBlock = firstBlock.GenerateNextBlock(dto, Nonce); await chainRepository.InsertBlock(newBlock); return newBlock; }
|
Update Service Code
可以看到我們在計算 newBlock
是透過 id 為 0 的區塊計算出來的,但這不是我們期望的,我們希望的是取得目前鏈上的最後一塊區塊對吧?
所以我們必須要來小修改下,當然這邊也會是由 TDD 出發,code 如下:
Unit Test Code
[Test] public async Task should_not_request_block_when_the_length_is_0() { _chainRepository!.GetChainLength().Returns(0); await _chainService.GenerateNewBlock(new GenerateNewBlockDto() { Data = "new", TimeStamp = DateTime.Now });
_chainRepository.DidNotReceive().GetBlockBy(Arg.Any<int>()); await _chainRepository.Received().InsertBlock(Arg.Is<BlockDomain>(b => b.Data == "Genesis Block")); }
|
Production Code
public async Task<BlockDomain> GenerateNewBlock(GenerateNewBlockDto dto) { if (await chainRepository.GetChainLength() == 0) { var genesisBlock = new BlockDomain { Data = "Genesis Block", Hash = "0", PreviousHash = "0", TimeStamp = DateTime.Now, Nonce = 0 }; await chainRepository.InsertBlock(genesisBlock); return genesisBlock; } var firstBlock = chainRepository.GetBlockBy(0); var newBlock = firstBlock.GenerateNextBlock(dto, Nonce); await chainRepository.InsertBlock(newBlock); return newBlock; }
|
接著我們還可以再寫下個 test case → 鏈長度不為 0的時候
Unit Test Code
[Test] public async Task should_generate_new_block_base_on_latest_block() { _chainRepository!.GetChainLength().Returns(1);
GivenBlock(new BlockDomain { Hash = "123", });
var newBlock = await _chainService.GenerateNewBlock(new GenerateNewBlockDto() { Data = "new", TimeStamp = DateTime.Now });
_chainRepository.Received().GetBlockBy(Arg.Is<int>(i => i==1-1)); newBlock.PreviousHash.Should().Be("123"); }
|
Production Code
public async Task<BlockDomain> GenerateNewBlock(GenerateNewBlockDto dto) { var chainLength = await chainRepository.GetChainLength();
if (chainLength == 0) { var genesisBlock = new BlockDomain { Data = "Genesis Block", Hash = "0", PreviousHash = "0", TimeStamp = DateTime.Now, Nonce = 0 }; await chainRepository.InsertBlock(genesisBlock); return genesisBlock; } var firstBlock = chainRepository.GetBlockBy(chainLength-1); var newBlock = firstBlock.GenerateNextBlock(dto, Nonce); await chainRepository.InsertBlock(newBlock); return newBlock;
|
該有的邏輯都完成了,就來重構下吧!
Refactor Code
public async Task<BlockDomain> GenerateNewBlock(GenerateNewBlockDto dto) { var chainLength = await chainRepository.GetChainLength(); var newBlock = chainLength == 0 ? new BlockDomain { Data = "Genesis Block", Hash = "0", PreviousHash = "0", TimeStamp = DateTime.Now, Nonce = 0 } : chainRepository.GetBlockBy(chainLength - 1).GenerateNextBlock(dto, Nonce);
await chainRepository.InsertBlock(newBlock); return newBlock; }
|
以上我們就完成了,我們的新需求:基於上一個區塊的 hash值進行計算。
題外話:Get Block By
如果記憶力好的話,我們還有個 GetBlockBy
還沒有實作,這就來實作個,code 如下
public async Task<BlockDomain> GetBlockBy(int id) { var block = await _blocks.FirstAsync(x=>x.Id == id); return block.ToDomain(); }
|
E2e Test
如此一來,系統就可以好好運行啦,來個 E2E Test下
First Block
Second Block
Conclusion
總算是打通與DB的連線了~~~ 接著我們就可以往可編輯 的方向走了,但這是明天的事情 ,明天再說**
結語:明天上班 🫠🫠🫠🫠🫠