1

经过几次尝试和一些搜索,我终于决定在这里尝试一下。

简短的故事:我已经为内容管理系统应用程序创建了一个相当“规范化”的数据库设计,现在我正试图将它映射到 EF 实体。

在此处输入图像描述

我在页面与其内容(ContentVersion 表)之间有 M:N 关系 - 由表 Content 连接。这是因为该应用程序是多语言的,我将存储最近的内容编辑历史。

现在,我可以从我的数据库中创建 DbContext,搭建几个控制器并拥有坚实的基础,但这会有问题。当我尝试创建新页面时,基本上只允许输入标题和标签,但该页面没有内容。我当然可以以某种方式将它与其他实体连接起来,并制作一些分层的业务逻辑,每次都会更新几个实体,但我首先想尝试一种更抽象的方法。

我想我基本上需要实体拆分 3 个相关表。我想创建一个实体(我们称之为 FinalPage),它可以用来将页面作为一个整体工作 -> 用它来搭建控制器等。

FinalPage 是用户在创建新页面时填写的内容,因此它将包含:

Title(来自 Page 实体)
Slug(来自 Page 实体)
TemplateId(来自 Page 实体)
LanguageId(来自 Content 实体)
Title(来自 ContentVersion 实体)
Body(来自 ContentVersion 实体)
+ 来自 ContentVersion 实体的其他相关字段

当我为它生成“创建新”页面时,我会填写所有内容,所有内容都会保存到适当的表格中。

我尝试在我的实体模型中创建新实体,将属性映射到不同的表......尝试了几种方法,但从未得到有效的解决方案。主要是因为典型的“你需要映射每个键”错误。我正在查看Rob Bagby 的这篇文章,但他只连接了两个共享相同密钥的表。

我还尝试了一些感觉非常愚蠢的事情......从页面和内容实体创建实体(entA) - 通过 PageId 属性连接。并从 Content 和 ContentVersion 创建第二个 (entB) - 由 ContentId 连接。然后我想创建第三个(entC)来连接 entA 和 entB。那也没有用(我真的很高兴,因为那将是一个非常奇怪的解决方案)。

我觉得我是:
a.)缺少明显的东西。
b.) 有一个糟糕的数据库设计。
c.) 对英孚的要求太多。

欢迎任何帮助。
- 如果您在数据库设计中看到一些糟糕的东西,请告诉我。
- 如果你知道,如何实现我的目标,请分享。
- 如果您有使用此类应用程序的经验并有更好的解决方案,我想听听。

我基本上也在寻找一些最佳实践。

4

2 回答 2

3

听起来您正在尝试使您的 ORM 适合您的 UI 模型,这不是这样做的方法。您应该创建一个表示要从表单中捕获的数据的新类,然后根据需要将其处理到各种表中。因此,您在创建新页面时将使用的模型将是这样的:

public class FinalPage{
    public string Title { get; set; }
    public string Slug { get; set; }
    public string Body { get; set; }
    public int TemplateId { get; set; }
    public int LanguageId { get; set; }
}

当用户提交表单时(您没有提到您正在使用的 UI),您会将这些值处理到各种 ORM 对象中。

public void SaveData(FinalPage model){
    var page = new Page{
        Title = model.Title,
        ...
    };
    db.Pages.Add(page);

    var contentVersion = new ContentVersion{
        Body = model.Body,
        ...
    };
    db.ContentVersions.Add(contentVersion);

    ...
}

View Model这是 a和 a之间的区别Database Model,不必相同,并且业务逻辑应用于两个实体之间。我在之前的项目中使用过的另一种方法是拥有一个包含您要使用的 ORM 对象的类,这样您就可以在 UI 中获得 DataAnnotation 验证内容。

public class FinalPage{
    public Page Page { get; set; }
    public ContentVersion ContentVersion { get; set; }
    public Content Content { get; set; }
}

然后显示每个内部对象的相应字段。在带有 Razor 的 MVC4 中:

@Html.EditorFor(m=>m.Page.Title)

这将产生您期望的验证错误,并且在数据模型中定义,但是您需要注意您可能需要确保以后正确处理对象(例如在 MVC 中,您ModelState几乎总是无效) . 我个人会推荐第一种方法,因为这是使用不同 UI 和 ORM 模型进行操作的传统方法。我遵循的一般规则是,如果可以,请使用 ORM 模型,但一旦它变得有点棘手,就切换到 ViewModel 类来表示您的值。

于 2012-10-25T12:43:24.843 回答
1

并非所有 ORM 都是相同的,但该模式在敏捷数据中运行良好。

虽然大多数 ORM 会让您在敏捷数据中使用多个实体,但您可以通过扩展甚至在对象级别上重用 ContentVersion 的定义:

// Single-table model
class ContentVersion extends \atk4\data\Model {
    public $id_field = 'contentVersionId';

    function init() {
        parent::init();
        $this->addField('VerisonTitle', ['actual'=>'Title']); 
        $this->addfield('Body', ['type'=>'text']);
    }
}

// Separate entity for 3-table join
class FinalPage extends ContentVersion {

    function init() {
        parent::init(); // initializes fields from 'Page'

        $j_content = $this->joinLeft('Content.ContentId', 'ContentId');
        $j_content->hasMany('LanguageId', new Language());
        $j_content->addField('ContentId');
        $j_content->addFiled('PageId');

        $page = $this->joinLeft('Page.PageId', 'PageId');
        $page->hasOne('TemplateId', new Template());
        $page->hasOne('ParentId', new Page());

        $page->addField('Slug');
        $page->addField('Title');
    }
}

要使用数据,您可以直接访问它:

$fp = new FinalPage($db);
$fp->load($content_version_id);

var_dump($fp->get());

$fp->set('..')
$fp->save();

该模型将连接所有字段,并与通用的敏捷 UI表单类集成。

$form = new \atk4\ui\Form();
$form->setModel(new FinalPage($db));

$html = $form->render();

表单将包含所有表格中的所有字段,保存后它将自动以正确的顺序保存它们并将表格链接在一起。我不知道有任何其他 ORM 在做类似的事情。

于 2017-07-11T15:11:53.723 回答