Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Workflow Automation with C# and ASP.NET MVC


One of the more abstract concepts you'll handle when building your business is what the workflow will look like.

At its core, setting up a standardized workflow is about enabling your service providers (agents, hosts, customer service reps, administrators, and the rest of the gang) to better serve your customers.

To illustrate a very real-world example, today we'll build a C# and ASP.NET MVC webapp for finding and booking vacation properties — tentatively called Airtng.

Here's how it'll work:

  1. A host creates a vacation property listing
  2. A guest requests a reservation for a property
  3. The host receives an SMS notifying them of the reservation request. The host can either Accept or Reject the reservation
  4. The guest is notified whether a request was rejected or accepted

Learn how Airbnb used Twilio SMS to streamline the rental experience for 60M+ travelers around the world.(link takes you to an external page)


Workflow Building Blocks

workflow-building-blocks page anchor

We'll be using the Twilio REST API to send our users messages at important junctures. Here's a bit more on our API:

  • Sending Messages with Twilio API

Notify the host

notify-the-host page anchor

AirTNG.Web/Domain/Reservations/Notifier.cs


_61
using System;
_61
using System.Linq;
_61
using System.Text;
_61
using System.Threading.Tasks;
_61
using AirTNG.Web.Domain.Twilio;
_61
using AirTNG.Web.Models;
_61
using AirTNG.Web.Models.Repository;
_61
_61
namespace AirTNG.Web.Domain.Reservations
_61
{
_61
public interface INotifier
_61
{
_61
Task SendNotificationAsync(Reservation reservation);
_61
}
_61
_61
public class Notifier : INotifier
_61
{
_61
private readonly ITwilioMessageSender _client;
_61
private readonly IReservationsRepository _repository;
_61
_61
public Notifier() : this(
_61
new TwilioMessageSender(),
_61
new ReservationsRepository()) { }
_61
_61
public Notifier(ITwilioMessageSender client, IReservationsRepository repository)
_61
{
_61
_client = client;
_61
_repository = repository;
_61
}
_61
_61
public async Task SendNotificationAsync(Reservation reservation)
_61
{
_61
var pendingReservations = await _repository.FindPendingReservationsAsync();
_61
if (pendingReservations.Count() < 2)
_61
{
_61
var notification = BuildNotification(reservation);
_61
await _client.SendMessageAsync(notification.To, notification.From, notification.Messsage);
_61
}
_61
}
_61
_61
private static Notification BuildNotification(Reservation reservation)
_61
{
_61
var message = new StringBuilder();
_61
message.AppendFormat("You have a new reservation request from {0} for {1}:{2}",
_61
reservation.Name,
_61
reservation.VacationProperty.Description,
_61
Environment.NewLine);
_61
message.AppendFormat("{0}{1}",
_61
reservation.Message,
_61
Environment.NewLine);
_61
message.Append("Reply [accept] or [reject]");
_61
_61
return new Notification
_61
{
_61
From = PhoneNumbers.Twilio,
_61
To = reservation.PhoneNumber,
_61
Messsage = message.ToString()
_61
};
_61
}
_61
}
_61
}

Ready to go? Boldly click the button right after this sentence.


For this use case to work we have to handle authentication. We will rely on ASP.NET Identity(link takes you to an external page) for this purpose.

Each User will need to have a phone_number that we will use later to send SMS notifications.

AirTNG.Web/Models/IdentityModels.cs


