1

我正在使用 ASP.NET MVC3
我有一个.cshtml视图,我想将其字符串化以合并到电子邮件正文中。
这是我使用的方法:

//Renders a view to a string
private string RenderRazorViewToString(string viewName, object model)
{
    ViewData.Model = model;

    using (var sw = new System.IO.StringWriter())
    {
        var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);
        viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
        
        return sw.GetStringBuilder().ToString();
    }
}

当我从ActionResult从 Ajax 调用中调用的方法中调用此方法时,这非常有效

但是,我面临一个不寻常的情况:

在我的Global.asax文件中,我有一个每 10 分钟调用一次的方法,其目标是验证最近 10 分钟内是否在数据库中创建了一些特殊记录,如果是,则发送电子邮件。当然,邮件正文就是这个字符串化的视图。

这是我的一段代码:这个方法非常受这篇文章的启发

/* File : Gloabal.asax.cs */

private static CacheItemRemovedCallback OnMatchingCacheRemove = null;

protected void Application_Start()
{
    // ...
    AddMatchingTask("SendEmail", 600);
}

private void AddMatchingTask(string name, int seconds)
{
    OnMatchingCacheRemove = new CacheItemRemovedCallback(CacheItemMatchingRemoved);
    HttpRuntime.Cache.Insert(name, seconds, null, DateTime.UtcNow.AddSeconds(seconds), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, OnMatchingCacheRemove);
}


//This method is called every 600 seconds
public void CacheItemMatchingRemoved(string k, object v, CacheItemRemovedReason r)
{
    using (MyEntities context = new MyEntities())
    {
        var qMatching = from m in context.MY_TABLE
                        where m.IsNew == true
                        select m;

        if (qMatching.Any())
        {
            MatchingController matchingController = new MatchingController();
            matchingController.SendEmail();
        }
    }

    // re-add our task so it recurs
    AddMatchingTask(k, Convert.ToInt32(v));
 }

SendEmail()方法应该创建电子邮件的正文,获取视图并将其放入 HTML 字符串中以发送

public void SendEmail()
{
     /* [...] Construct a model myModel */
     
     /* Then create the body of the mail */
     string htmlContent = RenderRazorViewToString("~/Views/Mailing/MatchingMail.cshtml", myModel);  
}
    

在这里,RenderRazorViewToString()(方法的主体在这篇文章的顶部给出)在这一行失败:

var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);

ControllerContext 不能为空

为什么,仅在这种情况下ControllerContextnull?我已经阅读了这篇文章,但如果我理解正确,这是因为我手动实例化了我的控制器写作:

MatchingController matchingController = new MatchingController();

但是,我不知道如何继续...

任何帮助将不胜感激。
谢谢

4

3 回答 3

2

B2K 有一个正确的想法——您需要通过从外部调用应用程序来初始化 Web 请求,因此它将启动一个新的 HttpContext 来生成 HTML。

您可以使用此处此处的建议来创建后台电子邮件。

一种方法是使用初始请求对您的电子邮件进行字符串化并将其保存在数据库中以供以后邮寄。

或者,您可以只安装MvcMailerPostal并使用他们的解决方案。

于 2015-02-08T22:47:05.150 回答
1

根据MSDN

此类型的任何公共静态(在 Visual Basic 中为 Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。

方法失败,因为ControllerContext本地没有枚举,MatchingController matchingController = new MatchingController();没有生效!那么,它的真正价值是什么?考虑到您在单独的方法(有时是线程)中调用它,因此您不能使用这种与上下文相关的方法,例如使用的方法,[ViewEngineCollection.FindPartialView()][2]因为它不能使用它的controllerContext成员(is null)。

解决方案:

您可能想使用构造函数以防万一Application_Start并使用相同的方法。像这样的东西:

var viewResult = ViewEngines.Engines.FindPartialView(new ControllerContext(), viewName);

或使用ViewContext.View代替FindPartialView并重写一些方法:(

于 2015-02-05T15:12:46.297 回答
1

无需尝试模拟网络点击,您实际上可以触发网络点击以允许您使用适当的上下文渲染视图。获取结果并将其存储到您的电子邮件正文中。我不得不为保险报价做类似的事情。这是我的代码,然后是根据您的需要进行的调整。

    public ActionResult StartInsuranceQuote()
    {

        using (var client = new WebClient())
        {
            var values = new NameValueCollection
            {
                { "sid", DataSession.Id.ExtractSid() }
            };
            client.UploadValuesAsync(new Uri(Url.AbsoluteAction("QuoteCallback", "Quote")), values);
        }
        return PartialView();                
    }

这样做的关键是从您的模型中填充值集合。由于您没有提供,我将假设一些属性用于说明:

    public void SendEmail(YourViewModel model)
    {
        using (var client = new WebClient())
        {
            var values = new NameValueCollection
            {
                { "Name",  model.Name },
                { "Product", model.Product },
                { "Color", model.Color },
                { "Comment", model.Comment }
            };
            string body = client.UploadValues(new Uri(Url.AbsoluteAction("GenerateBody", "RenderEmail")), values);

            // send email here
        }
    }

渲染电子邮件控制器:

    public ActionResult GenerateBody()
    {
        return View();
    }

生成主体.cshtml:

@foreach (string key in Request.Form.AllKeys)
{
    Response.Write(key + "=" + Request[key] + "<br />");
}

更新:AbsoluteAction 是一种扩展方法,包括在下面

public static string AbsoluteAction(this UrlHelper url, string actionName, string controllerName, object routeValues = null)
{
    if (url.RequestContext.HttpContext.Request.Url != null)
    {
        string scheme = url.RequestContext.HttpContext.Request.Url.Scheme;
        return url.Action(actionName, controllerName, routeValues, scheme);
    }
    throw new Exception("Absolute Action: Url is null");
}
于 2015-02-05T16:06:06.423 回答