重新檢討架構規畫

昨天興高采烈地試著使用 Scaffolding 自動產生 Web API 控制器的動作方法,但是卻發生了如下圖所示的錯誤:

看來是將 Domain Model 與 DbContext 分開成兩個專案所造成的,解決的方法就是重改架構將這兩個專案合併成一個,否則就是自己動手寫。幾經思量之後決定修改架構,所以今天就來試看看怎麼改。

模仿是為了創新

動手開始改之前還是參考一下別人是怎麼做的,最後改成如下圖所示的架構:

將 Demae.Domain 與 Demae.Data 兩個專案,以一個 Demae.Core 取代,原先寫在 Demae.Domain 的檔案改成寫在 Entities 資料夾底下,而寫在 Demae.Data 的檔案改成寫在 Data 資料夾底下,其他的都一樣。在這裡就沒有一步一步地再示範如何寫了,建議讀者可以回朔參考先前的文章,順便也做個複習。

註:
阿源哥哥上課時,很喜歡跟學生講《模仿是為了創新》這句話,當不知道該如何做時,參考一下別人如何做的,多抄(模仿)幾次就會有自己的構想(創新)了。

新增控制器

接著如圖所示順序,使用選用【使用 Entity Framework 執行動作的 API 控制器】,讓開發工具為我們產生所有的程式碼:

成功了,產生一堆程式碼了:


[Produces("application/json")]
[Route("api/Addresses")]
public class AddressesController : Controller
{
    private readonly DemaeContext _context;

    public AddressesController(DemaeContext context)
    {
        _context = context;
    }

    // GET: api/Addresses
    [HttpGet]
    public IEnumerable<Address> GetAddresses()
    {
        return _context.Addresses;
    }

    // GET: api/Addresses/5
    [HttpGet("{id}")]
    public async Task<IActionResult> GetAddress([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var address = await _context.Addresses.SingleOrDefaultAsync(m => m.Id == id);

        if (address == null)
        {
            return NotFound();
        }

        return Ok(address);
    }

    // PUT: api/Addresses/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutAddress([FromRoute] int id, [FromBody] Address address)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != address.Id)
        {
            return BadRequest();
        }

        _context.Entry(address).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!AddressExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return NoContent();
    }

    // POST: api/Addresses
    [HttpPost]
    public async Task<IActionResult> PostAddress([FromBody] Address address)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Addresses.Add(address);
        await _context.SaveChangesAsync();

        return CreatedAtAction("GetAddress", new { id = address.Id }, address);
    }

    // DELETE: api/Addresses/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteAddress([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var address = await _context.Addresses.SingleOrDefaultAsync(m => m.Id == id);
        if (address == null)
        {
            return NotFound();
        }

        _context.Addresses.Remove(address);
        await _context.SaveChangesAsync();

        return Ok(address);
    }

    private bool AddressExists(int id)
    {
        return _context.Addresses.Any(e => e.Id == id);
    }
}

好吧!今天暫時先做到這裡,明天再來逐步分析這些程式碼,並實際測試新增、修改、刪除、查詢地址資料的動作。

註:
阿源哥哥向來是個能少打一些程式碼就盡量少打,如果有工具,有範本、有程式碼片段,可以幫助減少敲程式碼的次數的話,都會盡量使用。但是這些工具所產生出來的程式碼有時也不見得百分之百合用,所以徹底了解工具所產生的程式碼,有助於日後的改良。