_41
using System.Collections.Generic;
_41
using System.Data.Entity;
_41
using System.Security.Claims;
_41
using System.Threading.Tasks;
_41
using Microsoft.AspNet.Identity;
_41
using Microsoft.AspNet.Identity.EntityFramework;
_41
_41
namespace AirTNG.Web.Models
_41
{
_41
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
_41
public class ApplicationUser : IdentityUser
_41
{
_41
public string Name { get; set; }
_41
_41
public virtual IList<VacationProperty> VacationProperties { get; set; }
_41
_41
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
_41
{
_41
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
_41
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
_41
// Add custom user claims here
_41
return userIdentity;
_41
}
_41
}
_41
_41
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
_41
{
_41
public ApplicationDbContext()
_41
: base("AirTNGConnection", false)
_41
{
_41
}
_41
_41
public static ApplicationDbContext Create()
_41
{
_41
return new ApplicationDbContext();
_41
}
_41
_41
public DbSet<VacationProperty> VacationProperties { get; set; }
_41
public DbSet<Reservation> Reservations { get; set; }
_41
}
_41
}

Next let's take a look at the Vacation Property model.


The Vacation Property Model

the-vacation-property-model page anchor

Our rental application will obviously require listing properties.

The VacationProperty belongs to the User who created it (we'll call this user the host from this point on) and contains only two properties: a Description and an ImageUrl.

A VacationProperty can have many Reservations.

AirTNG.Web/Models/VacationProperty.cs


_16
using System;
_16
using System.Collections.Generic;
_16
_16
namespace AirTNG.Web.Models
_16
{
_16
public class VacationProperty
_16
{
_16
public int Id { get; set; }
_16
public string UserId { get; set; }
_16
public virtual ApplicationUser User { get; set; }
_16
public string Description { get; set; }
_16
public string ImageUrl { get; set; }
_16
public DateTime CreatedAt { get; set; }
_16
public virtual IList<Reservation> Reservations { get; set; }
_16
}
_16
}

Next, let's see what our Reservation model looks like.


The Reservation model is at the center of the workflow for this application.

It is responsible for keeping track of:

  • The VacationProperty it is associated with.
  • The User who owns that vacation property (the host). Through this property the user will have access to the host phone number indirectly.
  • The Status of the reservation

AirTNG.Web/Models/Reservation.cs


_18
using System;
_18
using System.ComponentModel.DataAnnotations.Schema;
_18
_18
namespace AirTNG.Web.Models
_18
{
_18
public class Reservation
_18
{
_18
public int Id { get; set; }
_18
public string Name { get; set; }
_18
public string PhoneNumber { get; set; }
_18
public ReservationStatus Status { get; set; }
_18
public string Message { get; set; }
_18
public DateTime CreatedAt { get; set; }
_18
public int VactionPropertyId { get; set; }
_18
[ForeignKey("VactionPropertyId")]
_18
public virtual VacationProperty VacationProperty { get; set; }
_18
}
_18
}

Now that our models are ready, let's have a look at the controller that will create reservations.


The reservation creation form holds only a single field: the message that will be sent to the host when one of her properties is reserved. The rest of the information needed to create a reservation is taken from the VacationProperty itself.

A reservation is created with a default status ReservationStatus.Pending. That way when the host replies with an accept or reject response the application knows which reservation to update.

The Reservations Controller

the-reservations-controller page anchor

AirTNG.Web/Controllers/ReservationsController.cs


_121
using System;
_121
using System.Threading.Tasks;
_121
using System.Web.Mvc;
_121
using AirTNG.Web.Domain.Reservations;
_121
using AirTNG.Web.Models;
_121
using AirTNG.Web.Models.Repository;
_121
using AirTNG.Web.ViewModels;
_121
using Twilio.AspNet.Mvc;
_121
using Twilio.TwiML;
_121
_121
namespace AirTNG.Web.Controllers
_121
{
_121
[Authorize]
_121
public class ReservationsController : TwilioController
_121
{
_121
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
_121
private readonly IReservationsRepository _reservationsRepository;
_121
private readonly IUsersRepository _usersRepository;
_121
private readonly INotifier _notifier;
_121
_121
public ReservationsController() : this(
_121
new VacationPropertiesRepository(),
_121
new ReservationsRepository(),
_121
new UsersRepository(),
_121
new Notifier()) { }
_121
_121
public ReservationsController(
_121
IVacationPropertiesRepository vacationPropertiesRepository,
_121
IReservationsRepository reservationsRepository,
_121
IUsersRepository usersRepository,
_121
INotifier notifier)
_121
{
_121
_vacationPropertiesRepository = vacationPropertiesRepository;
_121
_reservationsRepository = reservationsRepository;
_121
_usersRepository = usersRepository;
_121
_notifier = notifier;
_121
}
_121
_121
// GET: Reservations/Create
_121
public async Task<ActionResult> Create(int id)
_121
{
_121
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
_121
var reservation = new ReservationViewModel
_121
{
_121
ImageUrl = vacationProperty.ImageUrl,
_121
Description = vacationProperty.Description,
_121
VacationPropertyId = vacationProperty.Id,
_121
VacationPropertyDescription = vacationProperty.Description,
_121
UserName = vacationProperty.User.Name,
_121
UserPhoneNumber = vacationProperty.User.PhoneNumber,
_121
};
_121
_121
return View(reservation);
_121
}
_121
_121
// POST: Reservations/Create
_121
[HttpPost]
_121
public async Task<ActionResult> Create(ReservationViewModel model)
_121
{
_121
if (ModelState.IsValid)
_121
{
_121
var reservation = new Reservation
_121
{
_121
Message = model.Message,
_121
PhoneNumber = model.UserPhoneNumber,
_121
Name = model.UserName,
_121
VactionPropertyId = model.VacationPropertyId,
_121
Status = ReservationStatus.Pending,
_121
CreatedAt = DateTime.Now
_121
};
_121
_121
await _reservationsRepository.CreateAsync(reservation);
_121
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
_121
await _notifier.SendNotificationAsync(reservation);
_121
_121
return RedirectToAction("Index", "VacationProperties");
_121
}
_121
_121
return View(model);
_121
}
_121
_121
// POST Reservations/Handle
_121
[HttpPost]
_121
[AllowAnonymous]
_121
public async Task<TwiMLResult> Handle(string from, string body)
_121
{
_121
string smsResponse;
_121
_121
try
_121
{
_121
var host = await _usersRepository.FindByPhoneNumberAsync(from);
_121
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
_121
_121
var smsRequest = body;
_121
reservation.Status =
_121
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
_121
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
_121
? ReservationStatus.Confirmed
_121
: ReservationStatus.Rejected;
_121
_121
await _reservationsRepository.UpdateAsync(reservation);
_121
smsResponse =
_121
string.Format("You have successfully {0} the reservation", reservation.Status);
_121
}
_121
catch (Exception)
_121
{
_121
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
_121
}
_121
_121
return TwiML(Respond(smsResponse));
_121
}
_121
_121
private static MessagingResponse Respond(string message)
_121
{
_121
var response = new MessagingResponse();
_121
response.Message(message);
_121
_121
return response;
_121
}
_121
}
_121
}

Next, let's see how we will send SMS notifications to the vacation rental host.


When a reservation is created we want to notify the owner of the property that someone is interested.

This is where we use Twilio C# Helper Library(link takes you to an external page) to send a SMS message to the host, using our Twilio phone number(link takes you to an external page). As you can see, sending SMS messages using Twilio takes just a few lines of code.

Next we just have to wait for the host to send an SMS response accepting or rejecting the reservation. Then we can notify the guest and host that the reservation information has been updated.

AirTNG.Web/Domain/Reservations/Notifier.cs


_61
using System;
_61
using System.Linq;
_61
using System.Text;
_61
using System.Threading.Tasks;
_61
using AirTNG.Web.Domain.Twilio;
_61
using AirTNG.Web.Models;
_61
using AirTNG.Web.Models.Repository;
_61
_61
namespace AirTNG.Web.Domain.Reservations
_61
{
_61
public interface INotifier
_61
{
_61
Task SendNotificationAsync(Reservation reservation);
_61
}
_61
_61
public class Notifier : INotifier
_61
{
_61
private readonly ITwilioMessageSender _client;
_61
private readonly IReservationsRepository _repository;
_61
_61
public Notifier() : this(
_61
new TwilioMessageSender(),
_61
new ReservationsRepository()) { }
_61
_61
public Notifier(ITwilioMessageSender client, IReservationsRepository repository)
_61
{
_61
_client = client;
_61
_repository = repository;
_61
}
_61
_61
public async Task SendNotificationAsync(Reservation reservation)
_61
{
_61
var pendingReservations = await _repository.FindPendingReservationsAsync();
_61
if (pendingReservations.Count() < 2)
_61
{
_61
var notification = BuildNotification(reservation);
_61
await _client.SendMessageAsync(notification.To, notification.From, notification.Messsage);
_61
}
_61
}
_61
_61
private static Notification BuildNotification(Reservation reservation)
_61
{
_61
var message = new StringBuilder();
_61
message.AppendFormat("You have a new reservation request from {0} for {1}:{2}",
_61
reservation.Name,
_61
reservation.VacationProperty.Description,
_61
Environment.NewLine);
_61
message.AppendFormat("{0}{1}",
_61
reservation.Message,
_61
Environment.NewLine);
_61
message.Append("Reply [accept] or [reject]");
_61
_61
return new Notification
_61
{
_61
From = PhoneNumbers.Twilio,
_61
To = reservation.PhoneNumber,
_61
Messsage = message.ToString()
_61
};
_61
}
_61
}
_61
}

Now's let's peek at how we're handling the host's responses.


Handle Incoming Messages

handle-incoming-messages page anchor

The Reservations/Handle controller controller handles our incoming Twilio request and does four things:

  1. Check for the guest's pending reservation
  2. Update the status of the reservation
  3. Respond to the host
  4. Send notification to the guest

AirTNG.Web/Controllers/ReservationsController.cs


_121
using System;
_121
using System.Threading.Tasks;
_121
using System.Web.Mvc;
_121
using AirTNG.Web.Domain.Reservations;
_121
using AirTNG.Web.Models;
_121
using AirTNG.Web.Models.Repository;
_121
using AirTNG.Web.ViewModels;
_121
using Twilio.AspNet.Mvc;
_121
using Twilio.TwiML;
_121
_121
namespace AirTNG.Web.Controllers
_121
{
_121
[Authorize]
_121
public class ReservationsController : TwilioController
_121
{
_121
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
_121
private readonly IReservationsRepository _reservationsRepository;
_121
private readonly IUsersRepository _usersRepository;
_121
private readonly INotifier _notifier;
_121
_121
public ReservationsController() : this(
_121
new VacationPropertiesRepository(),
_121
new ReservationsRepository(),
_121
new UsersRepository(),
_121
new Notifier()) { }
_121
_121
public ReservationsController(
_121
IVacationPropertiesRepository vacationPropertiesRepository,
_121
IReservationsRepository reservationsRepository,
_121
IUsersRepository usersRepository,
_121
INotifier notifier)
_121
{
_121
_vacationPropertiesRepository = vacationPropertiesRepository;
_121
_reservationsRepository = reservationsRepository;
_121
_usersRepository = usersRepository;
_121
_notifier = notifier;
_121
}
_121
_121
// GET: Reservations/Create
_121
public async Task<ActionResult> Create(int id)
_121
{
_121
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
_121
var reservation = new ReservationViewModel
_121
{
_121
ImageUrl = vacationProperty.ImageUrl,
_121
Description = vacationProperty.Description,
_121
VacationPropertyId = vacationProperty.Id,
_121
VacationPropertyDescription = vacationProperty.Description,
_121
UserName = vacationProperty.User.Name,
_121
UserPhoneNumber = vacationProperty.User.PhoneNumber,
_121
};
_121
_121
return View(reservation);
_121
}
_121
_121
// POST: Reservations/Create
_121
[HttpPost]
_121
public async Task<ActionResult> Create(ReservationViewModel model)
_121
{
_121
if (ModelState.IsValid)
_121
{
_121
var reservation = new Reservation
_121
{
_121
Message = model.Message,
_121
PhoneNumber = model.UserPhoneNumber,
_121
Name = model.UserName,
_121
VactionPropertyId = model.VacationPropertyId,
_121
Status = ReservationStatus.Pending,
_121
CreatedAt = DateTime.Now
_121
};
_121
_121
await _reservationsRepository.CreateAsync(reservation);
_121
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
_121
await _notifier.SendNotificationAsync(reservation);
_121
_121
return RedirectToAction("Index", "VacationProperties");
_121
}
_121
_121
return View(model);
_121
}
_121
_121
// POST Reservations/Handle
_121
[HttpPost]
_121
[AllowAnonymous]
_121
public async Task<TwiMLResult> Handle(string from, string body)
_121
{
_121
string smsResponse;
_121
_121
try
_121
{
_121
var host = await _usersRepository.FindByPhoneNumberAsync(from);
_121
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
_121
_121
var smsRequest = body;
_121
reservation.Status =
_121
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
_121
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
_121
? ReservationStatus.Confirmed
_121
: ReservationStatus.Rejected;
_121
_121
await _reservationsRepository.UpdateAsync(reservation);
_121
smsResponse =
_121
string.Format("You have successfully {0} the reservation", reservation.Status);
_121
}
_121
catch (Exception)
_121
{
_121
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
_121
}
_121
_121
return TwiML(Respond(smsResponse));
_121
}
_121
_121
private static MessagingResponse Respond(string message)
_121
{
_121
var response = new MessagingResponse();
_121
response.Message(message);
_121
_121
return response;
_121
}
_121
}
_121
}

Let's have closer look at how Twilio webhooks are configured to enable incoming requests to our application.


Incoming Twilio Requests

incoming-twilio-requests page anchor

In the Twilio console(link takes you to an external page), you must setup the sms web hook to call your application's end point in the route Reservations/Handle.

SMS Webhook.

One way to expose your development machine to the outside world is using ngrok(link takes you to an external page). The url for the sms web hook on your number would look like this:


_10
http://<subdomain>.ngrok.io/Reservations/Handle

An incoming request from Twilio comes with some helpful parameters, such as a from phone number and the message body.

We'll use the from parameter to look for the host and check if he/she has any pending reservations. If he/she does, we'll use the message body to check for 'accept' and 'reject'.

In the last step, we'll use Twilio's TwiML as a response to Twilio to send an SMS message to the guest.

Accept or reject a reservation

accept-or-reject-a-reservation page anchor

AirTNG.Web/Controllers/ReservationsController.cs


_121
using System;
_121
using System.Threading.Tasks;
_121
using System.Web.Mvc;
_121
using AirTNG.Web.Domain.Reservations;
_121
using AirTNG.Web.Models;
_121
using AirTNG.Web.Models.Repository;
_121
using AirTNG.Web.ViewModels;
_121
using Twilio.AspNet.Mvc;
_121
using Twilio.TwiML;
_121
_121
namespace AirTNG.Web.Controllers
_121
{
_121
[Authorize]
_121
public class ReservationsController : TwilioController
_121
{
_121
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
_121
private readonly IReservationsRepository _reservationsRepository;
_121
private readonly IUsersRepository _usersRepository;
_121
private readonly INotifier _notifier;
_121
_121
public ReservationsController() : this(
_121
new VacationPropertiesRepository(),
_121
new ReservationsRepository(),
_121
new UsersRepository(),
_121
new Notifier()) { }
_121
_121
public ReservationsController(
_121
IVacationPropertiesRepository vacationPropertiesRepository,
_121
IReservationsRepository reservationsRepository,
_121
IUsersRepository usersRepository,
_121
INotifier notifier)
_121
{
_121
_vacationPropertiesRepository = vacationPropertiesRepository;
_121
_reservationsRepository = reservationsRepository;
_121
_usersRepository = usersRepository;
_121
_notifier = notifier;
_121
}
_121
_121
// GET: Reservations/Create
_121
public async Task<ActionResult> Create(int id)
_121
{
_121
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
_121
var reservation = new ReservationViewModel
_121
{
_121
ImageUrl = vacationProperty.ImageUrl,
_121
Description = vacationProperty.Description,
_121
VacationPropertyId = vacationProperty.Id,
_121
VacationPropertyDescription = vacationProperty.Description,
_121
UserName = vacationProperty.User.Name,
_121
UserPhoneNumber = vacationProperty.User.PhoneNumber,
_121
};
_121
_121
return View(reservation);
_121
}
_121
_121
// POST: Reservations/Create
_121
[HttpPost]
_121
public async Task<ActionResult> Create(ReservationViewModel model)
_121
{
_121
if (ModelState.IsValid)
_121
{
_121
var reservation = new Reservation
_121
{
_121
Message = model.Message,
_121
PhoneNumber = model.UserPhoneNumber,
_121
Name = model.UserName,
_121
VactionPropertyId = model.VacationPropertyId,
_121
Status = ReservationStatus.Pending,
_121
CreatedAt = DateTime.Now
_121
};
_121
_121
await _reservationsRepository.CreateAsync(reservation);
_121
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
_121
await _notifier.SendNotificationAsync(reservation);
_121
_121
return RedirectToAction("Index", "VacationProperties");
_121
}
_121
_121
return View(model);
_121
}
_121
_121
// POST Reservations/Handle
_121
[HttpPost]
_121
[AllowAnonymous]
_121
public async Task<TwiMLResult> Handle(string from, string body)
_121
{
_121
string smsResponse;
_121
_121
try
_121
{
_121
var host = await _usersRepository.FindByPhoneNumberAsync(from);
_121
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
_121
_121
var smsRequest = body;
_121
reservation.Status =
_121
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
_121
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
_121
? ReservationStatus.Confirmed
_121
: ReservationStatus.Rejected;
_121
_121
await _reservationsRepository.UpdateAsync(reservation);
_121
smsResponse =
_121
string.Format("You have successfully {0} the reservation", reservation.Status);
_121
}
_121
catch (Exception)
_121
{
_121
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
_121
}
_121
_121
return TwiML(Respond(smsResponse));
_121
}
_121
_121
private static MessagingResponse Respond(string message)
_121
{
_121
var response = new MessagingResponse();
_121
response.Message(message);
_121
_121
return response;
_121
}
_121
}
_121
}

Now that we know how to expose a webhook to handle Twilio requests, let's see how we generate the TwiML needed.


After updating the reservation status, we must notify the host that he/she has successfully confirmed or rejected the reservation. We also have to return a friendly error message if there are no pending reservations.

If the reservation is confirmed or rejected we send an additional SMS to the guest to deliver the news.

We use the verb Message from TwiML to instruct Twilio's server that it should send the SMS messages.

AirTNG.Web/Controllers/ReservationsController.cs


_121
using System;
_121
using System.Threading.Tasks;
_121
using System.Web.Mvc;
_121
using AirTNG.Web.Domain.Reservations;
_121
using AirTNG.Web.Models;
_121
using AirTNG.Web.Models.Repository;
_121
using AirTNG.Web.ViewModels;
_121
using Twilio.AspNet.Mvc;
_121
using Twilio.TwiML;
_121
_121
namespace AirTNG.Web.Controllers
_121
{
_121
[Authorize]
_121
public class ReservationsController : TwilioController
_121
{
_121
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
_121
private readonly IReservationsRepository _reservationsRepository;
_121
private readonly IUsersRepository _usersRepository;
_121
private readonly INotifier _notifier;
_121
_121
public ReservationsController() : this(
_121
new VacationPropertiesRepository(),
_121
new ReservationsRepository(),
_121
new UsersRepository(),
_121
new Notifier()) { }
_121
_121
public ReservationsController(
_121
IVacationPropertiesRepository vacationPropertiesRepository,
_121
IReservationsRepository reservationsRepository,
_121
IUsersRepository usersRepository,
_121
INotifier notifier)
_121
{
_121
_vacationPropertiesRepository = vacationPropertiesRepository;
_121
_reservationsRepository = reservationsRepository;
_121
_usersRepository = usersRepository;
_121
_notifier = notifier;
_121
}
_121
_121
// GET: Reservations/Create
_121
public async Task<ActionResult> Create(int id)
_121
{
_121
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
_121
var reservation = new ReservationViewModel
_121
{
_121
ImageUrl = vacationProperty.ImageUrl,
_121
Description = vacationProperty.Description,
_121
VacationPropertyId = vacationProperty.Id,
_121
VacationPropertyDescription = vacationProperty.Description,
_121
UserName = vacationProperty.User.Name,
_121
UserPhoneNumber = vacationProperty.User.PhoneNumber,
_121
};
_121
_121
return View(reservation);
_121
}
_121
_121
// POST: Reservations/Create
_121
[HttpPost]
_121
public async Task<ActionResult> Create(ReservationViewModel model)
_121
{
_121
if (ModelState.IsValid)
_121
{
_121
var reservation = new Reservation
_121
{
_121
Message = model.Message,
_121
PhoneNumber = model.UserPhoneNumber,
_121
Name = model.UserName,
_121
VactionPropertyId = model.VacationPropertyId,
_121
Status = ReservationStatus.Pending,
_121
CreatedAt = DateTime.Now
_121
};
_121
_121
await _reservationsRepository.CreateAsync(reservation);
_121
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
_121
await _notifier.SendNotificationAsync(reservation);
_121
_121
return RedirectToAction("Index", "VacationProperties");
_121
}
_121
_121
return View(model);
_121
}
_121
_121
// POST Reservations/Handle
_121
[HttpPost]
_121
[AllowAnonymous]
_121
public async Task<TwiMLResult> Handle(string from, string body)
_121
{
_121
string smsResponse;
_121
_121
try
_121
{
_121
var host = await _usersRepository.FindByPhoneNumberAsync(from);
_121
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
_121
_121
var smsRequest = body;
_121
reservation.Status =
_121
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
_121
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
_121
? ReservationStatus.Confirmed
_121
: ReservationStatus.Rejected;
_121
_121
await _reservationsRepository.UpdateAsync(reservation);
_121
smsResponse =
_121
string.Format("You have successfully {0} the reservation", reservation.Status);
_121
}
_121
catch (Exception)
_121
{
_121
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
_121
}
_121
_121
return TwiML(Respond(smsResponse));
_121
}
_121
_121
private static MessagingResponse Respond(string message)
_121
{
_121
var response = new MessagingResponse();
_121
response.Message(message);
_121
_121
return response;
_121
}
_121
}
_121
}

Congratulations! You've just learned how to automate your workflow with Twilio SMS.

Next, lets see what else we can do with the Twilio C# SDK.


If you're a .NET developer working with Twilio you know we've got a lot of great content here on the Docs site. Here are just a couple ideas for your next tutorial:

IVR: Phone Tree

Easily route callers to the right people and information with an IVR (interactive voice response) system.

Automated Survey

Instantly collect structured data from your users with a survey conducted over a voice call or SMS text messages.

Did this help?

did-this-help page anchor

Thanks for checking out this tutorial! Tweet to us @twilio(link takes you to an external page) with what you're building!


Rate this page: