Domanda

In passato ho usato controlli utente per creare modelli di posta elettronica che posso riempire proprietà su e poi usare LoadControl e poi RenderControl per ottenere il codice HTML per i quali da utilizzare per il testo del corpo della mia e-mail. Questo è stato all'interno di ASP.NET WebForms.

Sono nei lanci di costruire un sito web MVC e volevo fare qualcosa di simile. In realtà ho pensato di mettere questa funzionalità in una libreria di classi separate e sono alla ricerca di come posso fare questo in modo che nel mio strato web posso solo chiamare EmailTemplate.SubscriptionEmail () che sarà quindi generare il codice HTML dal mio modello con proprietà a rilevanti posti (ovviamente ci deve essere parametri per l'indirizzo di posta elettronica ecc in là).

Ho voluto creare un unico metodo Render controllo per cui posso passare una stringa al percorso di UserControl che è il mio modello. Ho incontrato questo sul web questo tipo di adatta alle mie esigenze:

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

Il mio problema è che il mio UserControl (s) può avere molteplici e diverse proprietà. Così SubscribeEmail può richiedere FirstName e EmailAddress dove un altro modello e-mail UserControl (consente di chiamare DummyEmail) richiederebbe FirstName, EmailAddress e DateOfBirth.

Il metodo di cui sopra appare solo per portare un parametro per propertyName e propertyValue. Ho considerato un array di stringhe che ho potuto mettere le proprietà variano in ma poi ho pensato che sarebbe stato bello avere un intialiser oggetto così ho potuto chiamare il metodo come questo:

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

Ha senso? Mi stavo chiedendo se questo è a tutto il possibile, in primo luogo e come mi piacerebbe implementarlo? Non sono sicuro se sarebbe possibile mappare le proprietà impostate su 'oggetto' per le proprietà sul controllo utente caricato e se è possibile, da dove cominciare a fare questo?

qualcuno ha fatto qualcosa di simile prima? Chiunque può aiutare?

Lloyd

È stato utile?

Soluzione

Il vostro esempio potrebbe essere implementato. Ma esso viola il pattern MVC un bel po '. E se si sta facendo che in ogni caso si potrebbe altrettanto bene andare con la stessa soluzione esatta si aveva in WebForms.

Quando creo mail HTML Io di solito creare una vista MVC normale (con un'azione su alcuni controller e una vista). Poi mi rendo quella vista in una stringa e mando via. In questo modo si sta seguendo il pattern MVC e si ottiene la possibilità di vedere la posta nel browser automaticamente (si può solo visitare l'url a quell'azione. Questo può naturalmente essere limitato in alcun modo che si desidera).

Per effettuare il rendering in vista di una stringa che uso questa classe:

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

Questo utilizza una risposta falsa che scrive il risultato della vista in uno StringBuilder. Poi si utilizza questo in questo modo:

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

Poi basta inviare la posta con quel corpo del testo. Ovviamente si può avvolgere questo in te stessa classe in modo da poter chiamare EmailTemplate.SubscriptionEmail (); (Dal vostro esempio).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top