65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 FluentNHibernate 和 Repository 模式在 ASP.NET Web API 中进行 CRUD 操作

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2014年10月17日

CPOL

6分钟阅读

viewsIcon

76028

downloadIcon

770

在这篇循序渐进的文章中,我们将讨论使用 Repository 模式在 Web API 中进行所有 CRUD(创建、读取、更新、删除)操作。

引言

在这篇循序渐进的文章中,我们将讨论使用 Repository 模式和最流行的 ORM FluentNHibernate 在 Web API 中进行创建、读取、更新、删除 (CRUD) 操作。本文不会深入探讨如何使用 WEB API。

如果您想将 Web API 作为客户端从第三方使用,我建议阅读以下文章以了解更多信息

为什么选择 WEB API?

在准备这份白皮书时,我问自己一个问题:“为什么选择 Web API?”。简单来说,我们选择 Web API 而不是 WebServices、WCF 或 REST WCF 的原因有很多。

我将它们归类如下

  • 特点
  • 功能
  • 资源
  • 行为型

尽管我们不会深入探讨上述内容,因为它超出了我们主要主题的范围,但我仍建议阅读以下内容以更好地理解这些术语

先决条件

要实现并运行源代码,您应该具备

  • ASP.NET MVC 的基本知识
  • REST 服务的 A基本知识
  • FluentNHibernate 的基本知识
  • Repository 模式的基本知识
  • 需要 Visual Studio 2012 或更高版本并支持 ASP.NET WebAPI

让我们开始创建 ASP.NET Web API 项目

  • 启动 Visual Studio 并选择“文件”->“新建项目”(Ctrl + Shift + N)
  • 从可用对话框中,选择“Web 模板”->“ASP.NET MVC 4 Web 应用程序”
  • 我将其命名为“CRUDWithWebAPI”——您可以选择您喜欢的名称 :)

    Add asp.net web api application

    添加 ASP.NET web API 应用程序。
  • 选择使用 Razor 视图引擎的空 Web API。

    Add asp.net web api application

    添加 ASP.NET web API 应用程序。

文件夹结构

默认情况下,我们会得到这些文件夹

  • App_Data
  • App_Start:包含启动应用程序所需的配置类(例如路由集合等)
  • Controllers:Web API 的控制器
  • Models:包含模型/类
  • Scripts:所有脚本和 CSS
  • 视图

Default folder structure of asp.net Web API application

ASP.NET Web API 应用程序的默认文件夹结构

对上面提到的每个文件夹的更多解释超出了本文的范围。

安装 FluentNHibernate

为您的项目安装 FluentNHibernate 支持

  • 使用“视图”->“其他窗口”打开“包管理器控制台”
  • 现在输入“Install-Package FluentNHibernate”并按 Enter

等待 FluentNHibernate 安装完成。

模型、映射和 Repository 模式

在此步骤中,我们将创建模型及其映射,并添加一个 Repository。

添加模型及其映射

  • 要添加模型,右键单击解决方案资源管理器中的Models文件夹并选择类,将其命名为“ServerData”。
      public class ServerData
    	{
    		public virtual int Id { get; set; }
    		public virtual DateTime InitialDate { get; set; }
    		public virtual DateTime EndDate { get; set; }
    		public virtual int OrderNumber { get; set; }
    		public virtual bool IsDirty { get; set; }
    		public virtual string IP { get; set; }
    		public virtual int Type { get; set; }
    		public virtual int RecordIdentifier { get; set; }
    	} 

    Adding a model

    添加模型
  • 添加其映射类,右键单击解决方案资源管理器中的Models文件夹并选择类,将其命名为“ServerDataMap”。
      public class ServerDataMap : ClassMap<ServerData>
        {
            public ServerDataMap()
            {
                Table("ServerData");
    
                Id(x => x.Id, "Id").GeneratedBy.Identity().UnsavedValue(0);
    
                Map(x => x.InitialDate);
                Map(x => x.EndDate);
                Map(x => x.OrderNumber);
                Map(x => x.IsDirty);
                Map(x => x.IP).Length(11);
                Map(x => x.Type).Length(1);
                Map(x => x.RecordIdentifier);
            }
        } 
  • 不要忘记添加以下命名空间
     using FluentNHibernate.Mapping; 

为您的应用程序添加 NHibernate 支持

  • 添加新文件夹并将其命名为“Helper”。
  • 在“Helper”文件夹下添加一个新类,并将其命名为“NHibernateHelper”。
  • 在此类中,我们需要配置 NHibernate 并构建所有sessionfactory,以便我们的应用程序将与数据库交互

     private static void CreateSessionFactory()
    	{
    		_sessionFactory = Fluently.Configure()
    			.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString).ShowSql)
    			.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ServerData>())
    			.ExposeConfiguration(cfg => new SchemaExport(cfg).Create(false, false))
    			.BuildSessionFactory();
    	} 

    我们不会详细讨论所有这些内容,因为它们超出了本文的范围。

  • 在“Models”下创建一个新文件夹,并将其命名为“Persistance”。
  • 在“Persistance”下添加一个接口“IServerDataRepository”。
     public interface IServerDataRepository
        {
            ServerData Get(int id);
            IEnumerable<ServerData> GetAll();
            ServerData Add(ServerData serverData);
            void Delete(int id);
            bool Update(ServerData serverData);
        } 
  • 在“Persistance”下添加一个类“ServerDataRepository”并实现“IServerDataRepository”。
     public class ServerDataRepository : IServerDataRepository
        {
    	 //method implementation goes here	
        } 

    现在,我们的 Repository 已经准备好开始运行了。 :)

    有关 Repository 模式的更多详细信息,请参阅 http://msdn.microsoft.com/en-us/library/ff649690.aspx

