Hi all, 今天第九天 今天就來讓專案可以 Insert Block吧!
那由於主要內容都是與SQL做互動,所以我們的code 主要也會動在 repository 層的 InsertBlock(BlockDomain)
。
首先我們必須在 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;
}
可以看到我們在計算 newBlock
是透過 id 為 0 的區塊計算出來的,但這不是我們期望的,我們希望的是取得目前鏈上的最後一塊區塊對吧? 所以我們必須要來小修改下,當然這邊也會是由 TDD 出發,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"));
}
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的時候
[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");
}
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;
該有的邏輯都完成了,就來重構下吧!
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值進行計算。
如果記憶力好的話,我們還有個 GetBlockBy
還沒有實作,這就來實作個,code 如下
public async Task<BlockDomain> GetBlockBy(int id)
{
var block = await _blocks.FirstAsync(x=>x.Id == id);
return block.ToDomain();
}
如此一來,系統就可以好好運行啦,來個 E2E Test下
總算是打通與DB的連線了~~~ 接著我們就可以往可編輯 的方向走了,但這是明天的事情 ,明天再說**
結語:明天上班 🫠🫠🫠🫠🫠