14

ich ein Modell meines Inhalt haben:ASP.NET EF entfernen Diskriminator Spalte von nicht abgebildet Klasse

class BaseModel { 
    public virtual string Content{ get; set; } 
    // ... 
} 

die Daten anzuzeigen nur das Modell oben in Ordnung ist. Aber ich möchte die Funktionalität hinzufügen, um den Inhalt zu bearbeiten. Also muss ich dem Mitglied Inhalt ein Attribut hinzufügen - Aber das sollte nur passieren, wenn der Autor eine Bearbeitungsschaltfläche drücken, nicht in der regulären Ansicht des Inhalts.

Also habe ich ein zweites Modell, das von der Basemodel erbt so, dass ich das Mitglied mit meinem Attribut überschreiben können:

class EditableBaseModel : BaseModel { 
    [UIHint("MyEditor"), AllowHtml] 
    public override string Content{ get; set; } 
} 

Dies funktioniert gut, aber wegen der Erbschaft EF schaffen eine zusätzliche Spalte Diskriminator. Es enthält den Typ der Klasse als String. In meinem Fall ist es immer Basemodel, weil ich immer konvertieren EditableBaseModel zu Basemodel bevor es wird auf die Datenbank wie folgt gespeichert:

myBbContextInstance.BaseModels.Add(editableBaseModelInstance as EditableBaseModel); 

Somit ist die Unterscheidungs-Säule ist eine Verschwendung von Speicherplatz und ich möchte es zu entfernen. Ich fand heraus, dass dies getan werden kann using the NotMapped-attribute. Dies führt jedoch zu folgender Ausnahme, wenn ich versuche, das Modell zu speichern:

Mapping- und Metadateninformationen wurden für EntityType 'EditableBaseModel' nicht gefunden.

Es scheint, dass die NotMapped-Attribut EF wissen lassen, dass eine andere Klasse, die erbt von Basemodel existiert, aber EF keine Informationen über diese Klasse erhalten. Aber das ist nicht was ich will. Ich muss sagen, EF: EditableBaseModel ist nichts, das es interessieren sollte, weil es nur zu meiner Ansicht passen, und würde nie für die Datenbank verwendet werden.

Wie kann ich das tun? Der einzige Weg, fand ich heraus, ist die EditableBaseModel Instanz manuell auf einen Basemodel Objekt wie folgt zu konvertieren:

public ActionResult Save(EditableBaseModel editableBaseModel) { 
    var baseModel = new BaseModel() { 
     Content = editableBaseModel.Content 
     // ... 
    }; 
    myDbContextInstance.BaseModels.Add(baseModel); 
} 

Aber das scheint kein guter Weg, das zu tun, weil ich multiplice Attribute. Und es ist auch nicht sehr flexibel, denn wenn ich etwas zum BaseModel hinzufüge, muss ich es auch hier hinzufügen - was zu seltsamen Fehlern führen kann.

+0

Warum fügen Sie dem BaseModel nicht einfach ein nicht zugeordnetes boolesches Feld hinzu, das anzeigt, dass der Benutzer den Inhalt ändern möchte? –

+0

Weil ich das Attribut ** UIHint ** verwende, um Teilansichten wie die aus meinem Editor einzufügen. Es scheint, dass es keine Möglichkeit gibt, dieses Attribut zur Laufzeit zu setzen. – Lion

+0

Attribute sind pro Klasse, nicht pro Instanz. Meiner Meinung nach könnten Sie eine Bedingung in einer Teilansicht verwenden, die basierend auf einem Flag-Feld Bearbeitungssteuerelemente ein- oder ausblenden würde. –

Antwort

1

Haben Sie einen Konstruktor in BaseModel betrachtet verwendet, die in der folgenden Art und Weise funktioniert:

public BaseModel(EditableBaseModel editableBaseModel) { 
    this.Content = editableBaseModel.Content 
} 

und es wie folgt verwendet werden:

myBbContextInstance.BaseModels.Add(new BaseModel(editableBaseModelInstance)); 
+0

Ja, ich habe eine ähnliche Lösung ausprobiert, bei der ich eine Methode namens ** ToBaseModel ** im EditableBaseModel platziert habe, die eine BaseModel-Instanz zurückgibt. Es funktioniert, aber ich mag diese Lösung nicht, denn wenn ich das Modell ändere, muss ich auch das Attribut der Konverter-Methode hinzufügen. Also würde ich eine automatisierte Lösung wie Typ Casting bevorzugen, aber leider funktioniert das nicht. – Lion

+0

Ich verstehe. Ich habe an einen Konstruktor gedacht, weil jeder, der das Modell ändert, weiß, dass der Konstruktor ebenfalls geändert werden muss, aber ja, nicht viel Automation dort ... – DDan

8

Misch EF Konzepte mit MVC Konzepte in eine Model kann nicht passt für beide. In diesem Fall ist es richtig, BaseModel zu erstellen und den Inhalt von EditableBaseModel in BaseModel zu kopieren. Sie können AutoMapper zum Zuordnen von Daten zwischen zwei Modellen verwenden.

class EditableBaseModel 
{ 
    [UIHint("MyEditor"), AllowHtml] 
    public string Content{ get; set; } 
} 

public ActionResult Save(EditableBaseModel editableBaseModel) { 
    var baseModel = new BaseModel(); 
    Mapper.Map<EditableBaseModel, BaseModel>(editableBaseModel, baseModel); 
    myDbContextInstance.BaseModels.Add(baseModel); 
    . 
    . 
    . 
} 
4

Unterm Strich ist, dass unter Verwendung von Vererbung in Entity Framework, Sie nicht den gleichen Datensatz in der Datenbank, die von zwei verschiedenen Typen darstellen können.

Anders ausgedrückt: Wenn Sie Vererbung in irgendeiner Weise verwenden, kann EF jede Zeile in der Datenbank nur für einen Typ materialisieren. Was Sie wollen, ist nie möglich, mit oder ohne Diskriminator.

Ich denke, die Umwandlung zu und von EditableBaseModel ist eine praktikable Option. Oder wickeln Sie ein BaseModel in einem EditableBaseModel, wobei letztere Eigenschaften delegieren wie

public string Content 
{ 
    [UIHint("MyEditor"), AllowHtml] 
    get { return _baseModel.Content; } 
    set { _baseModel.Content = value; } 
} 

Dies ist ein gemeinsames Muster, genannt Decorator. Beachten Sie, dass Sie in diesem Fall (oder bei Ihrer Konvertierung) EditableBaseModel nicht als Entität im EF-Modell registrieren sollten.

Technisch wäre ein anderer Ansatz möglich. Sie können jedes Objekt durch DbContext.Database.SqlQuery materialisieren. Sie können BaseModel nur für Anzeigezwecke verwenden und EditableBaseModel als zugeordnete Entitätsklasse verwenden. BaseModel s dann könnte durch

myBbContextInstance.Database.SqlQuery<BaseModel>("SELECT * FROM dbo.BaseModel"); 

Natürlich verwirklicht werden kann die Abfrage parametrisiert werden, um die Modelle zu filtern. Die BaseModel s werden nicht vom Kontext erfasst, aber da Sie sie nur anzeigen möchten, ist dies nicht erforderlich. Dies ist die einzige Möglichkeit, einen Datensatz in der Datenbank nach einem anderen Typ darzustellen.

Während ich die technische Möglichkeit erwähne, heißt das nicht, dass ich es empfehle. Aber selbst für die editierbare Option würde ich lieber Ansichtsmodelle verwenden. Ich mag diese enge Kopplung zwischen Datenschicht und UI nicht.

Verwandte Themen