使用Entity Framework模型键入安全密钥

时间:2015-10-30 16:45:30

标签: c# entity-framework

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

动机:

  1. 比较ModelTypeA.ID和ModelTypeB.ID(至少几乎)始终是错误。为什么不在编译时处理呢?
  2. 如果您正在使用每个请求的示例DbContext,它很容易实现直接从id获取模型。 " id.Value.ProductNumber"
  3. 代码将更加自我声明。
  4. 他们只是自然输入,为什么不呢?
  5. 好的,这是我的实施。我希望它非常自我陈述我的意思。

    //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;
        }       
    }
    

    创建迁移时,它的jus抱怨没有密钥。当试图在流畅的api上设置密钥时,它会给出更多宝贵的错误:财产ID&#39;不能用作实体的关键属性&#39; MyNs.Models.TestModel1&#39;因为属性类型不是有效的键类型。只有标量类型,字符串和byte []是受支持的键类型。

    好的,知道密钥不能是任何类型,但我的类型的数据只是一个int甚至有隐式转换。在这种情况下继承int非常诱人,但我们知道这是不可能的。

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

    次要问题:这是个好主意吗?为什么?如果目前无法提出功能请求,您是否建议?

1 个答案:

答案 0 :(得分:1)

I believe I understand your goal: your aim is to encapsulate the primary key of each model such that primary keys of two different models cannot be compared directly. So, for example, you would want to avoid the comparison Customer.ID == Order.ID at compile time.

However, in your code example, the implicit operator for int <-> DbId<T> works against your goal because this code compiles:

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

So, if I follow your reasoning, it would not work even if EF6+ allowed [Key] on a class (other than string.)

Getting back to basics, if you believe the name ID is too ambiguous, why not follow the Entity Framework Primary Key Convention of class name followed by "ID"?

Example:

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

This naming convention (along with plain ID) is the convention I see the most in Entity Framework code. So, it has the major benefit of allowing other people to step in and seamlessly acclimate and maintain your code (a benefit that we tinkerers are all guilty of overlooking sometimes!)

Looking at your motivations...

Comparing ModelTypeA.ID and ModelTypeB.ID is (at least almost) always an error.

Are you solving a problem that isn't actually a problem? How often do programmers really screw up Order.CustomerID == Customer.CustomerID?

Code will be more self declarative.

Up to debate? If I spot DbId<Customer> id = Customer.ID in someone's code, is it really more declarative than int id = Customer.CustomerID?

That said, I applaud your effort! Solving problems is what we programmers love to do. Good luck!