ASP.NET Web API實踐系列02,在MVC4下的一個例項, 包含EF Code First,依賴注入, Bootstrap等

Darren Ji發表於2014-10-24

本篇體驗在MVC4下,實現一個對Book資訊的管理,包括增刪查等,用到了EF Code First, 使用Unity進行依賴注入,前端使用Bootstrap美化。先上最終效果:

3


→建立一個MVC4專案,選擇Web API模版。

1

 

→在Models資料夾建立一個Book.cs類。

namespace MyMvcAndWebApi.Models
{
    public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

 

→在Models資料夾建立BookInitializer類,用來初始化資料庫表資料。

using System.Data.Entity;
namespace MyMvcAndWebApi.Models
{
    public class BookInitializer : DropCreateDatabaseIfModelChanges<BookStore>
    {
        protected override void Seed(BookStore context)
        {
            context.Books.Add(new Book() {Name = "我有一頭小毛驢", Price = 200M});
            context.Books.Add(new Book() { Name = "今天天氣真好", Price = 300M });
            context.Books.Add(new Book() { Name = "秋天是落葉的季節", Price = 500M });
        }
    }
}

 

→在Models資料夾中,建立BookStore類,派生於DbContext。

using System.Data.Entity;
namespace MyMvcAndWebApi.Models
{
    public class BookStore : DbContext
    {
        public BookStore() : base("conn")
        {
            Database.SetInitializer(new BookInitializer());
        }
       public  DbSet<Book> Books { get; set; } 
    }
}

 

→在Web.config中配置連線字串。

  <connectionStrings>
    ......
   <add name="conn" connectionString="Data Source=.;User=yourusername;Password=yourpassword;Initial Catalog=BookStore;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

 

→Repository首先需要一個介面,在Models資料夾中建立IBookRepository介面。

using System.Collections.Generic;
namespace MyMvcAndWebApi.Models
{
    public interface IBookRepository
    {
        IEnumerable<Book> GetAll();
        Book Get(int id);
        Book Add(Book book);
        void Remove(int id);
        bool Update(Book book);
    }
}

 

 

→實現IBookRepository介面,用到BookStore這個上下文。

using System.Collections.Generic;
using System.Data;
namespace MyMvcAndWebApi.Models
{
    public class BookRepository : IBookRepository
    {
        private BookStore db = new BookStore();
        public BookRepository(){}
        public IEnumerable<Book> GetAll()
        {
            return db.Books;
        }
        public Book Add(Book book)
        {
            db.Books.Add(book);
            db.SaveChanges();
            return book;
        }
        public void Remove(int id)
        {
            Book book = db.Books.Find(id);
            db.Books.Remove(book);
            db.SaveChanges();
        }
        public bool Update(Book book)
        {
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
            return true;
        }
        public Book Get(int id)
        {
            return db.Books.Find(id);
        }
    }
}

 

→有了介面和實現,接下來會用到依賴注入。選擇使用Unity,在NuGet中下載。
2

 

→首先需要一個依賴注入容器。在專案下建立Helper資料夾,在其中建立IoCContainer類。

 

using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Practices.Unity;
namespace MyMvcAndWebApi.Helper
{
    class ScopeContainer : IDependencyScope
    {
        protected IUnityContainer container;
        public ScopeContainer(IUnityContainer container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            this.container = container;
        }
        public object GetService(Type serviceType)
        {
            if (container.IsRegistered(serviceType))
            {
                return container.Resolve(serviceType);
            }
            else
            {
                return null;
            }
        }
        public IEnumerable<object> GetServices(Type serviceType)
        {
            if (container.IsRegistered(serviceType))
            {
                return container.ResolveAll(serviceType);
            }
            else
            {
                return new List<object>();
            }
        }
        public void Dispose()
        {
            container.Dispose();
        }
    }
    class IoCContainer : ScopeContainer, IDependencyResolver
    {
        public IoCContainer(IUnityContainer container)
            : base(container)
        {
        }
        public IDependencyScope BeginScope()
        {
            var child = container.CreateChildContainer();
            return new ScopeContainer(child);
        }
    }
}

 

→在Global.asax中註冊Unity。

sing System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using MyMvcAndWebApi.Controllers;
using MyMvcAndWebApi.Helper;
using MyMvcAndWebApi.Models;
namespace MyMvcAndWebApi
{
    // 注意: 有關啟用 IIS6 或 IIS7 經典模式的說明,
    // 請訪問 http://go.microsoft.com/?LinkId=9394801
    public class WebApiApplication : System.Web.HttpApplication
    {
        //註冊控制器,介面和實現
        void ConfigureApi(HttpConfiguration config)
        {
            var unity = new UnityContainer();
            unity.RegisterType<BooksController>();
            unity.RegisterType<IBookRepository, BookRepository>(new HierarchicalLifetimeManager());
            config.DependencyResolver = new IoCContainer(unity);
           
        }
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            //註冊依賴注入
            ConfigureApi(GlobalConfiguration.Configuration);
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);           
        }
    }
}

 

