asp.net mvc - Json.Net: el método de ayuda HTML no se regenera
asp.net-mvc angularjs (1)
Esto parece ser un error con
CamelCasePropertyNamesContractResolver
.
Su clase base,
DefaultContractResolver
, tiene dos constructores: un
constructor sin parámetros
y una versión
DefaultContractResolver (Boolean)
(que acaba de quedar obsoleta en Json.NET 7.0).
Este parámetro tiene el siguiente significado:
shareCache
Tipo: System.Boolean
Si se establece en verdadero,
DefaultContractResolver
utilizará un caché compartido con otros resolvers del mismo tipo. Compartir el caché mejorará significativamente el rendimiento con múltiples instancias de resolución porque la reflexión costosa solo ocurrirá una vez. Esta configuración puede causar un comportamiento inesperado si se supone que diferentes instancias del solucionador producen resultados diferentes. Cuando se establece en falso, se recomienda reutilizar las instanciasDefaultContractResolver
conJsonSerializer
.
El valor predeterminado es
false
.
Desafortunadamente, el
CamelCasePropertyNamesContractResolver
para
CamelCasePropertyNamesContractResolver
establece el valor en
true
:
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
public CamelCasePropertyNamesContractResolver()
#pragma warning disable 612,618
: base(true)
#pragma warning restore 612,618
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
}
Además, no hay un segundo constructor con la opción
shareCache
.
Esto rompe su
SpecificFieldsResolver
.
Como solución alternativa, puede derivar su resolución de
DefaultContractResolver
y usar
CamelCaseNamingStrategy
para hacer la asignación de nombre:
public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
public IndependentCamelCasePropertyNamesContractResolver()
: base()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
}
public class SpecificFieldsResolver : IndependentCamelCasePropertyNamesContractResolver
{
// Remainder unchanged
}
Tenga en cuenta que si está utilizando una versión de Json.NET anterior a 9.0,
CamelCaseNamingStrategy
no existe.
En cambio, un kludge
CamelCasePropertyNamesContractResolver
anidado se puede utilizar para asignar los nombres:
public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
class CamelCaseNameMapper : CamelCasePropertyNamesContractResolver
{
// Purely to make the protected method public.
public string ToCamelCase(string propertyName)
{
return ResolvePropertyName(propertyName);
}
}
readonly CamelCaseNameMapper nameMapper = new CamelCaseNameMapper();
protected override string ResolvePropertyName(string propertyName)
{
return nameMapper.ToCamelCase(propertyName);
}
}
Me encuentro con un problema en el que un método de ayuda html ASP.NET MVC que creé no se "regenera" cada vez que se llama.
El propósito del método auxiliar es crear objetos Javascript para usar en un marco angularjs. Por ejemplo, aquí hay un fragmento de código donde se usa el método auxiliar (llamado desde una etiqueta de script de una página html):
var app = angular.module( "appName", ["ui.bootstrap"] );
app.controller( ''appCtrl'', function( $scope ) {
$scope.model = @Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role" } );
} );
Model es una instancia de una clase que tiene una variedad de propiedades, pero solo quiero que FirstName, LastName, ID y Role se serialicen en un objeto javascript.
El método auxiliar ToJavascript () se define en una clase statis de la siguiente manera:
public static HtmlString ToJavascript( this HtmlHelper helper, object toConvert, string[] includedFields = null, Formatting formatting = Formatting.Indented, ReferenceLoopHandling loopHandling = ReferenceLoopHandling.Ignore )
{
using( var stringWriter = new StringWriter() )
using( var jsonWriter = new JsonTextWriter( stringWriter ) )
{
var serializer = new JsonSerializer()
{
// Let''s use camelCasing as is common practice in JavaScript
ContractResolver = new SpecificFieldsResolver( includedFields ),
Formatting = formatting,
ReferenceLoopHandling = loopHandling,
};
// We don''t want quotes around object names
jsonWriter.QuoteName = false;
serializer.Serialize( jsonWriter, toConvert );
return new HtmlString( stringWriter.ToString() );
}
}
Esto utiliza Json.NET para hacer la serialización real.
Una de las muchas características interesantes de Json.NET es que le permite definir, sobre la marcha, qué campos se serializan. Eso es lo que hace SpecificFieldsResolver. Lo he definido de la siguiente manera:
public class SpecificFieldsResolver : CamelCasePropertyNamesContractResolver
{
private string[] _included;
public SpecificFieldsResolver( string[] included )
{
_included = included;
}
protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization )
{
JsonProperty prop = base.CreateProperty( member, memberSerialization );
bool inclField = ( _included == null )
|| _included.Contains( member.Name, StringComparer.CurrentCultureIgnoreCase );
prop.ShouldSerialize = obj => inclField;
return prop;
}
}
Lo que me confunde es la forma en que se llama CreateProperty (). Específicamente, parece que solo se llama una vez por cada tipo de objeto que se serializa.
Eso es un problema porque en otro archivo cshtml tengo otra llamada a ToJavascript () que está tratando de serializar el mismo tipo de objeto, pero con diferentes campos para la salida de la serialización:
var app = angular.module( "app2Name", ["ui.bootstrap"] );
app.controller( ''app2Ctrl'', function( $scope ) {
$scope.model = @Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role", "Category", "VoterID" } );
} );
Category y VoterID también son campos de clase válidos. Pero ToJavascript () no los seraliza. En cambio, solo serializa los campos definidos en la primera llamada a ToJavascript () ... a pesar de que esa llamada se realiza en un archivo cshtml diferente. Es como si SpecificFieldsResolver recuerda los objetos JsonProperty que crea.
Pensamientos?
Actualizar
Gracias a dbc por diagnosticar exactamente lo que estaba mal y sugerir una solución alternativa. Lo modifiqué ligeramente porque confío en la resolución de nombres de casos de camellos de Json.NET en varios solucionadores:
public class CamelCaseNameMapper : CamelCasePropertyNamesContractResolver
{
public string ToCamelCase( string propertyName )
{
return ResolvePropertyName( propertyName );
}
}
public class MaoDefaultContractResolver : DefaultContractResolver
{
private CamelCaseNameMapper _mapper = new CamelCaseNameMapper();
protected override string ResolvePropertyName( string propertyName )
{
return _mapper.ToCamelCase( propertyName );
}
}
Ahora, cada solucionador, como mi SpecificFieldsResolver, que deriva de MaoDefaultContractResolver, hereda automáticamente la carcasa del camello pero evita el problema de almacenamiento en caché que el dbc identificó.