2016-08-13 2 views
2

Basisklasse: AbstractBuildBlock, Abgeleitet: TextBlock, ImageBlock, EmptyBlock, ....Modellbindung und Vererbung ASP MVC 5

hier Blocks: Site -> Seiten [someIndex] -> Zeilen [someIndex] -> BuildBlocks

Felder BuildBlocks ist vom Typ AbstractBuildBlock, also wenn ich Site DB bin Speichern in BuildBlocks jeder Datensatz hat descriminator AbstractBuildBlock . Ich versuche, die nächsten in BuildBlockRepository zu tun:

switch(obj.ContentType) 
      { 
       case "text": 
        obj = obj as TextBlock; 
        context.BuildBlocks.Add(obj); 
        break; 
      } 

Nach obj = obj as TextBlockobjnull ist. Der Grund dafür ist, dass obj vom Typ AbstractBuildBlock ist. Ich fand bei msdn, dass dieser Code funktionieren sollte:

BaseClass a = new DerivedClass() 
DerivedClass b = a as DerivedClass 

Also muss ich diesen Code bei Modell verbindlich reproduzieren. Dies ist Ajax-Anforderung:

$('.saveSite').click(function() { 
    $.ajax({ 
     url: '/Site/Update', 
     data: { site: getSite() }, 
     method: 'POST', 
     success: function (data) { 
      console.log('Success save'); 
     }, 
     error: function (data) { 
      debugBox(data); 
     } 
    }); 
}); 

und mvc Aktion für diese Anfrage

public string Update(Site site) 
    { 
     siteRepository.Add(site); 
     return "Success"; 
    } 

Also ich Site in json Form, BuildBlocks senden, die auch in dieser Seite in Form von json sind, aber natürlich ihre (Blöcke) ist kein AbstractBuildBlock, sie sind alle aus TextBlock, ImageBlock usw. und haben Felder mit Werten.

Das Problem: Site Feld BuildBlocks hat die Art AbstractBuildBlock und Binder Modell etwas tun, wie das ist:

buildBlock = new AbstractBuildBlock(); //loose derived classes fields and posibility to convert it in DerivedClass 
buildBlocks.push(buildBlock) 

aber ich brauche sowas wie diese

switch(buildBlock.contenType) { 
    case "text" : buildBlock = new TextBlock();buidlBlocks.push(buildBlock); 
} 
+1

Werfen Sie einen Blick auf: http://stackoverflow.com/questions/7222533/polymorphic-model-binding, was ich denke, kann hilfreich sein. Übrigens, es ist klar, dass Sie wirklich hart daran gearbeitet haben, diese Frage zu formulieren - viele Leute, die nach SO fragen, bemühen sich nicht um ihre Fragen, aber Sie haben es getan. Allerdings denke ich, dass Sie in diesem Fall Ihre Frage etwas vereinfachen können, indem Sie sich auf "obj = obj als TextBlock" konzentrieren und warum es nicht funktioniert. Gibt uns den vollständigen Code dafür, aber konzentrieren Sie sich weniger auf die anderen Sachen (halten Sie gerade genug, damit wir eine Vorstellung davon haben, was Sie tun). – DWright

+1

Sie können Json.NET für Ihr Modellbinder verwenden, wie in [Wie verwendet man Json.NET für JSON-Modellbindungen in einem MVC5-Projekt?] (Https://stackoverflow.com/questions/23995210), sie verwenden einen 'JsonConverter 'zu einem konkreten Typ auf Basis der im JSON vorhandenen Eigenschaften zu deserialisieren, wie in [Deserialisieren von polymorphen JSON-Klassen ohne Typinformation unter Verwendung von json.net] (https://stackoverflow.com/questions/19307752) gezeigt. – dbc

Antwort

0

JSON NET Custom deserializer not work at all

ASP MVC 5 How to read object from request with the help of JSON NET

Blick auf Antworten in zwei Links oben die korrekte Ajax-Aufruf

und der Server-Code unter

mvc Aktion

public string Update(Site site) 
    { 
     TextBlock block = site.Pages[0].Rows[0].BuildBlocks[0] as TextBlock; 
     siteRepository.Add(site); 
     return "Success"; 
    } 

AbstractJsonCreationConverter Ich habe es in Infrastructure Ordner erstellt beschrieben wird

public abstract class AbstractJsonCreationConverter<T> : JsonConverter 
{ 
    protected abstract T Create(Type objectType, JObject jsonObject); 

    public override bool CanConvert(Type objectType) 
    { 
     return typeof(T).IsAssignableFrom(objectType); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, 
     object existingValue, JsonSerializer serializer) 
    { 
     var jsonObject = JObject.Load(reader); 
     var target = Create(objectType, jsonObject); 
     serializer.Populate(jsonObject.CreateReader(), target); 
     return target; 
    } 

    public override void WriteJson(JsonWriter writer, object value, 
    JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Und in dem gleichen Ordner konkrete Klasse

public class JsonBuildBlockConverter : AbstractJsonCreationConverter<AbstractBuildBlock> 
{ 
    protected override AbstractBuildBlock Create(Type objectType, JObject jsonObject) 
    { 
     var type = jsonObject["contentType"].ToString(); 
     switch(type) 
     { 
      case "text": 
       return new TextBlock(); 
      default: 
       return null; 
     } 
    } 
} 

und eine weitere Klasse in Infrastruktur

internal class SiteModelBinder : System.Web.Mvc.IModelBinder 
{ 
    public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext) 
    { 
     // use Json.NET to deserialize the incoming Position 
     controllerContext.HttpContext.Request.InputStream.Position = 0; // see: https://stackoverflow.com/a/3468653/331281 
     Stream stream = controllerContext.RequestContext.HttpContext.Request.InputStream; 
     var readStream = new StreamReader(stream, Encoding.UTF8); 
     string json = readStream.ReadToEnd(); 
     return JsonConvert.DeserializeObject<Site>(json, new JsonBuildBlockConverter()); 
    } 
} 

Die letzte Klasse Modelbinder ist, der zu analysieren Variablen vom Typ Website aufgerufen wird, um es Ihnen in Global.asax.cs registrieren müssen, damit es funktioniert in ApplicationStart()

protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     BundleConfig.RegisterBundles(BundleTable.Bundles); 
     ModelBinders.Binders.Add(typeof(Site), new SiteModelBinder()); //RegisterModelBinder for Site 
    } 

Das ist alles.