POCO,ORM和不变性。如何让它们一起工作?

时间:2012-11-09 14:30:42

标签: c# orm poco immutability

假设我有以下C#类,我希望它是不可变的。您只能使用参数化构造函数来设置它。

public class InsulineInjection
{
    private InsulineInjection()
    {
        // We don't want to enable a default constructor.
    }

    public InsulineInjection(Millilitre millilitre, DateTime dateTime, string remark)
    {
        this.Remark = remark;
        this.DateTime = dateTime;
        this.Millilitre = millilitre;
    }

    public string Remark { get; private set; }
    public DateTime DateTime { get; private set; }
    public Millilitre Millilitre { get; private set; }
}

现在我想使用ORM来创建这个POCO。但是,据我所知,所有.NET ORM都希望可以访问属性,并且有一个公共构造函数可以创建这个POCO。所以我必须将我的POCO改为:

public class InsulineInjection
{
    public InsulineInjection()
    {
    }

    public InsulineInjection(Millilitre millilitre, DateTime dateTime, string remark)
    {
        this.Remark = remark;
        this.DateTime = dateTime;
        this.Millilitre = millilitre;
    }

    public string Remark { get; set; }
    public DateTime DateTime { get; set; }
    public Millilitre Millilitre { get; set; }
}
然而,这使我的POCO再次变得可变。使用它的人可以随后改变任何不属于我想要的属性。

据我所知,我可以通过两种不同的方式解决这个问题:

  1. 编写我自己的数据访问层(或修改orm),以便能够使用我创建的构造函数创建正确的POCO实例。
  2. 创建某种映射器。让ORM创建简单的DTO对象,并使用映射器在适当的时候将DTO对象转换为我的POCO。
  3. 我倾向于解决方案2.有人有一个如何做到这一点的例子吗?或者有人有比我上面描述的解决方案更好的解决方案吗?

4 个答案:

答案 0 :(得分:5)

只要有默认构造函数和setter(无论它们是否公开,它们只需要存在),许多OR / Ms都会工作。

所以这不会工作(没有默认构造函数):

public class InsulineInjection
{
    public InsulineInjection(Millilitre millilitre, DateTime dateTime, string remark)
    {
        this.Remark = remark;
        this.DateTime = dateTime;
        _millilitre = millilitre;
    }

    public string Remark { get; set; }
    public DateTime DateTime { get; set; }
    public Millilitre Millilitre { get { return _millilitre; } }
}

或者这个(没有最后一个属性的setter)

public class InsulineInjection
{
    public InsulineInjection(Millilitre millilitre, DateTime dateTime, string remark)
    {
        this.Remark = remark;
        this.DateTime = dateTime;
        _millilitre = millilitre;
    }

    public string Remark { get; set; }
    public DateTime DateTime { get; set; }
    public Millilitre Millilitre { get { return _millilitre; } }
}

虽然这样可行:

public class InsulineInjection
{
    protected InsulineInjection()
    {
        // works with many OR/Ms
    }

    public InsulineInjection(Millilitre millilitre, DateTime dateTime, string remark)
    {
        this.Remark = remark;
        this.DateTime = dateTime;
        this.Millilitre = millilitre;
    }

    public string Remark { get; private set; }
    public DateTime DateTime { get; private set; }
    public Millilitre Millilitre { get; private set; }
}

答案 1 :(得分:3)

为避免您自己的代码使用构造函数,您可以使用Obsolete attribute

[Obsolete("Default constructor only here for the ORM", true)]
public InsulineInjection() {}

如果您实际调用此构造函数,则在属性中传递true将产生编译器错误。

答案 2 :(得分:2)

并非所有.NET ORM都需要公共属性访问才能写入数据。 NHibernate支持将数据写入私有字段或setter。但是,您需要遵循一个允许的字段命名约定,以便它可以从属性名称中推断出字段名称。

Check out NHibernate documentation about property mapping,特别是看表5.1。访问策略和表5.2。命名策略。查看nosetter访问策略并选择符合您编码风格的命名策略。

答案 3 :(得分:1)

此方案由dapper-dot-net原生处理。

您只需要查询字段的顺序和类型以匹配构造函数参数的顺序和类型。因此,对于您的类,查询将类似于:

select millilitre, 
    ,dateTime
    ,remark
from injections

不幸的是,dapper不知道如何将某些东西从十进制转换为毫升。