为什么这个 属性 没有在我的 mvc 项目中序列化?
Why isn't this property being serialized in my mvc project?
最终目标是从我的 View
中序列化一个 abstract
类型,供我的 Controller
使用。
我的抽象类型有一个 enum
属性 ,其名称对应于具体派生类型的名称;这就是我将如何确定 select 的具体类型。这个enum
的值是通过反射在抽象类型的构造函数中设置的:
[JsonConverter(typeof(BlockJsonConverter)]
public abstract class Block{
[NotMapped, JsonProperty]
public BlockType BlockType {get; set;}
public string Name {get;set:}
public int Height{get;set;}
public int Width {get;set;}
public int Depth {get;set;}
protected Block(){
BlockType = Enum.TryParse(GetType().Name, out BlockType blocktype)
?? blocktype : BlockType.Unknown
}
}
public enum BlockType {
Long, Short, Tall, Unknown
}
public class Long : Block { /*...*/ }
public class Short : Block { /*...*/ }
public class Tall : Block { /*...*/ }
public class Unknown : Block { /*...*/ }
Block
class 被 Entity Framework 使用,但 BlockType
属性 未存储在数据库中,因此 BlockType
属性 标记为 [NotMapped]
属性;但是,因为我希望 属性 从视图到控制器往返,所以我用 [JsonProperty]
属性标记了它。
我创建了一个 TestModelBinder
来处理从视图到控制器的反序列化:
public class TestModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
return base.CreateModel(controllerContext, bindingContext,
GetModelType(controllerContext, bindingContext, modelType));
}
protected override ICustomTypeDescriptor GetTypeDescriptor(
ControllerContext controllerContext,ModelBindingContext bindingContext)
{
var modelType = GetModelType(controllerContext, bindingContext, bindingContext.ModelType);
return new AssociatedMetadataTypeTypeDescriptionProvider(modelType)
.GetTypeDescriptor(modelType);
}
private static Type GetModelType(ControllerContext controllerContext, ModelBindingContext bindingContext,
Type modelType)
{
if (modelType.Name == "Block")
{
• breakpoint
// get the value from bindingContext for BlockType
// and return the concrete type based on that
}
return modelType;
}
}
当我遇到上面的那个断点时,bindingContext
在它的 ValueProvider.FormValueProvider
中没有 BlockType
对于我的 BlockType
属性 - 但 Name
、Height
、Width
和 Depth
属性按预期列出。
它们在 EditorTemplate 中都以相同的方式列出:
@model Block
<div class="form-row">
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Name)
</div>
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.BlockType)
</div>
</div>
<div class="form-row">
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Height)
</div>
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Width)
</div>
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Depth)
</div>
</div>
... 而 BootstrapEditorGroupFor
助手只生成通常的标签、基于类型(枚举、字符串等)的编辑器和验证消息。枚举的编辑器模板如下:
@model Enum
@{
var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType)
?? ViewData.ModelMetadata.ModelType;
}
<div class="form-group">
<select class="form-control">
@if (ViewData.ModelMetadata.IsNullableValueType)
{
<option selected="@ReferenceEquals(Model, null)">Not Specified</option>
}
@foreach (var value in Enum.GetValues(type))
{
<option selected="@value.Equals(Model)">@value</option>
}
</select>
</div>
您的 select 元素似乎没有呈现该元素的 name
。如果没有 name
属性,数据将不会与您模型中的适当 属性 相匹配。
您可以通过在浏览器中编辑 HTML 并在 select 输入中添加属性 name="BlockType"
来仔细检查它,然后尝试发布表单以查看它是否有效。
最终目标是从我的 View
中序列化一个 abstract
类型,供我的 Controller
使用。
我的抽象类型有一个 enum
属性 ,其名称对应于具体派生类型的名称;这就是我将如何确定 select 的具体类型。这个enum
的值是通过反射在抽象类型的构造函数中设置的:
[JsonConverter(typeof(BlockJsonConverter)]
public abstract class Block{
[NotMapped, JsonProperty]
public BlockType BlockType {get; set;}
public string Name {get;set:}
public int Height{get;set;}
public int Width {get;set;}
public int Depth {get;set;}
protected Block(){
BlockType = Enum.TryParse(GetType().Name, out BlockType blocktype)
?? blocktype : BlockType.Unknown
}
}
public enum BlockType {
Long, Short, Tall, Unknown
}
public class Long : Block { /*...*/ }
public class Short : Block { /*...*/ }
public class Tall : Block { /*...*/ }
public class Unknown : Block { /*...*/ }
Block
class 被 Entity Framework 使用,但 BlockType
属性 未存储在数据库中,因此 BlockType
属性 标记为 [NotMapped]
属性;但是,因为我希望 属性 从视图到控制器往返,所以我用 [JsonProperty]
属性标记了它。
我创建了一个 TestModelBinder
来处理从视图到控制器的反序列化:
public class TestModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
return base.CreateModel(controllerContext, bindingContext,
GetModelType(controllerContext, bindingContext, modelType));
}
protected override ICustomTypeDescriptor GetTypeDescriptor(
ControllerContext controllerContext,ModelBindingContext bindingContext)
{
var modelType = GetModelType(controllerContext, bindingContext, bindingContext.ModelType);
return new AssociatedMetadataTypeTypeDescriptionProvider(modelType)
.GetTypeDescriptor(modelType);
}
private static Type GetModelType(ControllerContext controllerContext, ModelBindingContext bindingContext,
Type modelType)
{
if (modelType.Name == "Block")
{
• breakpoint
// get the value from bindingContext for BlockType
// and return the concrete type based on that
}
return modelType;
}
}
当我遇到上面的那个断点时,bindingContext
在它的 ValueProvider.FormValueProvider
中没有 BlockType
对于我的 BlockType
属性 - 但 Name
、Height
、Width
和 Depth
属性按预期列出。
它们在 EditorTemplate 中都以相同的方式列出:
@model Block
<div class="form-row">
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Name)
</div>
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.BlockType)
</div>
</div>
<div class="form-row">
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Height)
</div>
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Width)
</div>
<div class="col">
@Html.BootstrapEditorGroupFor(m => m.Depth)
</div>
</div>
... 而 BootstrapEditorGroupFor
助手只生成通常的标签、基于类型(枚举、字符串等)的编辑器和验证消息。枚举的编辑器模板如下:
@model Enum
@{
var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType)
?? ViewData.ModelMetadata.ModelType;
}
<div class="form-group">
<select class="form-control">
@if (ViewData.ModelMetadata.IsNullableValueType)
{
<option selected="@ReferenceEquals(Model, null)">Not Specified</option>
}
@foreach (var value in Enum.GetValues(type))
{
<option selected="@value.Equals(Model)">@value</option>
}
</select>
</div>
您的 select 元素似乎没有呈现该元素的 name
。如果没有 name
属性,数据将不会与您模型中的适当 属性 相匹配。
您可以通过在浏览器中编辑 HTML 并在 select 输入中添加属性 name="BlockType"
来仔细检查它,然后尝试发布表单以查看它是否有效。