Search code examples
asp.net-mvcquartz.netpostal

Error when sending email using postal MVC?


Requirement:

Send emails to all users dynamically everyday at a particular time, say 6:00 AM.

What I did so far:

I use a third party library called Quartz.net from Nuget.

public class TaskScheduler : IJob
{
    public void Execute(IJobExecutionContext context)
    {
        try {
            UserRepository userRepo = new UserRepository();
            var users = userRepo.GetUsers();

            DashBoardRepository repo = new DashBoardRepository();
            foreach (var rec in users)
            {
                var tasks = repo.GetIncompleteTasks(rec.UserID);
                var appointments = repo.GetUpcomingAppointments(rec.UserID);

                if (tasks.Count > 0 || appointments.Count > 0)
                {
                    dynamic email = new Email("TaskEmail");
                    email.To = rec.Email;
                    email.From = "[email protected]";
                    email.Subject = "Pending items in XYZ";
                    email.Tasks = tasks;
                    email.Appointments = appointments;
                    email.Send();
                }
            }
        }
        catch(Exception ex)
        {
            clsLog.LogMessageToFile(ex.ToString());
        }
    }
}

public static class JobScheduler
{
    public static void Start()
    {
        IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
        scheduler.Start();

        IJobDetail job = JobBuilder.Create<TaskScheduler>().Build();

        ITrigger trigger = TriggerBuilder.Create()
            .WithDailyTimeIntervalSchedule
              (s =>
                s.WithIntervalInHours(24)
                .OnEveryDay()
                .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(6, 0))
              )
            .Build();

        scheduler.ScheduleJob(job, trigger);
    }
}

protected void Application_Start()
    {           
        JobScheduler.Start();
    }

I am using Postal in MVC to send the email.

This works fine in my local if I run from my Visual Studio. But once I deploy it onto IIS, I get the error below:

   System.ArgumentException: The virtual path '/' maps to another application, which is not allowed.
   at System.Web.CachedPathData.GetVirtualPathData(VirtualPath virtualPath, Boolean permitPathsOutsideApp)
   at System.Web.HttpContext.GetFilePathData()
    -------------

I am hosting this application as a sub directory in the IIS. So it is something like http://www.maindemosite.com/xyz

enter image description here

Web.Config:

    <?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=301880
  -->
<configuration>
  <configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5v361934e089" requirePermission="false" />
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
  </configSections>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-XYZ-201405202911.mdf;Initial Catalog=aspnet-XYZ-20140534102911;Integrated Security=True" providerName="System.Data.SqlClient" />
    <add name="XYZEntities" connectionString="metadata=res://*/DAL.XYZEntities.csdl|res://*/DAL.XYZEntities.ssdl|res://*/DAL.XYZEntities.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=SERVER\MSSQL;initial catalog=XYZTest;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="AuthenticationSlide" value="100" />
    <add key="DocumentsPath" value="C:/Users/KKK/Documents/Documents/" />
    <add key="XYZDeployment" value="http://localhost:51641" />
  </appSettings>
  <system.identityModel.services>
    <federationConfiguration>
      <cookieHandler requireSsl="false" name="ABCAuthorization" />
    </federationConfiguration>
  </system.identityModel.services>
  <system.net>
    <mailSettings>
      <smtp>
        <network host="smtp.ABC.com" port="25" userName="[email protected]" password="!@abc99" />
      </smtp>
    </mailSettings>
  </system.net>
  <system.identityModel>
    <identityConfiguration>
      <securityTokenHandlers>
        <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </securityTokenHandlers>
    </identityConfiguration>
  </system.identityModel>
  <system.web>
    <sessionState mode="InProc" timeout="1" />
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" maxRequestLength="1048576" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>
  </system.web>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </modules>
  <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers></system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.AspNet.Identity.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Practices.Unity" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <system.serviceModel>
    <bindings />
    <client />
  </system.serviceModel>
</configuration>

Complete Error Message:

    Log Entry : 10:09:40 AM Friday, October 14, 2016
  :
  :System.ArgumentException: The virtual path '/' maps to another application, which is not allowed.
   at System.Web.CachedPathData.GetVirtualPathData(VirtualPath virtualPath, Boolean permitPathsOutsideApp)
   at System.Web.HttpContext.GetFilePathData()
   at System.Web.Configuration.RuntimeConfig.GetConfig(HttpContext context)
   at System.Web.Configuration.HttpCapabilitiesBase.GetBrowserCapabilities(HttpRequest request)
   at System.Web.HttpRequest.get_Browser()
   at System.Web.HttpRequestWrapper.get_Browser()
   at System.Web.WebPages.BrowserHelpers.GetOverriddenBrowser(HttpContextBase httpContext, Func`2 createBrowser)
   at System.Web.WebPages.DisplayModeProvider.<.ctor>b__2(HttpContextBase context)
   at System.Web.WebPages.DisplayModeProvider.<GetAvailableDisplayModesForContext>d__4.MoveNext()
   at System.Web.Mvc.VirtualPathProviderViewEngine.GetPath(ControllerContext controllerContext, String[] locations, String[] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String[]& searchedLocations)
   at System.Web.Mvc.VirtualPathProviderViewEngine.FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
   at System.Web.Mvc.ViewEngineCollection.<>c__DisplayClass6.<FindView>b__4(IViewEngine e)
   at System.Web.Mvc.ViewEngineCollection.Find(Func`2 lookup, Boolean trackSearchedPaths)
   at System.Web.Mvc.ViewEngineCollection.FindView(ControllerContext controllerContext, String viewName, String masterName)
   at Postal.EmailViewRenderer.CreateView(String viewName, ControllerContext controllerContext)
   at Postal.EmailViewRenderer.Render(Email email, String viewName)
   at Postal.EmailService.Send(Email email)
   at XYZ.Utilities.TaskScheduler.Execute(IJobExecutionContext context) in D:\Applications\XYZ\Utilities\TaskScheduler.cs:line 33

Solution

  • According to this issue: Postal Issue #65, it seems that you have HttpContext.Current contains null value when trying to get relative path of your project root directory in IIS deployment server. Here was the checklist to do:

    1. Try moving JobScheduler.Start() method from Application_Start to Application_AuthenticateRequest method call during application startup. AFAIK, the Application_Start method doesn't contain HttpContext.Current instance as Application_AuthenticateRequest has.

      protected void Application_AuthenticateRequest()
      {
          JobScheduler.Start();
      }
      
    2. Check if your IIS deployment server setting set to integrated mode. In this mode Application_Start doesn't contain HttpContext.Current due to design changes on integrated pipeline. As Mike Volodarsky said:

    Basically, if you happen to be accessing the request context in Application_Start, you have two choices:

    1. Change your application code to not use the request context (recommended).
    2. Move the application to Classic mode (NOT recommended).

    The first choice means it was some alternatives beside Application_AuthenticateRequest, which uses Application_BeginRequest or any other event methods that includes HttpContext.Current instance.

    1. If absolute path was used, check if it is exist on deployment server. The relative path is more preferred in web.config file, thus you may change paths like this:

      <add key="DocumentsPath" value="~/Documents/Documents/" />
      

    Afterwards, ensure Application_Start method doesn't contain any other method that contains HttpContext.Current instance, including database context if any.

    References:

    Why is HttpContext.Current null?

    IIS7 Integrated mode: Request is not available in this context exception in Application_Start

    Related problem:

    Request is not available in this context