2010-01-20 58 views
0

在过去,我使用UserControls来创建电子邮件模板,我可以使用LoadControl然后使用RenderControl来获取用于我的正文电子邮件。这是在asp.net webforms。使用LoadControl和对象初始值设定器来创建属性

我在建立一个mvc网站的投掷,并想做类似的事情。我实际上已经考虑把这个功能放在一个单独的类库中,并且正在研究如何做到这一点,以便在我的Web层中,我可以调用EmailTemplate.SubscriptionEmail(),然后从我的模板生成html,地方(显然需要有电子邮件地址等参数)。

我想创建一个Render控制方法,我可以将一个字符串传递给UserControl的路径,这是我的模板。我遇到这样的适合我的需要在网络上:

public static string RenderUserControl(string path, 
       string propertyName, object propertyValue) 
     { 
      Page pageHolder = new Page(); 
      UserControl viewControl = 
       (UserControl)pageHolder.LoadControl(path); 

      if (propertyValue != null) 
      { 
       Type viewControlType = viewControl.GetType(); 
       PropertyInfo property = viewControlType.GetProperty(propertyName); 
       if (property != null) 
        property.SetValue(viewControl, propertyValue, null); 
       else 
       { 
        throw new Exception(string.Format(
         "UserControl: {0} does not have a public {1} property.", 
         path, propertyName)); 
       } 
      } 

      pageHolder.Controls.Add(viewControl); 
      StringWriter output = new StringWriter(); 
      HttpContext.Current.Server.Execute(pageHolder, output, false); 
      return output.ToString(); 
     } 

我的问题是,我的用户(一个或多个)可能有多个和不同的性质。因此,SubscribeEmail可能需要FirstName和EmailAddress,而另一个电子邮件模板UserControl(让它称为DummyEmail)将需要FirstName,EmailAddress和DateOfBirth。

上面的方法似乎只为propertyName和propertyValue携带一个参数。我认为,我可以把不同性质到字符串数组但后来我想那会很酷的一个对象intialiser,所以我可以这样调用方法:

RenderUserControl("EmailTemplates/SubscribeEmail.ascs", new object() { Firstname="Lloyd", Email="[email protected]" }) 

这是否有意义?我只是想知道这是否完全可能,以及我如何实施它?我不知道是否可以将'object'上设置的属性映射到加载的用户控件上的属性,以及是否可以从何处开始执行此操作?

有没有人做过这样的事情?谁能帮忙?

Lloyd

回答

2

你的例子可以实现。但它有点违反了MVC模式。如果你正在做这件事,那么你也可以使用webforms中完全相同的解决方案。

当我创建html邮件时,我通常会创建一个正常的mvc视图(对某些控制器和视图执行操作)。然后,我将该视图渲染成一个字符串并发送出去。这样你就可以跟踪mvc模式,并且你可以自动地在浏览器中看到邮件(你可以访问该URL的那个动作,当然这可以以任何你想要的方式来限制)。

要渲染视图为一个字符串我使用这个类:

public class ViewRenderer 
{ 
    protected RouteCollection RouteCollection { get; private set; } 

    public DefaultViewRenderer() 
     : this(RouteTable.Routes) 
    { 

    } 

    public DefaultViewRenderer(RouteCollection routeCollection) 
    { 
     RouteCollection = routeCollection; 
    } 

    public virtual string RenderViewAsString<TController>(Expression<Action<TController>> action) 
     where TController : Controller 
    { 
     var sb = new StringBuilder(); 
     var memWriter = new StringWriter(sb); 

     var fakeContext = CreateFakeContext(memWriter); 

     var oldContext = HttpContext.Current; 
     HttpContext.Current = fakeContext; 

     CreateHtmlHelper(fakeContext).RenderAction(action); 

     HttpContext.Current = oldContext; 

     memWriter.Flush(); 
     memWriter.Dispose(); 
     return sb.ToString(); 
    } 

    protected virtual HttpContext CreateFakeContext(StringWriter memWriter) 
    { 
     var fakeResponse = new HttpResponse(memWriter); 
     var context = new HttpContext(HttpContext.Current.Request, fakeResponse); 

     foreach (var key in HttpContext.Current.Items.Keys) 
      context.Items[key] = HttpContext.Current.Items[key]; 

     foreach (string key in HttpContext.Current.Session.Keys) 
      context.Session[key] = HttpContext.Current.Session[key]; 

     return context; 
    } 

    protected virtual HtmlHelper CreateHtmlHelper(HttpContext fakeContext) 
    { 
     var fakeControllerContext = CreateControllerContext(fakeContext, RouteCollection); 
     return new HtmlHelper(new ViewContext(fakeControllerContext, 
                new FakeView(), new ViewDataDictionary(), new TempDataDictionary()), new FakePage()); 
    } 

    protected virtual ControllerContext CreateControllerContext(HttpContext fakeContext, RouteCollection routeCollection) 
    { 
     return new ControllerContext(
      new HttpContextWrapper(fakeContext), 
      routeCollection.GetRouteData(new HttpContextWrapper(HttpContext.Current)), new FakeController()); 
    } 

    protected class FakeView : IView 
    { 
     public void Render(ViewContext viewContext, TextWriter writer) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    protected class FakeController : Controller 
    { 

    } 

    protected class FakePage : IViewDataContainer 
    { 
     public ViewDataDictionary ViewData 
     { 
      get { return new ViewDataDictionary(); } 
      set { } 
     } 
    } 
} 

这将使用写入视图到一个StringBuilder的结果的假响应。然后你使用这样的:

var viewRenderer = new ViewRenderer(); 
var body = viewRenderer.RenderViewAsString<SomeController>(x => x.ActionThatRendersHtmlMail(parameters)); 

然后,你只需发送邮件与正文。你当然可以把它包装在你自己的类中,这样你可以调用EmailTemplate.SubscriptionEmail(); (从你的例子)。