我有一个类FormBuilder<TObject>
,其方法为Property<TProperty>(Expression<Func<TObject, TProperty>> expression)
。在构造函数中,我想在获取TObject
的所有属性后调用该方法。我怎么能这样做呢?我现在的代码,从其他SO答案改编,只是抛出错误,我不知道如何解决它们。这就是我到目前为止所做的:
public abstract class FormBuilder<TObject> :
FormBuilder {
public FormBuilder() {
// REPLICATE: Property(p => p.NAME);
var method = this.GetType().GetMethod("Property");
var properties = typeof(TObject).GetProperties();
var objectType = typeof(TObject);
foreach (var p in properties) {
var propertyType = p.GetType();
var parameter = Expression.Parameter(propertyType, "p");
var property = Expression.Property(parameter, p.Name);
var @delegate = typeof(Func<,>).MakeGenericType(objectType, propertyType);
// throws: ParameterExpression of type 'System.Reflection.RuntimePropertyInfo'
// cannot be used for delegate parameter of type 'UserQuery+Command'
var expression = Expression.Lambda(@delegate, property, new[] { parameter });
// method.MakeGenericMethod(parameter).Invoke(this, new[] { property });
}
}
public FormPropertyBuilder<TProperty> Property<TProperty>(
Expression<Func<TObject, TProperty>> expression) {
var member = (expression.Body as MemberExpression).Member;
var builder = new FormPropertyBuilder<TProperty>(member.Name);
Properties.Add(builder);
return builder;
}
}
修改
public sealed class CommandFormBuilder :
FormBuilder<Command> {
public CommandFormBuilder() {
Property(
p => p.SourceOfLoss).HasLabel("Source of Loss");
}
}
这就是我通常会调用该方法的方法,但我需要在构造每个属性时立即调用它。
我的最终目标是通过该方法简单地播种所有属性,并在必要时稍后对其进行进一步配置。我感谢任何帮助!
答案 0 :(得分:1)
我回来了!
在@Nkosi的最后评论之后,我回到了绘图板上,从头开始。我太迷上了仿制药,一旦我退缩了一点,我就想出了如何得到自己想要的。不能说这是最正确的方法,但是由于我已经在整个应用程序中应用了它,因此似乎可以正常工作。我最终为特定任务(例如表单,表格,PDF文档)制作了多个构建器(模板构建器?)。这是我想出的。我将仅展示表单构建器,因为除适用于其方案的专用方法外,其他构建器几乎都会重复使用它。自发布问题以来,这是经过多次迭代的当前版本。哦,这个构建的项目是在VS 2013中的C#5上进行的,因此,一旦升级到较新的版本,就可以进行改进。
操作
public interface IFormActionBuilder {
IFormActionBuilder HasFormId(
string formId);
IFormActionBuilder HasLabel(
string label);
IFormActionBuilder IsDefault();
IFormActionBuilder IsFileUpload();
}
public interface IFormActionMetadata {
string Controller { get; }
bool Default { get; }
string FormId { get; }
string Enctype { get; }
string Label { get; }
string Name { get; }
}
组
public interface IFormGroupBuilder {
IFormGroupBuilder HasOrder(
short order);
}
public interface IFormGroupMetadata {
string Label { get; }
short Order { get; }
}
属性
public interface IFormPropertyBuilder {
IFormPropertyBuilder HasFormat(
string format);
IFormPropertyBuilder HasLabel(
string label);
IFormPropertyBuilder HasOrder(
short order);
IFormPropertyBuilder HasType(
FormFieldType type);
IFormPropertyBuilder InGroup(
string group);
IFormPropertyBuilder IsHidden();
IFormPropertyBuilder IsReadOnly();
IFormPropertyBuilder IsRequired();
}
public interface IFormPropertyMetadata {
string Format { get; }
string Group { get; }
bool IsIgnored { get; set; }
string Label { get; }
string Name { get; }
short Order { get; }
bool Required { get; }
FormFieldType Type { get; }
}
从技术上讲,可以将动作,组和属性接口合并为一个,但是我选择将它们拆分为方法和属性,以在配置过程中和提取元数据时将它们保持整洁。
构建器
public interface IFormMetadata {
IList<IFormActionMetadata> ActionMetadatas { get; }
IList<IFormGroupMetadata> GroupMetadatas { get; }
IList<IFormPropertyMetadata> PropertyMetadatas { get; }
}
public abstract class FormBuilder<TObject> :
IFormMetadata {
public IList<IFormActionMetadata> ActionMetadatas { get; private set; }
public IList<IFormGroupMetadata> GroupMetadatas { get; private set; }
public IList<IFormPropertyMetadata> PropertyMetadatas { get; private set; }
protected FormBuilder() {
ActionMetadatas = new List<IFormActionMetadata>();
GroupMetadatas = new List<IFormGroupMetadata> {
new GroupBuilder()
};
PropertyMetadatas = typeof(TObject).GetProperties().Select(
p => new PropertyBuilder(p.Name)).Cast<IFormPropertyMetadata>().ToList();
}
public IFormActionBuilder Action<TController>(
Expression<Action<TController>> action)
where TController : IController {
var method = ((MethodCallExpression)action.Body).Method;
var controller = typeof(TController).Name.Replace("Controller", null);
var builder = new ActionBuilder(method.Name, controller);
ActionMetadatas.Add(builder);
return builder;
}
public IFormGroupBuilder Group(
string label) {
var builder = new GroupBuilder(label);
GroupMetadatas.Add(builder);
return builder;
}
public void Ignore<TProperty>(
Expression<Func<TObject, TProperty>> expression) {
var member = ((MemberExpression)expression.Body).Member;
var propertyMetadata = PropertyMetadatas.SingleOrDefault(
b => b.Name == member.Name);
if (propertyMetadata == null) {
return;
}
propertyMetadata.IsIgnored = true;
}
protected static T Placeholder<T>() {
return default(T);
}
public IFormPropertyBuilder Property<TProperty>(
Expression<Func<TObject, TProperty>> expression) {
var member = ((MemberExpression)expression.Body).Member;
return PropertyMetadatas.Single(
b => b.Name == member.Name) as IFormPropertyBuilder;
}
public sealed class ActionBuilder :
IFormActionBuilder,
IFormActionMetadata {
public string Controller { get; private set; }
public bool Default { get; private set; }
public string Enctype { get; private set; }
public string FormId { get; private set; }
public string Label { get; private set; }
public string Name { get; private set; }
public ActionBuilder(
string action,
string controller) {
Controller = controller;
Enctype = "application/x-www-form-urlencoded";
FormId = "panel-form";
Label = "Save";
Name = action;
}
public IFormActionBuilder HasFormId(
string formId) {
FormId = formId;
return this;
}
public IFormActionBuilder HasLabel(
string label) {
Label = label;
return this;
}
public IFormActionBuilder IsDefault() {
Default = true;
return this;
}
public IFormActionBuilder IsFileUpload() {
Enctype = "multipart/form-data";
return this;
}
}
public sealed class GroupBuilder :
IFormGroupBuilder,
IFormGroupMetadata {
public string Label { get; private set; }
public short Order { get; private set; }
public GroupBuilder() {
}
public GroupBuilder(
string label) {
Label = label;
}
public IFormGroupBuilder HasOrder(
short order) {
Order = order;
return this;
}
}
public sealed class PropertyBuilder :
IFormPropertyBuilder,
IFormPropertyMetadata {
private static readonly Type StringType;
static PropertyBuilder() {
StringType = typeof(string);
}
public string Format { get; private set; }
public string Group { get; private set; }
public bool IsIgnored { get; set; }
public string Label { get; private set; }
public string Name { get; private set; }
public short Order { get; private set; }
public bool Required { get; private set; }
public FormFieldType Type { get; private set; }
public PropertyBuilder(
string name) {
Name = name;
Order = short.MaxValue;
Required = !IsNullable(name);
Type = FormFieldType.Text;
}
public IFormPropertyBuilder HasFormat(
string format) {
Format = format;
return this;
}
public IFormPropertyBuilder HasLabel(
string label) {
Label = label;
return this;
}
public IFormPropertyBuilder HasOrder(
short order) {
Order = order;
return this;
}
public IFormPropertyBuilder HasType(
FormFieldType type) {
Type = type;
return this;
}
public IFormPropertyBuilder InGroup(
string group) {
Group = group;
return this;
}
public IFormPropertyBuilder IsHidden() {
Type = FormFieldType.Hidden;
return this;
}
private static bool IsNullable(
string name) {
var type = typeof(TObject).GetProperty(name).PropertyType;
if (type == StringType) {
return true;
}
return type.IsValueType
&& Nullable.GetUnderlyingType(type) != null;
}
public IFormPropertyBuilder IsReadOnly() {
Type = FormFieldType.None;
return this;
}
public IFormPropertyBuilder IsRequired() {
Required = true;
return this;
}
}
}
查看
public sealed class FormView {
public IList<Action> Actions { get; private set; }
public IList<FormGroup> Groups { get; private set; }
public FormView(
object obj) {
if (obj == null) {
throw new ArgumentNullException("obj");
}
var type = obj.GetType();
var builder = FormLoader.Get(type);
if (builder == null) {
var message = string.Format("Unable to find form builder for type: {0}", type.FullName);
throw new InvalidOperationException(message);
}
Actions = GetActions(builder);
Groups = GetGroups(obj, builder);
}
private static IList<Action> GetActions(
IFormMetadata builder) {
return builder.ActionMetadatas.Select(
a => new Action {
Controller = a.Controller,
Enctype = a.Enctype,
FormId = a.FormId,
IsDefault = a.Default,
Label = a.Label,
Name = a.Name
}).ToList();
}
private static object GetFieldValue(
object obj,
PropertyInfo property,
IFormPropertyMetadata propertyMetadata) {
var value = property.GetValue(obj, null);
if (string.IsNullOrEmpty(propertyMetadata.Format)) {
return value;
}
return string.Format(propertyMetadata.Format, value);
}
private static IList<FormField> GetFields(
object obj,
string group,
IList<PropertyInfo> properties,
IList<IFormPropertyMetadata> propertyMetadatas) {
return properties.Select(
p => {
var propertyMetadata = propertyMetadatas.SingleOrDefault(
pm =>
!pm.IsIgnored
&& pm.Name == p.Name);
if (propertyMetadata == null) {
return null;
}
return new {
propertyMetadata.Group,
propertyMetadata.Label,
propertyMetadata.Name,
propertyMetadata.Order,
propertyMetadata.Required,
propertyMetadata.Type,
Value = GetFieldValue(obj, p, propertyMetadata)
};
}).Where(
a =>
a != null
&& a.Group == group).OrderBy(
a => a.Order).Select(
a => new FormField {
IsRequired = a.Required,
Label = a.Label,
Name = a.Name,
Type = a.Type,
Value = a.Value
}).ToList();
}
private static IList<FormGroup> GetGroups(
object obj,
IFormMetadata builder) {
var properties = obj.GetType().GetProperties();
return builder.GroupMetadatas.Select(
g => new {
g.Label,
g.Order,
Fields = GetFields(obj, g.Label, properties, builder.PropertyMetadatas)
}).OrderBy(
a => a.Order).Select(
a => new FormGroup {
Label = a.Label,
Fields = a.Fields
}).ToList();
}
public sealed class Action {
public string Controller { get; set; }
public string Enctype { get; set; }
public string FormId { get; set; }
public bool IsDefault { get; set; }
public string Label { get; set; }
public string Name { get; set; }
}
}
加载程序
public static class FormLoader {
private static IDictionary<Type, Builder> _builders;
public static IFormMetadata Get<TObject>() {
return Get(typeof(TObject));
}
public static IFormMetadata Get(
Type type) {
var builder = _builders[type];
if (builder.Instance == null) {
builder.Instance = Activator.CreateInstance(builder.Type) as IFormMetadata;
}
return builder.Instance;
}
public static void Initialize() {
Initialize(Assembly.GetExecutingAssembly());
}
public static void Initialize(
params Assembly[] assemblies) {
var type = typeof(FormBuilder<>);
_builders = assemblies.SelectMany(
a => a.GetTypes()).Where(
t =>
!t.IsAbstract
&& !t.IsInterface
&& t.BaseType != null
&& t.BaseType.IsGenericType
&& t.BaseType.GetGenericTypeDefinition() == type).Select(
t => new {
ObjectType = t.BaseType.GetGenericArguments()[0],
BuilderType = t
}).ToDictionary(
k => k.ObjectType,
v => new Builder {
Type = v.BuilderType
});
}
private sealed class Builder {
public IFormMetadata Instance { get; set; }
public Type Type { get; set; }
}
}
我不太满意的装载机。它可以工作,但是每种构建器类型都有一个加载器,因此我只重复很多次。前几天,我花了几个小时试图将它们全部合并为一个,但并没有恢复原状。两者之间的主要区别是它们更改了接口。如果有人对改进加载程序和/或任何其他代码有建议,我将很高兴听到他们的声音。
对于两个构建器,我确实有一个特殊情况,它们需要继承基础对象的构建器,因此它们有一个IncludeBase<TObjectBase>
方法。我对此不是很自信,但是它可以工作。
public void IncludeBase<TObjectBase>() {
var baseBuilder = DocumentLoader.Get<TObjectBase>();
if (baseBuilder == null) {
return;
}
foreach (var basePropertyMetadata in baseBuilder.PropertyMetadatas) {
var propertyMetadata = PropertyMetadatas.SingleOrDefault(
pm => pm.Name == basePropertyMetadata.Name);
if (propertyMetadata == null) {
continue;
}
propertyMetadata = basePropertyMetadata;
}
}
就是这样。感谢您的阅读,如果您对本书有所了解,我也要感谢@Nkosi,使我摆脱了自己陷入的陷阱。