14

我有我的内容模型:

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

要显示数据,只有上面的模型就可以了。但我想添加编辑内容的功能。所以我需要为成员内容添加一个属性——但这应该只发生在作者按下编辑按钮时,而不是在内容的常规视图中。

所以我创建了第二个模型,它继承自BaseModel以便我可以用我的属性覆盖成员:

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

这工作正常,但由于继承 EF 创建一个额外的列鉴别器。它包含类的类型作为字符串。在我的情况下,它总是BaseModel因为我总是在将EditableBaseModel转换为BaseModel之前它被保存到数据库中,如下所示:

myBbContextInstance.BaseModels.Add(editableBaseModelInstance as EditableBaseModel);

因此,鉴别器列是浪费空间,我想删除它。我发现这可以使用 NotMapped-attribute来完成。但是当我尝试保存模型时,这将导致以下异常:

找不到 EntityType 'EditableBaseModel' 的映射和元数​​据信息。

似乎 NotMapped 属性会让 EF 知道存在另一个继承自BaseModel的类,但 EF 不会获得有关此类的任何信息。但这不是我想要的。我需要告诉 EF:EditableBaseModel不是它应该关心的,因为它只适合我的视图,并且永远不会用于数据库。

我怎样才能做到这一点?我发现的唯一方法是将EditableBaseModel实例手动转换为BaseModel对象,如下所示:

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

但这似乎不是一个好方法,因为我有多重属性。而且它也不是很灵活,因为当我向 BaseModel 添加一些东西时,我也必须在这里添加它——这可能会导致奇怪的错误。

4

3 回答 3

8

EF将概念与MVC概念混合在一起Model可能并不适合两者。在这种情况下,像您一样创建新BaseModel内容并将其复制EditableBaseModel到其中BaseModel,是正确的方法。您可以使用AutoMapper在两个模型之间映射数据。

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);
    .
    .
    .
}
于 2015-08-15T04:41:02.937 回答
4

底线是,在 Entity Framework 中使用继承,您不能用两种不同的类型来表示数据库中的相同记录

换句话说,如果您以任何方式使用继承,EF 只能将数据库中的任何行具体化为一种类型。因此,无论有没有鉴别器,你想要的都是不可能的。

我认为转换和转换EditableBaseModel是一个可行的选择。或者将 a 包裹BaseModel在 aEditableBaseModel中,后者具有委托属性,例如

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

这是一种常见的模式,称为Decorator。请注意,在这种情况下(或您的转换),您不应EditableBaseModel在 EF 模型中注册为实体。

从技术上讲,另一种方法是可能的。您可以通过 实现任何对象DbContext.Database.SqlQuery。您可以BaseModel仅用于显示目的并EditableBaseModel用作映射的实体类。BaseModels 那么,可以通过

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

当然,可以对查询进行参数化以过滤模型。s 不会被上下文跟踪,BaseModel但因为您只想显示它们,所以没有必要。这是我看到用另一种类型表示(某种)数据库中的一条记录的唯一方法。

虽然我提到了技术上的可能性,但这并不意味着我推荐它。但是,即使对于可编辑选项,我也更喜欢使用视图模型。我不喜欢数据层和 UI 之间的这种紧密耦合。

于 2015-08-18T22:50:39.897 回答
1

您是否考虑过使用以BaseModel下列方式工作的构造函数:

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

并像这样使用它:

myBbContextInstance.BaseModels.Add(new BaseModel(editableBaseModelInstance));
于 2015-08-15T03:31:14.210 回答