c# - Thread.Timer With HttpContext 不起作用

标签 c# asp.net-mvc multithreading thread-safety renderpartial

您好,我正在尝试在找到代码的特定时间发送电子邮件。 在 global.asax.cs 文件上

protected void Application_Start()
{
    ETHOS.Controllers.HomeController mail = new Controllers.HomeController();
    mail.ScheduleService();
    private void SchedularCallback(object e)
    {
        this.WriteToFile("Simple Service Log: {0}");
        getData();//Email function
        this.ScheduleService();
    }
    public void ScheduleService()
    {
        try
        {
            Schedular = new Timer(new TimerCallback(SchedularCallback));
            string mode = "DAILY";
            this.WriteToFile("Simple Service Mode: " + mode + " {0}");
            //Set the Default Time.
            //DateTime d = DateTime.Today;
            //TimeSpan t = new TimeSpan(12, 40, 00);
            //DateTime scheduledTime = d.Date + t;
            DateTime scheduledTime = DateTime.Now.AddSeconds(30);
            if (DateTime.Now > scheduledTime)
            {
                //If Scheduled Time is passed set Schedule for the next day.
                // scheduledTime = scheduledTime.AddDays(1);
                scheduledTime = scheduledTime.AddDays(1);
            }
            TimeSpan timeSpan = scheduledTime.Subtract(DateTime.Now);
            string schedule = string.Format("{0} day(s) {1} hour(s) {2} minute(s) {3} seconds(s)", timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
            this.WriteToFile("Simple Service scheduled to run after: " + schedule + " {0}");
            //Get the difference in Minutes between the Scheduled and Current Time.
            int dueTime = Convert.ToInt32(timeSpan.TotalMilliseconds);

            //Change the Timer's Due Time.
            Schedular.Change(dueTime, Timeout.Infinite);
        }
        catch (Exception ex)
        {
            WriteToFile("Simple Service Error on: {0} " + ex.Message + ex.StackTrace);
        }
    }

在电子邮件功能中,我通过 gmail 发送电子邮件,在正文上,我将部分 View 渲染为字符串类,以将 Razor View 渲染为字符串

 string body = ViewRenderer.RenderPartialView("~/Views/Shared/Email.cshtml", LEM);

问题是它没有获取 HttpContext.Current。它显示 HttpContext.Current = null。我认为两者都在创建线程,所以这就是为什么它没有获得 HttpContext 那么我如何才能将两者与相同的 httpcontext 一起使用。 这是下面的 RendererView 类

   public class ViewRenderer:ETHOS.Controllers.HomeController
      {/// <summary>
    /// Required Controller Context
    /// </summary>
    protected ControllerContext Context { get; set; }

    /// <summary>
    /// Initializes the ViewRenderer with a Context.
    /// </summary>
    /// <param name="controllerContext">
    /// If you are running within the context of an ASP.NET MVC request pass in
    /// the controller's context. 
    /// Only leave out the context if no context is otherwise available.
    /// </param>
    public ViewRenderer(ControllerContext controllerContext = null)
    {
        System.Web.HttpContext ctx =  (System.Web.HttpContext) Session["ctx"];
        // Create a known controller from HttpContext if no context is passed
        if (controllerContext == null)
        {
            if (System.Web.HttpContext.Current != null)
                controllerContext = CreateController<EmptyController>().ControllerContext;
            else
                throw new InvalidOperationException(
                    "ViewRenderer must run in the context of an ASP.NET " +
                    "Application and requires HttpContext.Current to be present.");
        }
        Context = controllerContext;
    }

    /// <summary>
    /// Renders a full MVC view to a string. Will render with the full MVC
    /// View engine including running _ViewStart and merging into _Layout        
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to render the view with</param>
    /// <returns>String of the rendered view or null on error</returns>
    public string RenderViewToString(string viewPath, object model = null)
    {
        return RenderViewToStringInternal(viewPath, model, false);
    }

    /// <summary>
    /// Renders a full MVC view to a writer. Will render with the full MVC
    /// View engine including running _ViewStart and merging into _Layout        
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to render the view with</param>
    /// <returns>String of the rendered view or null on error</returns>
    public void RenderView(string viewPath, object model, TextWriter writer)
    {
        RenderViewToWriterInternal(viewPath, writer, model, false);
    }


    /// <summary>
    /// Renders a partial MVC view to string. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <returns>String of the rendered view or null on error</returns>
    public string RenderPartialViewToString(string viewPath, object model = null)
    {
        return RenderViewToStringInternal(viewPath, model, true);
    }

    /// <summary>
    /// Renders a partial MVC view to given Writer. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="writer">Writer to render the view to</param>
    public void RenderPartialView(string viewPath, object model, TextWriter writer)
    {
        RenderViewToWriterInternal(viewPath, writer, model, true);
    }

    /// <summary>
    /// Renders a partial MVC view to string. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="controllerContext">Active Controller context</param>
    /// <returns>String of the rendered view or null on error</returns>
    public static string RenderView(string viewPath, object model = null,
                                    ControllerContext controllerContext = null)
    {
        ViewRenderer renderer = new ViewRenderer(controllerContext);
        return renderer.RenderViewToString(viewPath, model);
    }

    /// <summary>
    /// Renders a partial MVC view to the given writer. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="writer">Writer to render the view to</param>
    /// <param name="controllerContext">Active Controller context</param>
    /// <returns>String of the rendered view or null on error</returns>
    public static void RenderView(string viewPath, TextWriter writer, object model,
                                    ControllerContext controllerContext)
    {
        ViewRenderer renderer = new ViewRenderer(controllerContext);
        renderer.RenderView(viewPath, model, writer);
    }

    /// <summary>
    /// Renders a partial MVC view to string. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="controllerContext">Active Controller context</param>
    /// <param name="errorMessage">optional out parameter that captures an error message instead of throwing</param>
    /// <returns>String of the rendered view or null on error</returns>
    public static string RenderView(string viewPath, object model,
                                    ControllerContext controllerContext,
                                    out string errorMessage)
    {
        errorMessage = null;
        try
        {
            ViewRenderer renderer = new ViewRenderer(controllerContext);
            return renderer.RenderViewToString(viewPath, model);
        }
        catch (Exception ex)
        {
            errorMessage = ex.GetBaseException().Message;
        }
        return null;
    }

    /// <summary>
    /// Renders a partial MVC view to the given writer. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="controllerContext">Active Controller context</param>
    /// <param name="writer">Writer to render the view to</param>
    /// <param name="errorMessage">optional out parameter that captures an error message instead of throwing</param>
    /// <returns>String of the rendered view or null on error</returns>
    public static void RenderView(string viewPath, object model, TextWriter writer,
                                    ControllerContext controllerContext,
                                    out string errorMessage)
    {
        errorMessage = null;
        try
        {
            ViewRenderer renderer = new ViewRenderer(controllerContext);
            renderer.RenderView(viewPath, model, writer);
        }
        catch (Exception ex)
        {
            errorMessage = ex.GetBaseException().Message;
        }
    }


    /// <summary>
    /// Renders a partial MVC view to string. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="controllerContext">Active controller context</param>
    /// <returns>String of the rendered view or null on error</returns>
    public static string RenderPartialView(string viewPath, object model = null,
                                            ControllerContext controllerContext = null)
    {
        ViewRenderer renderer = new ViewRenderer(controllerContext);
        return renderer.RenderPartialViewToString(viewPath, model);
    }

    /// <summary>
    /// Renders a partial MVC view to string. Use this method to render
    /// a partial view that doesn't merge with _Layout and doesn't fire
    /// _ViewStart.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">The model to pass to the viewRenderer</param>
    /// <param name="controllerContext">Active controller context</param>
    /// <param name="writer">Text writer to render view to</param>
    /// <param name="errorMessage">optional output parameter to receive an error message on failure</param>
    public static void RenderPartialView(string viewPath, TextWriter writer, object model = null,
                                            ControllerContext controllerContext = null)
    {
        ViewRenderer renderer = new ViewRenderer(controllerContext);
        renderer.RenderPartialView(viewPath, model, writer);
    }


    /// <summary>
    /// Internal method that handles rendering of either partial or 
    /// or full views.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">Model to render the view with</param>
    /// <param name="partial">Determines whether to render a full or partial view</param>
    /// <param name="writer">Text writer to render view to</param>
    protected void RenderViewToWriterInternal(string viewPath, TextWriter writer, object model = null, bool partial = false)
    {
        // first find the ViewEngine for this view
        ViewEngineResult viewEngineResult = null;
        if (partial)
            viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
        else
            viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);

        if (viewEngineResult == null)
            throw new FileNotFoundException();

        // get the view and attach the model to view data
        var view = viewEngineResult.View;
        Context.Controller.ViewData.Model = model;

        var ctx = new ViewContext(Context, view,
                                    Context.Controller.ViewData,
                                    Context.Controller.TempData,
                                    writer);
        view.Render(ctx, writer);
    }

    /// <summary>
    /// Internal method that handles rendering of either partial or 
    /// or full views.
    /// </summary>
    /// <param name="viewPath">
    /// The path to the view to render. Either in same controller, shared by 
    /// name or as fully qualified ~/ path including extension
    /// </param>
    /// <param name="model">Model to render the view with</param>
    /// <param name="partial">Determines whether to render a full or partial view</param>
    /// <returns>String of the rendered view</returns>
    private string RenderViewToStringInternal(string viewPath, object model,
                                                bool partial = false)
    {
        // first find the ViewEngine for this view
        ViewEngineResult viewEngineResult = null;
        if (partial)
            viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
        else
            viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);

        if (viewEngineResult == null || viewEngineResult.View == null)
            throw new FileNotFoundException();//Resources.ViewCouldNotBeFound);

        // get the view and attach the model to view data
        var view = viewEngineResult.View;
        Context.Controller.ViewData.Model = model;

        string result = null;

        using (var sw = new StringWriter())
        {
            var ctx = new ViewContext(Context, view,
                                        Context.Controller.ViewData,
                                        Context.Controller.TempData,
                                        sw);
            view.Render(ctx, sw);
            result = sw.ToString();
        }

        return result;
    }


    /// <summary>
    /// Creates an instance of an MVC controller from scratch 
    /// when no existing ControllerContext is present       
    /// </summary>
    /// <typeparam name="T">Type of the controller to create</typeparam>
    /// <returns>Controller for T</returns>
    /// <exception cref="InvalidOperationException">thrown if HttpContext not available</exception>
    public static T CreateController<T>(RouteData routeData = null, params object[] parameters)
                where T : Controller, new()
    {
        // create a disconnected controller instance
        T controller = (T)Activator.CreateInstance(typeof(T), parameters);

        // get context wrapper from HttpContext if available
        HttpContextBase wrapper = null;
        if (System.Web.HttpContext.Current != null)
            wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
        else
            throw new InvalidOperationException(
                "Can't create Controller Context if no active HttpContext instance is available.");

        if (routeData == null)
            routeData = new RouteData();

        // add the controller routing if not existing
        if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
            routeData.Values.Add("controller", controller.GetType().Name
                                                        .ToLower()
                                                        .Replace("controller", ""));

        controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
        return controller;
    }

}

