использование LoadControl с инициализатором объекта для создания свойств - PullRequest
0 голосов
/ 20 января 2010

В прошлом я использовал UserControls для создания шаблонов электронной почты, которые я мог бы заполнить свойствами, а затем с помощью LoadControl, а затем RenderControl, чтобы получить HTML-код, который будет использоваться для основного текста моего электронного письма. Это было в рамках asp.net веб-форм.

Я в процессе создания сайта MVC и хотел сделать что-то подобное. Я на самом деле подумал о том, чтобы поместить эту функциональность в отдельную библиотеку классов, и сейчас я изучаю, как я могу это сделать, чтобы в моем веб-слое я мог просто вызвать 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. Я рассмотрел массив строк, в которые я мог бы добавить различные свойства, но потом подумал, что было бы здорово иметь инициализатор объекта, чтобы я мог вызвать метод следующим образом:

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

Это имеет смысл? Мне просто интересно, возможно ли это вообще вообще и как я это реализовал? Я не уверен, что можно было бы сопоставить свойства, установленные в «объекте», со свойствами в загруженном пользовательском элементе управления, и если это возможно, с чего начать?

Кто-нибудь делал что-то подобное раньше? Кто-нибудь может помочь?

Lloyd

1 Ответ

2 голосов
/ 20 января 2010

Ваш пример может быть реализован. Но это немного нарушает схему MVC. И если вы делаете это в любом случае, вы можете использовать то же решение, которое вы использовали в веб-формах.

Когда я создаю html-письма, я обычно создаю обычное представление mvc (с действием на каком-то контроллере и представлением). Затем я преобразовываю это представление в строку и отправляю ее. Таким образом, вы следуете шаблону mvc и получаете возможность автоматически видеть почту в браузере (вы можете просто перейти по ссылке на это действие. Конечно, это может быть ограничено любым удобным вам способом).

Чтобы отобразить строку, я использую этот класс:

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 (); (из вашего примера).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...