Pregunta

En el pasado, he usado UserControls para crear plantillas de correo electrónico en las que puedo llenar las propiedades y luego usar LoadControl y luego RenderControl para obtener el HTML para el cual usar el texto del cuerpo de mi correo electrónico. Esto fue dentro de ASP.NET WebForms.

Estoy a tiro de construir un sitio web de MVC y quería hacer algo similar. En realidad, he considerado poner esta funcionalidad en una biblioteca de clases separadas y estoy investigando cómo puedo hacer esto para que en mi capa web pueda llamar a CagerTemplate.subscriptionEmail () que luego generará el HTML de mi plantilla con propiedades en relevantes Lugares (obviamente, debe haber parámetros para la dirección de correo electrónico, etc. allí).

Quería crear un solo método de control de renderizado para el cual pueda pasar una cadena a la ruta del UserControl, que es mi plantilla. Me he encontrado con esto en la web que se adapta a mis necesidades:

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

Mi problema es que mis usercontrol (s) pueden tener propiedades múltiples y diferentes. Por lo tanto, suscríbeteil puede requerir el nombre de primer nombre y el correo electrónico, donde otra plantilla de correo electrónico userControl (llamándolo dummymail) requeriría el nombre de primer nombre, el correo electrónico y la fecha de la cierta.

El método anterior solo parece llevar un parámetro para PropertyName y PropertyValue. Consideré una variedad de cuerdas en las que podría poner las diferentes propiedades, pero luego pensé que sería genial tener un intializador de objetos para poder llamar al método como este:

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

¿Tiene sentido? Me preguntaba si esto es posible en primer lugar y cómo lo implementaría. No estoy seguro de si sería posible mapear las propiedades establecidas en 'objeto' a las propiedades en el control del usuario cargado y si es posible dónde comenzar a hacer esto.

¿Alguien ha hecho algo como esto antes? ¿Alguien puede ayudar?

Lloyd

¿Fue útil?

Solución

Su ejemplo podría implementarse. Pero viola bastante el patrón MVC. Y si está haciendo eso de todos modos, podría ir con la misma solución exacta que tenía en las formas web.

Cuando creo correos HTML, generalmente creo una vista MVC normal (con una acción en algún controlador y una vista). Luego presiono esa vista en una cadena y la envío. De esta manera, sigue el patrón MVC y tiene la capacidad de ver el correo en el navegador automáticamente (puede visitar la URL a esa acción. Esto, por supuesto, puede restringirse de la manera que desee).

Para representar una vista a una cadena, uso esta clase:

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

Esto utiliza una respuesta falsa que escribe el resultado de la vista en un StringBuilder. Entonces usas esto así:

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

Entonces solo envía el correo con ese texto corporal. Por supuesto, puede envolver esto en su propia clase para que pueda llamar a C. (De tu ejemplo).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top