I have a model of my content:
class BaseModel {
public virtual string Content{ get; set; }
// ...
}
To display the data only the model above is fine. But I want to add the functionality to edit the content. So I need to add an attribute to the member content - But this should only happen when the autor press an edit button, not in the regular view of the content.
So I created a second model which inherits from the BaseModel so that I can override the member with my attribute:
class EditableBaseModel : BaseModel {
[UIHint("MyEditor"), AllowHtml]
public override string Content{ get; set; }
}
This works fine, but because of the inheritance EF create an additional column discriminator. It contains the type of the class as string. In my case its always BaseModel because I always convert EditableBaseModel to BaseModel before It gets saved to the database like this:
myBbContextInstance.BaseModels.Add(editableBaseModelInstance as EditableBaseModel);
Thus, the discriminator-column is a waste of space and I want to remove it. I found out that this can be done using the NotMapped-attribute. But this will result in the following exception when I try to save the model:
Mapping and metadata information could not be found for EntityType 'EditableBaseModel'.
It seems that the NotMapped-attribute will let EF know that another class exists that inherits from BaseModel, but EF won't get any information about this class. But thats not what I want. I need to tell EF: EditableBaseModel is nothing it should care about because its only to fit my view, and would be never used for the database.
How can I do that? The only way I found out is to convert the EditableBaseModel instance manually to a BaseModel object like this:
public ActionResult Save(EditableBaseModel editableBaseModel) {
var baseModel = new BaseModel() {
Content = editableBaseModel.Content
// ...
};
myDbContextInstance.BaseModels.Add(baseModel);
}
But this seems not a good way to do that because I have multiplice attributes. And it's also not very flexible because when I add something to the BaseModel, I have to add it also here - Which can result in strange errors.
Mixing EF
concepts with MVC
concepts into a Model
may not fits for both. In this case creating new BaseModel
and copy the content of EditableBaseModel
into BaseModel
as you did, is the right way. You can use AutoMapper for mapping data between two models.
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);
.
.
.
}
The bottom line is that, using inheritance in Entity Framework, you can't represent the same record in the database by two different types.
Stated differently, if you use inheritance in any way, EF can materialize any row in the database to one type only. So what you want is never possible, with or without discriminator.
I think the conversion to and from EditableBaseModel
is a viable option. Or wrap a BaseModel
in a EditableBaseModel
, where the latter has delegate properties like
public string Content
{
[UIHint("MyEditor"), AllowHtml]
get { return _baseModel.Content; }
set { _baseModel.Content = value; }
}
This is a common pattern, called Decorator. Note that in that case (or with your conversion) you should not register EditableBaseModel
as an entity in the EF model.
Technically, another approach would be possible. You can materialize any object by DbContext.Database.SqlQuery
. You could use BaseModel
for display purposes only and use EditableBaseModel
as the mapped entity class. BaseModel
s then, could be materialized by
myBbContextInstance.Database.SqlQuery<BaseModel>("SELECT * FROM dbo.BaseModel");
Of course the query can be parametrized to filter the models. The BaseModel
s will not be tracked by the context, but as you only want to display them, that's not necessary. This is the only way I see to represent (sort of) one record in the database by another type.
While I mention the technical possibility, that doesn't mean I recommend it. But then, even for the editable option I'd prefer using view models. I don't like this tight coupling between data layer and UI.
Have you considered using a constructor in BaseModel
which works in the following way:
public BaseModel(EditableBaseModel editableBaseModel) {
this.Content = editableBaseModel.Content
}
and use it like this:
myBbContextInstance.BaseModels.Add(new BaseModel(editableBaseModelInstance));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With