2014-12-04 2 views
1

Я использую ASP.NET MVC3
У меня есть .cshtml мнение, и я хочу stringify его для включения в теле сообщения электронной почты.
Вот метод я использую:Рендер вид бритвой строки => ControllerContext равно нулю, когда вызывается из возвратного задачи

//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 минут, и если да, отправляет электронное письмо. Конечно, этот электронный адрес ... вид.

Вот часть моего кода: Этот метод очень вдохновлен this post

/* 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)); 
} 

The 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 не может быть пустым

Почему в этом случае только, ControllerContext является null? Я прочитал this post, но если я правильно понял, это потому, что я вручную экземпляр моего контроллера письма:

MatchingController matchingController = new MatchingController(); 

Однако, я не знаю, как поступить иначе ...

Любая помощь будет очень оценен.
Спасибо

ответ

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();     
    } 

ключ к этот woul d - заполнение коллекции значений из вашей модели. Поскольку вы не предоставили, что я предполагаю, что некоторые свойства для иллюстрации:

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 
     } 
    } 

RenderEmailController:

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

GenerateBody.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"); 
} 
+0

Благодарим вас за ответ. Однако Url.AbsoluteAction, похоже, не существует. Я следую вашей идее и делаю некоторые исследования о WebClient() и нашел эту ссылку: http://stackoverflow.com/questions/7779824/using-c-sharp-webrequest-to-interact-with-an-asp-net-mvc -3-сайт, который мне очень помог. Так что ваша идея была хорошей, спасибо большое! – AlexB

1

MSDN виду наиболее:

Любые открытые (Shared в Visual Basic) члены этого типа являются поточно. Любые члены экземпляра не гарантируют, что это будет поток .

метод терпит неудачу, потому что ControllerContext не перечислены локально и MatchingController matchingController = new MatchingController(); не действует! Итак, какова реальная ценность для этого?Учитывая, что вы вызываете это в отдельных методах (а иногда и в потоках), впоследствии вы не можете использовать такие контекстно-зависимые методы, как тот, который используется, скажем, [ViewEngineCollection.FindPartialView()][2], поскольку он не может использовать его член controllerContext (это null).

Решение:

Вы можете хотите use a constructor в случае Application_Start и использовать тот же метод. что-то вроде этого:

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

или с использованием ViewContext.View вместо FindPartialView и переписать некоторые методы :(

+0

Спасибо за ваш ответ. Тем не менее, строка с 'new ControllerContext()' вы предлагаете с ошибкой со следующей ошибкой: «RouteData должен содержать элемент с именем« контроллер »с непустой строкой» (я передаю его по-английски, но это именно так). Кроме того, я не уверен, чтобы понять ваше последнее предложение, не могли бы вы объяснить его более подробно? Я действительно не понимаю, что переписать ...? Извините, я новичок в этой проблеме «контекста» в MVC – AlexB

+0

@AlexB http://stackoverflow.com/questions/1191958/get-virtualpath-of-a-view-using-viewcontext является одним из его удобства использования для «ViewContext» .View', который вы можете изменить свой код на основе этого помощника. –

+0

Кажется, что ViewContext не имеет статического свойства, называемого View, поэтому я вызвал переменную ViewContext и вызвал свойство View. Тем не менее, он кажется нулевым, и метод не удается из-за него :-( Я действительно не понимаю, как переписать мой метод RenderRazorViewToString, вы подумаете, что я очень новый об этом (это правда), но у вас есть фрагмент рабочего кода для совместного использования? – AlexB

2

B2K имеет правильную идею - вам нужно инициализировать веб-запрос, вызвав приложение из за пределами, поэтому он создаст новый HttpContext для генерации HTML.

Вы можете использовать совет here или here, чтобы создать фоновый почтовый ящик.

Один из подходов состоит в том, чтобы использовать первоначальный запрос, чтобы укрепить вашу электронную почту и сохранить ее в базе данных для последующей рассылки.

В качестве альтернативы, вы можете просто установить MvcMailer или Postal и использовать их решение.

 Смежные вопросы

  • Нет связанных вопросов^_^