过去我使用UserControls来创建电子邮件模板,我可以填写属性,然后使用LoadControl,然后使用RenderControl来获取用于我的电子邮件正文的html。这是在asp.net webforms中。
我正在建立一个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();
}
我的问题是我的UserControl可能有多个不同的属性。因此,SubscribeEmail可能需要FirstName和EmailAddress,其中另一个电子邮件模板UserControl(我们称之为DummyEmail)需要FirstName,EmailAddress和DateOfBirth。
上述方法似乎只带有propertyName和propertyValue的一个参数。我考虑了一系列字符串,我可以将不同的属性放入其中,但后来我觉得拥有一个对象intialiser很酷,所以我可以调用这样的方法:
RenderUserControl("EmailTemplates/SubscribeEmail.ascs", new object() { Firstname="Lloyd", Email="myemail@mydomain.com" })
这有意义吗?我只是想知道这首先是否可能,以及我如何实现它?我不确定是否可以将'object'上设置的属性映射到已加载的用户控件上的属性,以及是否可以从这里开始执行此操作?
以前有人做过这样的事吗?有人可以帮忙吗?
劳埃德
答案 0 :(得分: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(); (来自你的例子)。