【问题标题】:How should I model static reference data with Entity Framework?我应该如何使用 Entity Framework 对静态引用数据进行建模?
【发布时间】:2023-03-30 08:46:01
【问题描述】:

我有一个模拟模型,它使用数据库存储输入和输出数据,使用实体框架和数据库优先方法。通过数据访问层或多或少地查询数据库,如下所述:https://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/

但是,使用的一些静态输入数据存储在数据库中,而是作为字段硬编码到应用程序中。此数据是真正的静态数据,在应用程序运行时不会更改。例如:

public class Currency
{
    public string Symbol { get; private set; }
    public string Name { get; private set; }

    private Currency()
    {
    }

    // Fields like this store reference data
    public static readonly Currency USD = new Currency
    {
        Symbol = "USD",
        Name = "US Dollar"
    };

    public static readonly Currency EUR = new Currency
    {
        Symbol = "EUR",
        Name = "Euro"
    };
}

这样做的好处是引用参考数据就像使用例如Currency.USD 贯穿整个模型,无需经过数据访问层。这种实现方式的缺点是数据模型很笨拙并且不再是真正的关系(在某种意义上,关系是通过外键强制执行的);使用上述参考数据的模型对象,例如

public class Transaction
{
    public int Id { get; set; }
    public Currency Currency { get; set; }
    public double Price { get; set; }
}

在数据库中有一个如下所示的后备表:

create table Transaction
(
    Id int not null primary key,
    Currency nvarchar(3) not null , -- Currency symbol, not a foreign key
    Price float not null 
);

货币属性在通过业务层读写时在字符串和对象之间来回转换。

我想用以下目标重写它:

  • 将静态参考数据与所有其他数据一起存储在数据库中,以保持数据模型的清洁。
  • 不必每次需要静态参考数据时都查询数据访问层(即尽可能接近硬编码的Currency.USD)。在整个模拟运行过程中,参考数据可能会在启动时读取一次,然后查询 1,000,000,000 次。

我在这里寻找某种缓存机制吗?这可能足够高效吗?一般而言,特别是对于实体框架来说,解决这个问题的优雅方法是什么?

谢谢。

【问题讨论】:

  • 由于每个查找对象都具有除了名称之外的属性,因此枚举本身可能是不够的(除非我误解了文章,对 VB 不太熟悉)。但现在我想一想,我要寻找的本质上是一个具有属性的枚举,或者尽可能接近的东西。
  • 看看this post,它包含有关如何在 C# 中获取类似 java 的枚举(带有属性)的答案。我认为this answer 可能适合您与 EF 相关的情况,您可以通过缓存属性收集部分对其进行一些优化。
  • 另外,如果您只使用 2 个属性,那么符号 = 值和名称 = 描述。这正是我们为货币所做的。我们可以根据需要参考。
  • @RMH 有趣的方法,我会测试类似的东西。我想我们可以使用枚举来保存参考数据的标签(可以这么说),而实际数据(即其他所有数据)来自装饰每个枚举值并从数据库加载的属性。不过,不确定生成的数据模型会是什么样子。

标签: c# entity-framework oop design-patterns data-modeling


【解决方案1】:

以下是使用 EF 执行此操作的模式的想法:

此示例中的静态引用数据“颜色”具有非整数键和额外属性。这两者都不能用枚举来完成。它使用初始化程序将参考数据添加到数据库中,但在数据库优先中,您只需提前添加它,并假设它就在那里。

此模式的一个变体将使用启动时从数据库中检索的引用实体的惰性静态集合,而不是在类定义中硬编码值。无论哪种情况,在 SaveChanges 中将引用实体标记为 Unchanged 会阻止 EF 尝试将它们插入数据库。

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;

namespace Ef6Test
{

    public class Color
    {
        public static Color Red = new Color() { Name = "Red", RGB = 0xFF0000 };
        public static Color Green = new Color() { Name = "Green", RGB = 0x00FF00 };
        public static Color Blue = new Color() { Name = "Blue", RGB = 0x0000FF };
        [Key]
        public string Name { get; set; }
        public int RGB { get; set; }
    }

    public class Car
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public string ColorName { get; set; }
        public Color Color { get; set; }
    }

    class Db : DbContext
    {

        public DbSet<Color> Colors { get; set; }
        public DbSet<Car> Cars { get; set; }


        public override int SaveChanges()
        {
            return SaveChanges(false);
        }
        public int SaveChanges(bool includeStatic = false)
        {
            if (!includeStatic)
            {
                foreach (var e in ChangeTracker.Entries<Color>())
                {
                    e.State = EntityState.Unchanged;
                }
            }

            return base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }

    class MyInitializer : DropCreateDatabaseAlways<Db>
    {
        protected override void Seed(Db context)
        {
            base.Seed(context);
            context.Colors.AddRange(new[] {Color.Red,Color.Green,Color.Blue });
            context.SaveChanges(true);

        }
    }

    class Program
    {    
        static void Main(string[] args)
        {    
            Database.SetInitializer(new MyInitializer());

            using (var db = new Db())
            {
                db.Database.Log = m => Console.WriteLine(m);
                db.Database.Initialize(true);
            }
            using (var db = new Db())
            {
                db.Database.Log = m => Console.WriteLine(m);

                var c = db.Cars.Create();
                c.Color = Color.Red;
                db.Cars.Add(c);

                db.SaveChanges();

            }


            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}

【讨论】:

  • 重要区别:Color 由数据库中结构相同的表表示。但是,OP 的Currency 仅由字符串列表示。我认为没有办法将 CLR 对象映射到字符串。
  • 但是 OP 说他想要:“将静态参考数据与所有其他数据一起存储在数据库中,以保持数据模型的清洁。”
  • 关于“在启动时从数据库中检索的引用实体的惰性静态集合,而不是在类定义中硬编码值” - 您认为访问这些实体的最佳方式是什么实体?我的第一个想法是每个实体的静态访问器方法,它将枚举作为参数,其中枚举允许的值并返回数据库支持的实体。例如。 Currency usd = Currency.Get(CurrencySymbols.USD);。不过,我必须维护查找值和 DB 支持的实体之间的映射。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-17
  • 1970-01-01
  • 2019-05-31
  • 2019-10-25
  • 2010-12-15
相关资源
最近更新 更多