/// <summary>
/// Empty MVC Controller instance used to 
/// instantiate and provide a new ControllerContext
/// for the ViewRenderer
/// </summary>
public class EmptyController : Controller
{
}

最佳答案

HttpContext 仅在 HTTP 请求的生命周期内可用。在此类后台线程中不可能在外部使用它。 implement recurring background tasks in an ASP.NET applications 也被认为是不好的做法。 。建议的方法是从 Web 应用程序中卸载此任务,并将其放入 Windows 服务或控制台应用程序中,Windows 计划程序将定期执行该任务。

话虽这么说,如果您决定违背推荐的方法并仍然坚持这样做,您可以考虑 using the Razor engine outside of an ASP.NET application这样您就不会依赖 HttpContext

关于c# - Thread.Timer With HttpContext 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41760705/

相关文章:

c# - 按字符串值获取类

javascript - 在不返回服务器的情况下动态加载图像的最佳方法是什么?

java - Thread.sleep 暂停整个程序

c# - 获取 Blob 容器中特定前缀内所有 Uri 的名称

C# 在 switch 语句中没有隐式失败

c# - SendGrid 解析入站 header

asp.net - 默认模型绑定(bind)器和包含列表的复杂类型

c# - 从 C# 中的 JsonResult 中删除一个元素

java - 多线程 : the application doesn't produce same result every time

linux - Unix 环境中的多线程/并行 Bash 脚本