0

过去,我使用 UserControls 创建电子邮件模板,我可以在其上填充属性,然后使用 LoadControl 和 RenderControl 来获取用于我的电子邮件正文的 html。这是在 asp.net 网络表单中。

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

我想创建一个单一的渲染控制方法,我可以将一个字符串传递给作为我的模板的 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(s) 可能有多个不同的属性。因此,SubscribeEmail 可能需要 FirstName 和 EmailAddress,而另一个电子邮件模板 UserControl(我们称之为 DummyEmail)将需要 FirstName、EmailAddress 和 DateOfBirth。

上面的方法似乎只携带了一个propertyName和propertyValue的参数。我考虑了一个字符串数组,可以将不同的属性放入其中,但后来我认为拥有一个对象初始化器会很酷,因此我可以像这样调用该方法:

RenderUserControl("EmailTemplates/SubscribeEmail.ascs", new object() { Firstname="Lloyd", Email="myemail@mydomain.com" })

那有意义吗?我只是想知道这是否可能首先以及我将如何实现它?我不确定是否可以将“对象”上设置的属性映射到加载的用户控件上的属性,以及是否可以从哪里开始执行此操作?

有没有人做过这样的事情?任何人都可以帮忙吗?

劳埃德

4

1 回答 1

2

你的例子可以实现。但它违反了 MVC 模式。而且,如果您仍然这样做,那么您也可以使用与 Web 表单中完全相同的解决方案。

当我创建 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 { }
        }
    }
}

这使用了将视图结果写入字符串构建器的虚假响应。然后你像这样使用它:

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

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

于 2010-01-20T12:05:51.540 回答