C# 具有不可变类的实体框架6

C# 具有不可变类的实体框架6,c#,entity-framework,entity-framework-6,immutability,C#,Entity Framework,Entity Framework 6,Immutability,我有一个点类: // My immutable Point class public class Point { private readonly Distance _x; private readonly Distance _y; private readonly Distance _z; public Distance X { get { return _x; } } public Distance Y {

我有一个
类:

// My immutable Point class
public class Point
{
    private readonly Distance _x;
    private readonly Distance _y;
    private readonly Distance _z;

    public Distance X
    {
        get { return _x; }
    }

    public Distance Y
    {
        get { return _x; }
    }

    public Distance Z
    {
        get { return _x; }
    }

    // Ok, so this isn't immutable... but it's purely for EF
    public int DatabaseId { get; set; }

    public Point(Distance x, Distance y, Distance z)
    {
        _x = x;
        _y = y;
        _z = z;
    }
}
Distance
是存储单位和值的自定义类。)

太好了。我们热爱不变性。但实体框架不会意识到我需要将X、Y和Z放入数据库中,因为它们没有setter。他们不应该,因为这是一个不可变的类。甚至不应该有一个
私有集

我有以下代码构建我的模型:

    modelBuilder.Entity<Point>()
        .HasKey(point => point.DatabaseId);
modelBuilder.Entity()
.HasKey(point=>point.DatabaseId);

有什么方法可以保持这个类的真正不可变性,但也可以使它使EF6能够访问和存储这些值吗?

据我所知,EF将在表中同时存储
\ux
x
,因此您必须使用私有setter将它们公开

除了EF必须能够设置的主键外,它们仍然是不可变的


或者,您可以添加另一层:不可变类和EF table模型类,当您从数据库中提取时,您会得到一个不可变对象,而可变对象将隐藏在DAL之外。

在类似情况下,我必须创建一个EntityFramework可以使用的私有无参数构造函数,并将
Id
作为
private set
。这对我来说很好,这是我在保持类不变的时间里所能做的最好的事情

因此,在您的情况下,可以按如下方式更新和简化上述类:

public class Point
{
    public Distance X { get; private set; }
    public Distance Y { get; private set; }
    public Distance Z { get; private set; }
    public int DatabaseId { get; private set; } // DatabaseId can't be set publicly

    private Point() { } // Private Parameterless Constructor for EntityFramework

    public Point(Distance x, Distance y, Distance z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}
希望这有帮助

EF核心2.0.1:

型号:

public class Point
{
    [Obsolete("For ORM(EF) use only", true)]
    public Point() { }

    public Point(Distance x, Distance y, Distance z)
    {
        _x = x;
        _y = y;
        _z = z;
    }

    public int Id { get; set; }

    public Distance X { get => _x; }
    private Distance _x;

    public Distance Y { get => _y; }
    private Distance _y;

    public Distance Z { get => _z; }
    private Distance _z;
}
让EF填充私有字段

public class DomainDbContext : DbContext
{      
    /*skipping other stuff you have here*/ 

    public DbSet<Point> Points { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        // Fill Properties with private fields 
        builder.Entity<Point>().Property(m => m.X).UsePropertyAccessMode(PropertyAccessMode.Field);
        builder.Entity<Point>().Property(m => m.Y).UsePropertyAccessMode(PropertyAccessMode.Field);
        builder.Entity<Point>().Property(m => m.Z).UsePropertyAccessMode(PropertyAccessMode.Field);
    }
}
在控制器中使用:

[HttpPost]
public async Task<IActionResult> Create([Bind("X,Y,Z")] PointViewModel info)
{
     if (ModelState.IsValid)
     {
         var point = new Point(info.X, info.Y, info.Z);

         _context.Add(point);
         await _context.SaveChangesAsync();
         return RedirectToAction(nameof(Index));
      }
      return View(info);
}
[HttpPost]
公共异步任务创建([Bind(“X,Y,Z”)]PointViewModel信息)
{
if(ModelState.IsValid)
{
变量点=新点(信息X、信息Y、信息Z);
_上下文。添加(点);
wait_context.SaveChangesAsync();
返回重定向到操作(名称(索引));
}
返回视图(信息);
}

这可能有点挑剔,但这不应该是一个结构吗?您不应该将EF映射到一个可变类,然后使用它来创建struct对象吗?似乎您正试图将一个方形的销钉装入一个圆形的孔中。没有不可变的数据库行这样的东西。如果你需要一个不可变的对象,你可能需要一个从数据实体层到你的不可变类型所在层的映射层。只有使用私有setter才可行。但还有更多的担忧。数据模型应该是什么样子?您可以有一个
表,其中有三个FK到
距离
,但如何确保三个
距离
始终由一个
引用(而不是由任何其他
引用,我想这是您想要的)。
[HttpPost]
public async Task<IActionResult> Create([Bind("X,Y,Z")] PointViewModel info)
{
     if (ModelState.IsValid)
     {
         var point = new Point(info.X, info.Y, info.Z);

         _context.Add(point);
         await _context.SaveChangesAsync();
         return RedirectToAction(nameof(Index));
      }
      return View(info);
}