【问题标题】:CustomType on references with FluentNHibernate使用 FluentNHibernate 引用的 CustomType
【发布时间】:2019-04-10 04:19:03
【问题描述】:

我正在使用一个名为 LanguageExt 的库。该库提供了一些工具来处理 C# 代码中的函数式编程。我还使用 FluentNHibernate 将我的域类映射到我的数据库。

当一个属性可以为空时,我想使用来自 LanguageExt 的Option<T>。它是一个结构体,要么持有一个值,要么等于一个 None。

我的一个类模型,比如Car 有一个可选属性,比如Sunroof,它的类型是Option<Window>。像这样:

public class Car
{
   Window _sunroof;
   Option<Window> Sunroof
   {
     get => Optional(_sunroof);
     set => _sunroof = value.IfNoneUnsafe(() => null);
   }
}

我的映射是这样的:

References<Window>(x => x.Sunroof, "idSunroof")
   .Not.Nullable();

我的问题是:我如何使用其支持字段映射 Sunroof 属性,知道它们不共享相同的返回类型?

【问题讨论】:

  • 我对 NHibernate 不熟悉。实体框架具有称为实体的 DTO,它们有助于弥合数据库中的数据结构与业务模型中的数据结构之间的差距。 NHibernate 有这样的东西吗?
  • 是的,实际上 Car 类就是这样。它是一个域模型,但也通过 FluentNHibernate 完成的映射配置映射到数据库中的某个表。

标签: c# nhibernate fluent-nhibernate language-ext


【解决方案1】:

它是一个领域模型,但也通过 FluentNHibernate 完成的映射配置映射到数据库中的某个表。

我认为这不是一个好主意。您正在尝试在这一类中做三(或四)件事,我会分开。

我建议为 NHibernate 使用 DTO(可能称为 CarDto)和业务模型(可能称为 Car)。这样,CarDto 可以因为与数据库相关的原因而改变(但不是因为建模原因),Car 可以因为建模原因而改变(但不是因为数据库原因)。例如,函数式编程会使业务模型不可变,但 NHibernate 可能要求其 DTO 是可变的。如果你为这两个目的使用相同的类型,那么你就不能满足所有的设计约束。

如果知道 Sunroof 属性不共享相同的返回类型,我该如何使用它的支持字段来映射它们?

我认为您不应该拥有不同类型的属性和支持字段。对于CarDto,使用null 表示没有Window。然后当从CarDto 映射到Car 时,将null 映射到None 状态(通过您当前使用的Optional 函数)。然后在从Car 映射到CarDto 时,将None 映射回null(通过您当前使用的IfNoneUnsafe 方法)。

你的Car 班级

  1. 是 NHibernate 的 DTO,
  2. 是您的商业模式,
  3. 包含从 DTO 到业务模型的映射,并且
  4. 包含从业务模型到 DTO 的映射。

这是我上面提到的三或四件事(取决于你是否将映射算作一件事或两件事)。

于 2019 年 2 月 20 日添加

[你的答案是]不是我的问题的解决方案,而是一个更好的架构的建议

两者兼而有之。

我完全同意你所说的,我很乐意这样做,但我不能。在我的代码库中,我有超过 250 个模型类,它们设计得很糟糕,并且有很多错误的依赖项。我无法一次重构所有这些。

我并不是建议您立即更改所有内容。离得很远。按照 Martin Fowler 的 Refactoring 风格,我建议随着时间的推移进行许多小的更改。

例如,将Car 更改为

public class Car
{
   Option<Window> Sunroof
   {
     get => Optional(SunroofBacking);
     set => SunroofBacking = value.IfNoneUnsafe((Window) null);
   }
   Window SunroofBacking { get; set; }
}

出于业务逻辑原因使用(命名为“更好”的)属性Sunroof,出于NHibernate 原因使用SunroofBacking

【讨论】:

  • 这当然是获得最纯粹的架构的方法。不幸的是,在处理遗留代码时,这并不总是现实的。 This article 例如对此事表达了另一种看法。当然,所有这些都是无休止辩论的好来源:)
  • 你好@xlecoustillier。谢谢你的评论。我绝对知道许多其他人更喜欢“不那么纯粹”的方法。在我有限的经验中,“更纯粹”的方法一直是优越的。给定的代码使用函数式编程库 Language Ext。功能代码在软件开发和 C# 开发中通常被认为是“更纯粹的”。我的回答是试图帮助 OP 解决他们的问题,同时让他们的代码更符合使用 LanguageExt.Option&lt;&gt; 的决定。
  • 我 100% 同意这一点:)
  • 非常感谢您的回答,但我不能接受,因为这不是解决我的问题的方法,而是提出更好架构的建议。我完全同意你所说的,我很乐意这样做,但我不能。在我的代码库中,我有超过 250 个模型类,它们设计得很糟糕,并且有很多错误的依赖项。我不能一次重构所有这些。
  • 我更新了我的答案以回复您的评论@Ephasme
【解决方案2】:

我终于找到了解决办法:

References(x => x.Sunroof, "idSunroof")
    .Access.CamelCaseField(Prefix.Underscore)
    .Class<Window>();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    • 1970-01-01
    • 2018-12-17
    • 1970-01-01
    相关资源
    最近更新 更多