title: ASP.NET Core Create Outlook Calendar Reminder with Create Action Method
body: <p>I'm using ASP.NET Core 2.0 MVC, C#, EntityFramework Code First and SQL Server 2016.</p>
  
  <p>I have a web form for users to fill out. The end user must enter their first and last names, select their department from a drop down list and select a date value from a drop down list.</p>
  
  <p>Once the end user submit's their data a confirmation page is shown with the values they entered and selected. Is there a way to generate an Outlook calendar file on the confirmation page. So when they click that file it adds a calendar reminder based on the date they selected from the drop down list.</p>
  
  <p>I'm going to post my code if needed.</p>
  
  <p><strong>Models</strong></p>
  
  <pre><code>public class Employee
  {
      public int EmployeeID { get; set; }
      public string FirstName { get; set; }
      public string LastName { get; set; }
  
      public int DepartmentID { get; set; }
      public Department Department { get; set; }
  
      public int AppointmentID { get; set; }
      public Appointment Appointment { get; set; }
  }
  
  public class Department
  {
      public int DepartmentID { get; set; }
      public string Name { get; set; }
  
      public ICollection&lt;Employee&gt; Employees { get; set; }
  }
  
  public class Appointment
  {
      public int AppointmentID { get; set; }
      public string TimeSlot { get; set; }
  
      public ICollection&lt;Employee&gt; Employees { get; set; }
  }
  </code></pre>
  
  <p><strong>DbContext</strong></p>
  
  <pre><code>public class WinTenDbContext : DbContext
  {
      public WinTenDbContext(DbContextOptions&lt;WinTenDbContext&gt; options) : base(options)
      { }
  
      public DbSet&lt;Employee&gt; Employees { get; set; }
      public DbSet&lt;Department&gt; Departments { get; set; }
      public DbSet&lt;Appointment&gt; Appointments { get; set; }
  
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
          modelBuilder.Entity&lt;Employee&gt;()
              .HasKey(e =&gt; e.EmployeeID);
  
          modelBuilder.Entity&lt;Employee&gt;()
              .Property(e =&gt; e.FirstName)
              .HasColumnType("varchar(50)")
              .HasMaxLength(50)
              .IsRequired();
  
          modelBuilder.Entity&lt;Employee&gt;()
              .Property(e =&gt; e.LastName)
              .HasColumnType("varchar(50)")
              .HasMaxLength(50)
              .IsRequired();
  
          modelBuilder.Entity&lt;Department&gt;()
              .HasKey(d =&gt; d.DepartmentID);
  
          modelBuilder.Entity&lt;Department&gt;()
              .Property(d =&gt; d.Name)
              .HasColumnType("varchar(50)")
              .HasMaxLength(50);
  
          modelBuilder.Entity&lt;Appointment&gt;()
              .HasKey(a =&gt; a.AppointmentID);
  
          modelBuilder.Entity&lt;Appointment&gt;()
              .Property(a =&gt; a.TimeSlot)
              .HasColumnType("varchar(50)")
              .HasMaxLength(50);
      }
  }
  </code></pre>
  
  <p><strong>ViewModels</strong></p>
  
  <pre><code>public class EmployeeFormVM
  {
      public int EmployeeID { get; set; }
  
      [Required(ErrorMessage = "Please enter your First Name")]
      [Display(Name = "First Name")]
      [StringLength(50)]
      public string FirstName { get; set; }
  
      [Required(ErrorMessage = "Please enter your Last Name")]
      [Display(Name = "Last Name")]
      [StringLength(50)]
      public string LastName { get; set; }
  
      [Required(ErrorMessage = "Please select your Department")]
      [Display(Name = "Department")]
      public int DepartmentID { get; set; }
  
      public IEnumerable&lt;Department&gt; Departments { get; set; }
  
      [Required(ErrorMessage = "Please select your Appointment")]
      [Display(Name = "Appointment")]
      public int AppointmentID { get; set; }
  
      public IEnumerable&lt;Appointment&gt; Appointments { get; set; }
  }
  </code></pre>
  
  <p><strong>EmployeesController</strong></p>
  
  <pre><code>public class EmployeesController : Controller
  {
      private readonly WinTenDbContext _context;
  
      public EmployeesController(WinTenDbContext context)
      {
          _context = context;
      }
  
      public async Task&lt;IActionResult&gt; Index(string sortOrder, string currentFilter, string searchString, int? page)
      {
          ViewData["CurrentSort"] = sortOrder;
          ViewData["FirstNameSortParm"] = sortOrder == "fname" ? "fname_desc" : "fname";
          ViewData["LastNameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "lname_desc" : "";
          ViewData["DeptNameSortParm"] = sortOrder == "deptname" ? "deptname_desc" : "deptname";
          ViewData["DateSortParm"] = sortOrder == "time_slot" ? "time_slot_desc" : "time_slot";
  
          if (searchString != null)
          {
              page = 1;
          }
          else
          {
              searchString = currentFilter;
          }
  
          ViewData["CurrentFilter"] = searchString;
  
          var employees = from s in _context.Employees.Include(e =&gt; e.Appointment).Include(e =&gt; e.Department)
                          select s;
  
          if (!String.IsNullOrEmpty(searchString))
          {
              employees = employees.Where(s =&gt; s.LastName.Contains(searchString)
                                     || s.FirstName.Contains(searchString));
          }
  
          switch (sortOrder)
          {
              case "fname":
                  employees = employees.OrderBy(s =&gt; s.FirstName);
                  break;
              case "fname_desc":
                  employees = employees.OrderByDescending(s =&gt; s.FirstName);
                  break;
              case "lname_desc":
                  employees = employees.OrderByDescending(s =&gt; s.LastName);
                  break;
              case "deptname":
                  employees = employees.OrderBy(s =&gt; s.Department.Name);
                  break;
              case "deptname_desc":
                  employees = employees.OrderByDescending(s =&gt; s.Department.Name);
                  break;
              case "time_slot":
                  employees = employees.OrderBy(s =&gt; s.Appointment.AppointmentID);
                  break;
              case "time_slot_desc":
                  employees = employees.OrderByDescending(s =&gt; s.Appointment.AppointmentID);
                  break;
              default:
                  employees = employees.OrderBy(s =&gt; s.LastName);
                  break;
          }
  
          int pageSize = 10;
          return View(await PaginatedList&lt;Employee&gt;.CreateAsync(employees.AsNoTracking(), page ?? 1, pageSize));
      }
  
      // GET: Employees/Details/5
      public async Task&lt;IActionResult&gt; Details(int? id)
      {
          if (id == null)
          {
              return NotFound();
          }
  
          var employee = await _context.Employees
              .Include(e =&gt; e.Appointment)
              .Include(e =&gt; e.Department)
              .SingleOrDefaultAsync(m =&gt; m.EmployeeID == id);
          if (employee == null)
          {
              return NotFound();
          }
  
          return View(employee);
      }
  
      // GET: Employees/Confirmation/5
      public async Task&lt;IActionResult&gt; Confirmation(int? id)
      {
          if (id == null)
          {
              return NotFound();
          }
  
          var employee = await _context.Employees.Include(d =&gt; d.Department).Include(a =&gt; a.Appointment)
              .SingleOrDefaultAsync(m =&gt; m.EmployeeID == id);
  
          if (employee == null)
          {
              return NotFound();
          }
  
          return View(employee);
      }
  
      // GET: Employees/Create
      public IActionResult Create()
      {
          var departments = _context.Departments.ToList();
          var appointments = _context.Appointments.Include(x =&gt; x.Employees).Where(x =&gt; !x.Em‌​ployees.Any()).ToLis‌​t();
  
          var viewModel = new EmployeeFormVM
          {
              Departments = departments,
              Appointments = appointments
          };
  
          return View(viewModel);
      }
  
      [HttpPost]
      [ValidateAntiForgeryToken]
      public async Task&lt;IActionResult&gt; Create(EmployeeFormVM employee)
      {
          if (ModelState.IsValid)
          {
              var emp = new Employee();
              {
                  emp.FirstName = employee.FirstName;
                  emp.LastName = employee.LastName;
                  emp.DepartmentID = employee.DepartmentID;
                  emp.AppointmentID = employee.AppointmentID;
              }
  
              // Query DB to check if Employee exists with same First/Last Name
              Employee existingEmployee = await _context.Employees.SingleOrDefaultAsync(m =&gt; m.FirstName == employee.FirstName &amp;&amp; m.LastName == employee.LastName);
              if (existingEmployee != null)
              {
                  // Display Error if duplicate employee
                  ModelState.AddModelError(string.Empty, "An employee with this name has already registered. Please contact the Service Desk for any scheduling conflicts.");
                  employee.Departments = _context.Departments.ToList();
                  //employee.Appointments = _context.Appointments.ToList();
                  employee.Appointments = _context.Appointments.ToList();
                  return View(employee);
              }
  
              // Query DB to check if appointment has already been assigned to an employee
              Employee existingAppointment = await _context.Employees.SingleOrDefaultAsync(m =&gt; m.AppointmentID == employee.AppointmentID);
              if (existingAppointment != null)
              {
                  // Display error if the appointment was already chosen
                  ModelState.AddModelError(string.Empty, "This appointment has already been taken. Please select another timeslot.");
                  employee.Departments = _context.Departments.ToList();
                  //employee.Appointments = _context.Appointments.ToList();
                  employee.Appointments = _context.Appointments.ToList();
                  return View(employee);
              }
  
              _context.Add(emp);
              await _context.SaveChangesAsync();
              //return RedirectToAction(nameof(Index));
  
              var newlyCreatedId = emp.EmployeeID;
              return RedirectToAction(nameof(Confirmation), new { id = newlyCreatedId });
          }
          return View(employee);
      }
  
      // GET: Employees/Edit/5
      public async Task&lt;IActionResult&gt; Edit(int? id)
      {
          if (id == null)
          {
              return NotFound();
          }
  
          var employeevm = new EmployeeFormVM();
          {
              Employee employee = await _context.Employees.SingleOrDefaultAsync(m =&gt; m.EmployeeID == id);
  
              if (employee == null)
              {
                  return NotFound();
              }
  
              employeevm.EmployeeID = employee.EmployeeID;
              employeevm.FirstName = employee.FirstName;
              employeevm.LastName = employee.LastName;
  
              // Retrieve list of Departments
              var departments = _context.Departments.ToList();
              employeevm.Departments = departments;
              // Set the selected department
              employeevm.DepartmentID = employee.DepartmentID;
  
              // Retrieve list of Appointments
              var appointments = _context.Appointments.ToList();
              employeevm.Appointments = appointments;
              // Set the selected department
              employeevm.AppointmentID = employee.AppointmentID;
          }
          return View(employeevm);
      }
  
      // POST: Employees/Edit/5
      // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
      // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
      [HttpPost]
      [ValidateAntiForgeryToken]
      public async Task&lt;IActionResult&gt; Edit(EmployeeFormVM vmEdit)
      {
          if (ModelState.IsValid)
          {
              Employee employee = _context.Employees.SingleOrDefault(e =&gt; e.EmployeeID == vmEdit.EmployeeID);
  
              if (employee == null)
              {
                  return NotFound();
              }
  
              employee.FirstName = vmEdit.FirstName;
              employee.LastName = vmEdit.LastName;
              employee.DepartmentID = vmEdit.DepartmentID;
              employee.AppointmentID = vmEdit.AppointmentID;
  
              try
              {
                  _context.Update(employee);
                  await _context.SaveChangesAsync();
              }
              catch (DbUpdateConcurrencyException)
              {
                  if (!EmployeeExists(vmEdit.EmployeeID))
                  {
                      return NotFound();
                  }
                  else
                  {
                      throw;
                  }
              }
              return RedirectToAction(nameof(Index));
          }
          return View(vmEdit);
      }
  
      // GET: Employees/Delete/5
      public async Task&lt;IActionResult&gt; Delete(int? id)
      {
          if (id == null)
          {
              return NotFound();
          }
  
          var employee = await _context.Employees
              .Include(e =&gt; e.Appointment)
              .Include(e =&gt; e.Department)
              .SingleOrDefaultAsync(m =&gt; m.EmployeeID == id);
          if (employee == null)
          {
              return NotFound();
          }
  
          return View(employee);
      }
  
      // POST: Employees/Delete/5
      [HttpPost, ActionName("Delete")]
      [ValidateAntiForgeryToken]
      public async Task&lt;IActionResult&gt; DeleteConfirmed(int id)
      {
          var employee = await _context.Employees.SingleOrDefaultAsync(m =&gt; m.EmployeeID == id);
          _context.Employees.Remove(employee);
          await _context.SaveChangesAsync();
          return RedirectToAction(nameof(Index));
      }
  
      private bool EmployeeExists(int id)
      {
          return _context.Employees.Any(e =&gt; e.EmployeeID == id);
      }
  }
  </code></pre>
  
  <p><strong>Create View</strong></p>
  
  <pre><code>@using (Html.BeginForm("Create", "Employees"))
      {
          @Html.ValidationSummary(true, "", new { @class = "validation-summary-errors" })
  
          &lt;div class="form-group"&gt;
              @Html.LabelFor(e =&gt; e.FirstName)
              @Html.TextBoxFor(e =&gt; e.FirstName, new { @class = "form-control" })
              @Html.ValidationMessageFor(e =&gt; e.FirstName)
          &lt;/div&gt;
  
          &lt;div class="form-group"&gt;
              @Html.LabelFor(e =&gt; e.LastName)
              @Html.TextBoxFor(e =&gt; e.LastName, new { @class = "form-control" })
              @Html.ValidationMessageFor(e =&gt; e.LastName)
          &lt;/div&gt;
  
          &lt;div class="form-group"&gt;
              @Html.LabelFor(d =&gt; d.DepartmentID)
              @Html.DropDownListFor(d =&gt; d.DepartmentID, new SelectList(Model.Departments, "DepartmentID", "Name"), "", new { @class = "form-control" })
              @Html.ValidationMessageFor(d =&gt; d.DepartmentID)
          &lt;/div&gt;
  
          &lt;div class="form-group"&gt;
              @Html.LabelFor(a =&gt; a.AppointmentID)
              @Html.DropDownListFor(a =&gt; a.AppointmentID, new SelectList(Model.Appointments, "AppointmentID", "TimeSlot"), "", new { @class = "form-control" })
              @Html.ValidationMessageFor(a =&gt; a.AppointmentID)
          &lt;/div&gt;
  
          &lt;div class="form-group"&gt;
              &lt;button type="submit" class="btn btn-primary"&gt;Submit&lt;/button&gt;
          &lt;/div&gt;
      }
  </code></pre>
  
  <p><strong>Confirmation View</strong></p>
  
  <pre><code>&lt;div class="row"&gt;
  &lt;div class="col-md-12"&gt;
      &lt;img src="~/images/confirm.png" /&gt;
      &lt;h2&gt;Thank you @Html.DisplayFor(model =&gt; model.FirstName) @Html.DisplayFor(model =&gt; model.LastName)!&lt;/h2&gt;    
      &lt;p&gt;Your &lt;b&gt;@Html.DisplayFor(model =&gt; model.Appointment.TimeSlot)&lt;/b&gt; appointment has been booked. If you need to reschedule this appointment, please call the Service Desk at x1380.&lt;/p&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p></p>