Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CRUD Operations with ASP.NET Web API using FluentNHibernate, Repository Pattern

0.00/5 (No votes)
16 Oct 2014 1  
In this step-by-step article, we will discuss all about CRUD (Create Read Update Delete) operations with Web API using Repository pattern.

Introduction

In this step-by-step article, we will discuss all about Create, Read, Update, Delete (CRUD) operations with the Web API using the Repository pattern and using the most popular ORM, FluentNHibernate. In this article, we will not go into depth about how to consume WEB APIs.

I recommend reading the following article to learn more if you want to use the Web API from a third-party as a client:

Why WEB API?

While preparing this whitepaper, I asked myself the question "Why Web API?". In simple and few words, there are numerous things why we are doing it with the Web API and not with WebServices, WCF or REST WCF.

I categorized these as follows:

  • Features
  • Functionalities
  • Resources
  • Behavioral

Although we will not dig deely into the preceding since that is beyond the scope of our main topic, I would like to recommend reading the following to understand these terms better:

Pre-requisites

To implement and play with the source code, one should have:

  • Basic knowledge of ASP.NET MVC
  • Basic knowledge of REST services
  • Basic knowledge of FluentNHibernate
  • Basic knowledge of Repository pattern
  • Require Visual Studio 2012 or later with ASP.NET WebAPI support

Let's Start Creating an ASP.NET Web API Project

  • Start your Visual Studio and choose File -> New Project (Ctrl + Shift + N)
  • From the available dialog, choose Web Templates -> ASP.NET MVC 4 Web Application
  • I named it as ‘CRUDWithWebAPI’ – you can choose your favorite one :)

    Add asp.net web api application

    Add ASP.NET web API application.
  • Select Empty Web API using Razor View Engine.

    Add asp.net web api application

    Add ASP.NET web API application.

Folder Structure

By default, we get these folders:

  • App_Data
  • App_Start: Contains config classes required to initiate the app (e.g. route collection, etc.)
  • Controllers: Controller of web API
  • Models: Contain model/classes
  • Scripts: All scripts and CSS
  • Views

Default folder structure of asp.net Web API application

Default folder structure of ASP.NET Web API application

More explanation of each and every folder mentioned above is beyond the scope of this article.

Installing FluentNHibernate

To install FluentNHIbernate support to your project:

  • Open ‘Package Manager Console’ using View-> Other Windows
  • Now type ‘Install-Package FluentNHibernate’ and hit enter

Wait till FluentNHibernate gets installed.

Models, Mappings and Repository Pattern

In this step, we will create Model its mapping and will add a repository.

Adding Models and their Mappings

  • To add model, right click on Models folder from Solution Explorer and choose class, name it ‘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

    Adding a Model
  • Add its mapping class, right click on Models folder from Solution Explorer and choose class, name it ‘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);
          }
      }
    
  • Do not forget to add the following namespace:
    using FluentNHibernate.Mapping;
    

Adding NHibernate Support to Your Application

  • Add new folder and name it as ‘Helper’.
  • Add a new class beneath ‘Helper’ folder and name it as ‘NHibernateHelper’.
  • In this class, we need to configure NHibernate and build all sessionfactory, so our application will interact with database:

    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();
       }
    

    We are not going to discuss all this stuff in detail as these are beyond the scope of this article.

  • Create a new folder beneath ‘Models’ and name it as ‘Persistance’.
  • Add one Interface ‘IServerDataRepository’ under ‘Persistance’.
    public interface IServerDataRepository
       {
           ServerData Get(int id);
           IEnumerable<ServerData> GetAll();
           ServerData Add(ServerData serverData);
           void Delete(int id);
           bool Update(ServerData serverData);
       }
    
  • Add one class ‘ServerDataRepository’ under ‘Persistance’ and implement ‘IServerDataRepository’.
    public class ServerDataRepository : IServerDataRepository
       {
        //method implementation goes here
       }
    

    Now, we have our Repository which is ready to start play. :)

    Refer to http://msdn.microsoft.com/en-us/library/ff649690.aspx for more details on Repository Pattern.

Add Web API Controller