→建立一個空的Api控制器,編寫如下:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MyMvcAndWebApi.Models;
namespace MyMvcAndWebApi.Controllers
{
    public class BooksController : ApiController
    {
        //_repository執行時變數,在首次引用IBookRepository方法時動態分配記憶體
        private static IBookRepository _repository;
        public BooksController(IBookRepository repository)
        {
            if (repository == null)
            {
                throw new ArgumentNullException("repository");
            }
            _repository = repository;
        }
        //根據慣例,如果action名稱以Get開頭,那就接收Get請求
        public IEnumerable<Book> GetAllBooks()
        {
            return _repository.GetAll();
        }
        //ASP.NET Web API會自動幫我們把URL中的字串id轉換成引數型別int
        public Book GetBook(int id)
        {
            Book book = _repository.Get(id);
            if (book == null)
            {
                //HttpResponseException封裝返回的異常
                //HttpResponseMessage封裝返回的資訊
                throw new  HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
            }
            return book;
        }
        //新增
        //action名以post開頭,按照慣例,接收post請求
        //客戶端傳送來序列化的Book物件,在服務端對Book物件反序列化
        public HttpResponseMessage PostBook(Book book)
        {
            book = _repository.Add(book);
            // Web API預設返回的狀態碼為200,可是,根據HTTP/1.1協議,在新增完,我們希望返回201狀態碼
            var response = Request.CreateResponse(HttpStatusCode.Created, book);
            //返回新建立資源的url
            string uri = Url.Route(null, new {id = book.Id});
            response.Headers.Location = new Uri(Request.RequestUri, uri);
            return response;
        }
        //修改
        //引數id從url中獲取,book從request中反序列化
        //根據慣例,Put開頭的action,接收put請求
        public void PutBook(int id, Book book)
        {
            book.Id = id;
            if (!_repository.Update(book))
            {
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
            }
        }
        //刪除
        //根據管理,Delete開頭接收delete請求
        public HttpResponseMessage DeleteBook(int id)
        {
            _repository.Remove(id);
            //返回200狀態碼錶示刪除成功
            //返回202狀態碼錶示正在刪除
            //返回204狀態碼錶示沒有內容
            return new HttpResponseMessage(HttpStatusCode.NoContent);
        }
    }
}

以上,所有action方法名稱都符合了慣例。

 

→修改Home/Index.cshtml,我們在此使用jquery與服務端api控制器進行互動。

@section scripts {
    <script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"> </script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"> </script>
    <script src="@Url.Content("~/Scripts/jQuery.tmpl.js")" type="text/javascript"> </script>
    <script type="text/javascript">
        $(function () {
            //Get請求
            $.getJSON(
                "api/books",
                function (data) {
                    $.each(data,
                        function (index, value) {
                            $("#bookTemplate").tmpl(value).appendTo("#books");
                        }
                    );
                    $("#loader").hide("slow");
                    $("#addBook").show("slow");
                }
            );
            //新增
            $("#addBook").submit(function () {
                $.post(
                    "api/books",
                    $("#addBook").serialize(), //序列化Book物件
                    function (value) {
                        $("#bookTemplate").tmpl(value).appendTo("#books");
                        $("#name").val("");
                        $("#price").val("");
                    },
                    "json"
                );
                return false;
            });
            //刪除
            $(".removeBook").live("click", function () {
                $.ajax({
                    type: "DELETE",
                    url: $(this).attr("href"),
                    context: this,
                    success: function () {
                        $(this).closest("li").remove();
                    }
                });
                return false;
            });
            $("input[type=\"submit\"], .removeBook, .viewImage").button();
        });
        //根據id搜尋
        function find() {
            var id = $('#bookId').val();
            $.getJSON("api/books/" + id,
                function (data) {
                    var str = data.Name + ': $' + data.Price;
                    $('#book').html(str);
                })
                .fail(
                    function (jqXHR, textStatus, err) {
                        $('#book').html('Error: ' + err);
                    });
        }
    </script>
    <script id="bookTemplate" type="text/html"> 
        <li>
            <p>
                <strong> Book ID:</strong> ${ Id}
                <br />
                <strong> Book Name:</strong> ${ Name }
                <br />
                <strong> Price: $</strong> ${ Price }
            </p>
            <p>
                <a href="${ Self }" class="button small red removeBook">移除</a>
            </p>
        </li>
    </script>
}
<body>
    <form method="post" id="addBook">
    <div class="container_16">
        <h1 class="title-01">Book資訊</h1>
    </div>
    <div class="container_16">
        <div class="grid_16 body-container">
            <div class="margin grid_6 alpha">
                <label for="Name">
                    Name</label><br />
                <input type="text" id="name" name="Name" class="text grid_4" />
                <br class="clear" />
                <label for="Price">
                    Price</label><br />
                <input type="text" id="price" name="Price" class="text grid_4" />
                <br class="clear" />
                <input type="submit" value="新增" class="button small green" />
                <br />
                <br />
                <br class="clear" />
                <strong id="book">@*   <label id="book">
                    </label>*@ </strong>
                <br />
                <br class="clear" />
                <br />
                <label for="bookId">
                    根據ID搜尋
                </label>
                <br />
                <input type="text" id="bookId" size="20" class="text grid_4" /><br class="clear" />
                <input type="button" value="搜尋" onclick="find();" class="button small gray" />
            </div>
            <div class="grid_8 omega">
                <img id="loader" src="images/ajax-loader.gif" />
                <ul id="books" class="books">
                </ul>
            </div>
        </div>
    </div>
    <br class="clear" />
    <div class="footer clearfix">
    </div>
    </form>
</body>


另外,有關Bootsrap的樣式在BundleConfig類中定義。

 

參考資料:http://www.codeproject.com/Articles/344078/ASP-NET-WebAPI-Getting-Started-with-MVC-and-WebAP

相關文章