使用实体框架模型键入安全密钥

本文关键字:安全 密钥 模型 实体 框架 | 更新日期: 2023-09-27 18:19:52

这是我今天的完美想法:实体框架中的强类型id。

动机:

  1. 比较ModelTypeA.ID和ModelTypeB.ID(至少几乎)总是一个错误。为什么compiletime不处理它
  2. 如果您使用的是每个请求的示例DbContext,那么它很容易实现,可以直接从id获取模型。"id.Value.ProductNumber"
  3. 代码将更加自我声明
  4. 它们只是自然键入的,为什么不呢

好的,这是我的实现。我希望它能很好地说明我的意思。

//Optional interface may be handy on some scenarios
public interface Identifiable<T> where T : class, Identifiable<T>
{
    DbId<T> ID { get; set; }
}
public class TestModel1 : Identifiable<TestModel1>
{
    [Key]
    public DbId<TestModel1> ID { get; set; }
    public string Data1 { get; set; }
}
public class TestModel2 : Identifiable<TestModel2>
{
    [Key]
    public DbId<TestModel2> ID { get; set; }
    public string Data2 { get; set; }
    public DbId<TestModel1> TestModel1ID { get; set; }
    public virtual TestModel1 TestModel1 { get; set; }
}
[Serializable]
public class DbId<T> where T : class
{
    public int ID { get; set; }
    public static implicit operator DbId<T>(int id)
    {
        var c = new DbId<T>() { ID = id };
        return c;
    }
    public static implicit operator int (DbId<T> id)
    {
        return id.ID;
    }       
}

在创建迁移时,其司法机构抱怨没有钥匙。当尝试在fluent api上设置密钥时,会出现更严重的错误:属性"ID"不能用作实体"MyNs.Models.TestModel1"的密钥属性,因为该属性类型不是有效的密钥类型。只有标量类型、字符串和byte[]是受支持的键类型。

Ok理解的键不能是任何类型,但我的类型的数据只是一个int开关,甚至有隐式转换。在这种情况下继承int是非常诱人的,但正如我们所知,这是不可能的。

主要问题:如何完成这个并告诉EF将我的DbId转换为int并返回不是火箭科学。

第二个问题:这个主意好吗?为什么?如果目前无法实现,您是否建议功能请求?

使用实体框架模型键入安全密钥

我相信我理解您的目标:您的目标是封装每个模型的主键,使两个不同模型的主键不能直接比较。因此,例如,您可能希望在编译时避免比较Customer.ID == Order.ID

然而,在您的代码示例中,int <-> DbId<T>implicit operator不符合您的目标,因为此代码编译:

var model1 = new TestModel1() {ID = 1};
var model2 = new TestModel2() {ID = 2};
Console.WriteLine(model1.ID == model2.ID);

所以,如果我遵循你的推理,即使EF6+允许类(而不是string)上的[Key],它也不会起作用

回到基础,如果您认为名称ID太模糊,为什么不遵循实体框架主键约定,即类名后跟"ID"?

示例:

public class Customer
{
    // [Key] is implicit by convention
    public int CustomerID { get; set; }
    public string Name { get; set; }
}
public class Order
{
    // [Key] is implicit by convention
    public int OrderID { get; set; }
    public DateTime SubmittedDate { get; set; }
    // [ForeignKey("Customer")] is implicit by convention
    public int CustomerID{ get; set; }
    public virtual Customer Customer { get; set; }
}

这种命名约定(以及普通的ID)是我在实体框架代码中看到最多的约定。因此,它的主要好处是允许其他人介入并无缝地适应和维护您的代码(这是一个我们修补匠有时都会忽视的好处!)

看看你的动机。。。

比较ModelTypeA.ID和ModelTypeB.ID(至少几乎)总是一个错误。

你在解决一个实际上不是问题的问题吗?程序员多久真正搞砸一次Order.CustomerID == Customer.CustomerID

代码将更加自我声明。

要辩论吗?如果我在某人的代码中发现DbId<Customer> id = Customer.ID,它真的比int id = Customer.CustomerID更具声明性吗?

话虽如此,我为你的努力鼓掌!解决问题是我们程序员喜欢做的事。祝你好运!