In this step, we will create all the necessary action methods:

  • Right click on folder ‘Controllers’ and add a new controller, name it as ‘ServerDataController’ (remember to select an empty controller)
  • It would look like:
    public class ServerDataController : ApiController
      {
      }
    

    Add the following line of code (it will initiate our repository).

    static readonly IServerDataRepository serverDataRepository = new ServerDataRepository();
    

    Adding web API empty controller

    Adding web API empty controller.
    Quote:

    Please note that in this article/demo, we are not going to implement any DI pattern or Inversion of Control (IOC) framework.

    We are now ready to start the game. :)

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

    See the above defined method, why we add ‘Get’ suffix, this is a good practice to add ‘Get’ with your method name, by convention it maps GET request.

    This method does not have any parameter, so, you can say this maps URI which does not contain ‘id’ parameter.

  • Add the following method to get ServerData by id. This is an optional parameter as defined in our route and ASP.NET Web API framework automatically converts its type to int.
  • It will throw ‘HttpResponseException‘ exception if id is not valid (in the following method, we converted the exception to 404 NOT Found exception).
    public ServerData GetServerDataById(int id)
       {
           var serverData = serverDataRepository.Get(id);
    
           if (serverData == null)
               throw new HttpResponseException(HttpStatusCode.NotFound);
    
           return serverData;
       }
    
  • The following method is to get ServerData by its type:

    public IEnumerable<ServerData> GetServerDataByType(int type)
      {
          return serverDataRepository.GetAll().Where(d => d.Type == type);
      }
    
  • To invoke the above method, we need to define new routes in ‘WebApiConfig.cs’ as follows:

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

    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}"
               );
    
  • Here, we are going to delete serverdata using this method:

    public void DeletServerData(int id)
       {
           var serverData = serverDataRepository.Get(id);
    
           if (serverData == null)
               throw new HttpResponseException(HttpStatusCode.NotFound);
           serverDataRepository.Delete(id);
       }
    
  • Let's add a new method to our controller to add new record of ServerData.

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

Will the above method work? Of course, it works, but it's not an ideal method or say it's not quite complete, why?

  • In the above, suffix is ‘Post’ which sounds like it sends Http POST request.
  • Also, it's noticeable parameter is of type ‘ServerData’.
  • Complex type parameters are deserialized from the requested body when we used Web API, also, we can say we expect serialized input from client like either in XML or JSON.
  • In the above method, we miss the following from HTTP response:
    • Response code: by default, it is 200 (Ok) but as per HTTP1.1 protocol, the server should reply 201 (created), while POST request results in the creation of a resource.
    • Location: Server should include the URI of the new resource in the location header of the response, whenever it creates a resource.
  • So, we can implement this as defined in the following methods:
    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;
      }
    
  • Finally, we need to add an update method, it's straight forward:
    public void PutServerData(int id, ServerData serverData)
       {
           serverData.Id = id;
    
           if (!serverDataRepository.Update(serverData))
               throw new HttpResponseException(HttpStatusCode.NotFound);
       }
    

    From the above, we can understand that WEB API matches this method to PUT request, the above method has two parameter ids and serverdata. So, id is taken from URI path and serverdata is deserialized from the request body.

Quote:

Please note that by-default Web API framework takes simple parameter types from the route and complex types from the request body.

Set Default Result Output Type

We need result in JSON by default, let's add the following line either in Global.asx.cs file or in the file where you are registering routes:

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

Testing Web API Results

Here, I used ‘Poster for Firefox’ plug in to test the output. You can directly download this plug in from plug in directory of Firefox:

Requesting a GET operation using Poster for Firefox

Requesting a GET operation using Poster for Firefox

Just enter the URI and press GET or whatever you want to play, you will get the output accordingly. :)

Response of a GET from Poster for Firefox

Response of a GET from Poster for Firefox

What To Do Next?

This is a very good video tutorial from Questpond on ‘REST (Representational State Transfer)’ to understand about REST services.

Closing Notes

I hope you enjoyed this article. I tried to make this as simple as I can. If you like this article, please rate it and share it to share the knowledge.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here