添加 Web API 控制器

在此步骤中,我们将创建所有必要的动作方法

  • 右键单击文件夹“Controllers”并添加一个新控制器,将其命名为“ServerDataController”(记住选择一个空控制器)
  • 它看起来像
      public class ServerDataController : ApiController
        {
        } 

    添加以下代码行(它将初始化我们的存储库)。

     static readonly IServerDataRepository serverDataRepository = new ServerDataRepository(); 

    Adding web API empty controller

    添加 web API 空控制器。
    引用

    请注意,在本文/演示中,我们不会实现任何 DI 模式或控制反转 (IOC) 框架。

    我们现在准备开始游戏。 :)

     public IEnumerable<ServerData> GetServerData()
    	{
    		return serverDataRepository.GetAll();
    	} 

    请参阅上面定义的方法,为什么我们添加“Get”后缀,这是一个很好的做法,可以将“Get”添加到方法名称中,按照约定它会映射GET请求。

    此方法没有任何参数,因此,您可以说它映射的 URI 不包含“id”参数。

  • 添加以下方法以通过 id 获取 ServerData。这是一个可选参数,如我们的路由中所定义,ASP.NET Web API 框架会自动将其类型转换为 int
  • 如果 id 无效,它将抛出“HttpResponseException”异常(在以下方法中,我们将异常转换为 404 NOT Found 异常)。
     public ServerData GetServerDataById(int id)
    	{
    		var serverData = serverDataRepository.Get(id);
    
    		if (serverData == null)
    			throw new HttpResponseException(HttpStatusCode.NotFound);
    
    		return serverData;
    	} 
  • 以下方法用于按类型获取 ServerData

      public IEnumerable<ServerData> GetServerDataByType(int type)
    	{
    		return serverDataRepository.GetAll().Where(d => d.Type == type);
    	} 
  • 要调用上述方法,我们需要在“WebApiConfig.cs”中定义新路由,如下所示

     config.Routes.MapHttpRoute(
                    name: "ProductByType",
                    routeTemplate: "api/{controller}/type/{type}"
                ); 
  • 类似地

     public IEnumerable<ServerData> GetServerDataByIP(string ip)
            {
                return serverDataRepository.GetAll().Where(d => d.IP.ToLower() == ip.ToLower());
            } 
     config.Routes.MapHttpRoute(
                    name: "ProductByIP",
                    routeTemplate: "api/{controller}/ip/{ip}"
                ); 
  • 在这里,我们将使用此方法删除 serverdata

     public void DeletServerData(int id)
    	{
    		var serverData = serverDataRepository.Get(id);
    
    		if (serverData == null)
    			throw new HttpResponseException(HttpStatusCode.NotFound);
    		serverDataRepository.Delete(id);
    	} 
  • 让我们向控制器添加一个新方法,以添加 ServerData 的新记录。

    	public ServerData PostServerData(ServerData serverData)
    	{
    		return serverDataRepository.Add(serverData);
    	} 

上述方法能奏效吗?当然可以,但它不是一个理想的方法,或者说它不够完整,为什么呢?

  • 在上面,后缀是“Post”,听起来它发送的是 Http POST 请求。
  • 此外,值得注意的是参数类型为“ServerData”。
  • 当我们使用 Web API 时,复杂类型参数会从请求正文中反序列化,我们也可以说我们期望客户端提供序列化输入,例如 XML 或 JSON。
  • 在上述方法中,我们缺少 HTTP 响应中的以下内容
    • 响应代码:默认情况下为 200 (OK),但根据 HTTP 1.1 协议,服务器应回复 201 (created),而 POST 请求会导致资源的创建。
    • 位置:每当服务器创建资源时,它应在响应的位置标头中包含新资源的 URI。
  • 因此,我们可以按以下方法定义的方式实现此功能
      public HttpResponseMessage PostServerData(ServerData serverData)
    	{
    		serverData = serverDataRepository.Add(serverData);
    
    		var response = Request.CreateResponse<ServerData>(HttpStatusCode.Created, serverData);
    
    		var uri = Url.Link("DefaultApi", new { id = serverData.Id });
    		response.Headers.Location = new Uri(uri);
    
    		return response;
    	} 
  • 最后,我们需要添加一个update方法,它很简单
     public void PutServerData(int id, ServerData serverData)
    	{
    		serverData.Id = id;
    
    		if (!serverDataRepository.Update(serverData))
    			throw new HttpResponseException(HttpStatusCode.NotFound);
    	} 

    从上面我们可以看出,WEB API 将此方法与PUT请求匹配,上述方法有两个参数 id 和 serverdata。因此,id 取自 URI 路径,serverdata 从请求正文中反序列化。

引用

请注意,默认情况下,Web API 框架从路由中获取简单参数类型,从请求正文中获取复杂类型。

设置默认结果输出类型

我们默认需要 JSON 结果,让我们将以下行添加到 Global.asx.cs 文件或您正在注册路由的文件中

	//return JSON response by default
	config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

测试 Web API 结果

在这里,我使用“Firefox Poster”插件来测试输出。您可以直接从 Firefox 插件目录下载此插件

Requesting a GET operation using Poster for Firefox

使用 Firefox Poster 请求 GET 操作

只需输入 URI 并按下 GET 或您想做的任何操作,您就会得到相应的输出。 :)

Response of a GET from Poster for Firefox

Firefox Poster 的 GET 响应

接下来做什么?

这是一个非常好的视频教程,来自 Questpond,关于“REST(Representational State Transfer)”以了解 REST 服务。

结语

希望您喜欢这篇文章。我尽量使其简单明了。如果您喜欢这篇文章,请给它评分并分享,以分享知识。

© . All rights reserved.