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

时间:2015-09-04 19:37:07

标签: c# entity-framework entity-framework-6 immutability

我有一个Point课程:

// 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放在数据库中,因为他们没有设置器。他们不应该这样做,因为这是一个不可改变的阶级。甚至不应该是private set

我有这个代码构建我的模型:

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

是否有任何方法可以保留此类的真正不变性,还可以使EF6可以访问和存储这些值?

3 个答案:

答案 0 :(得分:1)

EF Core 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);
    }
}

最后一个问题是将它与控制器一起使用,所以:

public class PointViewModel // has default parameterless constructcor
{       
    public int Id { get; set; }

    public Distance X { get; set; }

    public Distance Y { get; set; }

    public Distance Z { get; set; }
}

在控制器中使用:

[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);
}

答案 1 :(得分:0)

据我所知,EF会将_xX存储在表格中,因此您必须将其与私人制定者一起公开。

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

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

答案 2 :(得分:0)

在类似的情况下,我必须创建一个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;
    }
}

希望这有帮助!