好的。。所以我有一个接口,保存时我会把它传给控制器,看起来有点像这样(删除大部分只是为了让大家明白这个想法(。如果需要的话,我可以添加更多的细节,但我不想让所有人都不知所措。
public interface IProjectElement
{
string Name { get; set; }
int ElementTypeID { get; set; }
...
}
public class BaseProjectElement : IProjectElement
{
public int ElementTypeID { get; set; }
public string AdditionalInformation { get; set; }
...
}
然后我有几个接口的实现,但这里有一个例子。。。
public class SQLElement : BaseProjectElement
{
public int? DatabasePlatformID { get; set; }
}
从Global.asax中启动
ModelBinders.Binders.Add(typeof(IProjectElement), new ProjectElementModelBinder());
所以问题来了。下面的代码确定对象的类型并创建相应的对象。我认为发生的事情是,我可以看到所有的值都在bindingContent中发送。ValueProvider数据,但有趣的是,属性集合只列出接口的属性。基地。CreateModel将创建我需要的类型,但只使用接口中的数据填充对象。我可以使用一些丑陋的代码来设置其他属性,我正在设置DatabasePlatformID,但我的一些类有几个其他属性。我可能遗漏了一些显而易见的东西。这样设置附加属性不是很容易维护的。
public class ProjectElementModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
string modelName = bindingContext.ModelName;
var elementType = (int)bindingContext.ValueProvider.GetValue(string.Format("{0}.ElementType", modelName)).ConvertTo(typeof(int));
Type instantiationType = null;
if (elementType == 1)
{
instantiationType = typeof(IISElement);
}
else if (elementType == 2)
{
instantiationType = typeof(SharePointElement);
}
else if (elementType == 3)
{
instantiationType = typeof(SQLElement);
}
else if (elementType == 12)
{
instantiationType = typeof(SharedElement);
}
else
{
instantiationType = typeof(BaseProjectElement);
}
var obj = base.CreateModel(controllerContext, bindingContext, instantiationType);
if (elementType == 3)
{
PropertyInfo pi = obj.GetType().GetProperty("DatabasePlatformID");
int databasePlatformID = (int)bindingContext.ValueProvider.GetValue(string.Format("{0}.DatabasePlatformID", modelName)).ConvertTo(typeof(int));
pi.SetValue(obj, databasePlatformID, null);
}
return obj;
}
}
请注意,我也尝试过使用我在其他地方看到的代码来实现这一点,但我看到的更糟糕的是,对象根本没有被填充。
var obj=Activator.CreateInstance(instantiationType);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
bindingContext.ModelMetadata.Model = obj;
return obj;
这是我的控制器
public ActionResult SaveElements(Guid projectID, List<IProjectElement> selectedElements, List<ProjectElementRelationshipsDAO> selectedRelationships)
{
using (ManagedHostingHelper helper = new ManagedHostingHelper())
{
BasePageViewModel viewModel = helper.SaveElements(projectID, selectedElements, selectedRelationships);
return Json(new
{
ViewModel = viewModel,
JsonRequestBehavior.AllowGet
});
}
}
最后,这里是调用控制器的javascript。使用敲除.js.
self.SaveElements = function () {
var saveURL = new UrlHelper().SaveElements();
data: ko.toJSON({ projectID: self.ProjectID(), selectedElements: self.SelectedElements(), selectedRelationships: self.SelectedRelationships() }),
type: "post", contentType: "application/json",
success: function (result) {
if (result.ReturnStatus == true) {
self.GoForward();
}
else { alert('error'); }
}
});
};
SelectedElements是一个knockout.js observableArray。以下是可以传递的javascript对象。。请注意,此数组将包含一个实现IProjectElement的元素集合。。我也没有显示所有不同的类型,但它们都有一组基本的数据,并添加了额外的属性。
// base class - see class diagram in folder as types should match this fairly closely
function BaseProjectElement(data) {
var self = this;
self.ProjectElementID = ko.observable(data.ProjectElementID);
self.ElementName = ko.observable(data.ElementName);
... more properties
self.BaseElementValidationGroup = ko.validatedObservable({
Name: self.Name
});
self.IsBaseElementValid = ko.computed(function () {
return self.BaseElementValidationGroup().errors().length == 0;
});
};
function SQLElement(data) {
var self = this;
ko.utils.extend(self, new BaseProjectElement(data));
self.DatabasePlatformID = ko.observable(data.DatabasePlatformID).extend({ required: { params: true, message: ' ' } });
self.SQLElementValidationGroup = ko.validatedObservable({
DatabasePlatformID: self.DatabasePlatformID
});
self.IsSQLElementValid = ko.computed(function () {
return (self.SQLElementValidationGroup().errors().length == 0) && self.IsBaseElementValid();
});
};
好的。。我以为我曾经做过类似的事情,但以下是对我有效的方法。ModelMetaData的设置与我在几个地方看到的行(如""(
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
与
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
string modelName = bindingContext.ModelName;
var elementType = (int)bindingContext.ValueProvider.GetValue(string.Format("{0}.ElementType", modelName)).ConvertTo(typeof(int));
if (elementType == 1)
{
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new IISElement(), typeof(IISElement));
}
else if (elementType == 2)
{
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new SharePointElement(), typeof(SharePointElement));
}
else if (elementType == 3)
{
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new SQLElement(), typeof(SQLElement));
}
else if (elementType == 12)
{
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new SharedElement(), typeof(SharedElement));
}
else
{
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new BaseProjectElement(), typeof(BaseProjectElement));
}
return base.BindModel(controllerContext, bindingContext);
}