примечание: Первоначально я разместил вопрос, подобный этому здесь , но я решил сделать репост, потому что преодолел исходную проблему и в процессе изменил дизайн. Я думал, что это оправдывает новую тему, потому что, как дизайн изменился, вопрос также существенно изменился. Я просто хочу прояснить, что я не пытаюсь залить ТАК тем же вопросом.
вопрос: Я работал над небольшим экспериментом, чтобы посмотреть, смогу ли я создать вспомогательный метод для сериализации любого из моих типов в любой тип HTML-тега, который я укажу. Я подумал, что передам это сообществу, чтобы помочь мне идентифицировать запахи кода или другие недостатки / неэффективность в дизайне, чтобы улучшить дизайн.
По сути, у меня есть этот код, который сгенерирует поле выбора (или любой другой элемент HTML) с рядом опций:
// the idea is I can use one method to create any complete tag of any type
// and put whatever I want in the content area.
// This makes the generation of all html completely testable
<% using (Html.GenerateTag<SelectTag>(Model, new { href = Url.Action("ActionName") })) { %>
// model is type ShareClass. It contains a list of Funds
<%foreach (var fund in Model.Funds) {%>
<% using (Html.GenerateTag<OptionTag>(fund)) { %>
<%= fund.Name %>
<% } %>
<% } %>
<% } %>
, который выдаст следующий вывод html:
<select shareclassname="MyShareClass"
shareclasstype="ShareClass_A"
href="/Ctrlr/ActionName">
<option selected="selected" id="1" name="MyFund_1">MyFund_1</option>
<option id="2" name="MyFund_2">MyFund_2</option>
<option id="3" name="MyFund_3">MyFund_3</option>
<option id="N" name="MyFund_N">MyFund_N</option>
</select>
Этот помощник Html.GenerateTag определен как:
public static MMTag GenerateTag<T>(this HtmlHelper htmlHelper, object elementData, object attributes) where T : MMTag
{
return (T)Activator.CreateInstance(typeof(T), htmlHelper.ViewContext, elementData, attributes);
}
В зависимости от типа T будет создан один из типов, определенных ниже:
public abstract class HtmlTypeBase : MMTag
{
public HtmlTypeBase(ViewContext viewContext, params object[] elementData)
{
_tag = this.GetTag();
base._viewContext = viewContext;
base.MergeDataToTag(viewContext, elementData);
}
public abstract TagBuilder GetTag();
}
public class SelectTag : HtmlTypeBase
{
public SelectTag(ViewContext viewContext, params object[] elementData)
: base(viewContext, elementData)
{
base._tag = new TagBuilder("select");
}
public override TagBuilder GetTag()
{
return new TagBuilder("select");
}
}
public class OptionTag : HtmlTypeBase
{
public OptionTag(ViewContext viewContext, params object[] elementData)
: base(viewContext, elementData)
{
base._tag = new TagBuilder("option");
}
public override TagBuilder GetTag()
{
return new TagBuilder("option");
}
}
public class AnchorTag : HtmlTypeBase
{
public AnchorTag(ViewContext viewContext, params object[] elementData)
: base(viewContext, elementData)
{
base._tag = new TagBuilder("a");
}
public override TagBuilder GetTag()
{
return new TagBuilder("a");
}
}
и это определение MMTag:
public class MMTag : IDisposable
{
internal bool _disposed;
internal ViewContext _viewContext;
internal TextWriter _writer;
internal TagBuilder _tag;
public MMTag() {}
public MMTag(ViewContext viewContext, params object[] elementData){ }
protected void MergeDataToTag(ViewContext viewContext, object[] elementData)
{
MergeTypeDataToTag(elementData[0]);
MergeAttributeDataToTag(elementData[1]);
this._viewContext = viewContext;
this._viewContext.Writer.Write(_tag.ToString(TagRenderMode.StartTag));
}
private void MergeAttributeDataToTag(object attributeData)
{
var dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
var attributes = attributeData;
if (attributes != null)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(attributes))
{
object value = descriptor.GetValue(attributes);
dic.Add(descriptor.Name, value);
}
}
_tag.MergeAttributes<string, object>(dic);
}
private void MergeTypeDataToTag(object typeData)
{
Type elementDataType = typeData.GetType();
foreach (PropertyInfo prop in elementDataType.GetProperties())
{
if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(Decimal) || prop.PropertyType == typeof(String))
{
object propValue = prop.GetValue(typeData, null);
string stringValue = propValue != null ? propValue.ToString() : String.Empty;
_tag.Attributes.Add(prop.Name, stringValue);
}
}
}
#region IDisposable
public void Dispose()
{
Dispose(true /* disposing */);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (disposing)
{
_writer = _viewContext.Writer;
_writer.Write(_tag.ToString(TagRenderMode.EndTag));
}
}
}
#endregion
}
Я также должен добавить, что Fund & ShareClass определены как:
public class Fund
{
public int ID { get; set; }
public string Name { get; set; }
public Fund()
{
this.ID = 123;
this.Name = "MyFund";
}
public Fund(int id, string name)
{
this.ID = id;
this.Name = name;
}
}
public class ShareClass
{
public string ShareClassName { get; set; }
public string ShareClassType { get; set; }
public IEnumerable<Fund> Funds { get; set; }
public ShareClass(string name, string shareClassType)
{
this.ShareClassName = name;
this.ShareClassType = shareClassType;
}
}