我们可以使用枚举作为类型安全实体ID吗?

Nie*_*oes 17 c# entity-framework entity-framework-6

我们正在使用EF 6.1代码首次设置中的一个相当大的模型,我们正在使用实体ID的int.

不幸的是,这不像我们想要的那样是类型安全的,因为人们可以很容易地混合id,例如比较不同类型的实体(myblog.Id == somePost.Id)或类似的ID.甚至更糟:myBlog.Id ++.

因此,我提出了使用类型ID的想法,因此您无法混淆ID.所以我们需要为我们的博客实体提供BlogId类型.现在,显而易见的选择是使用包装在结构中的int,但不能使用结构作为键.而你无法扩展... ...等等,你可以!使用枚举!

所以我想出了这个:

public enum BlogId : int { } 
public class Blog
{
    public Blog() { Posts = new List<Post>(); }
    public BlogId BlogId { get; set; }
    public string Name { get; set; }
    public virtual List<Post> Posts { get; set; }
}
internal class BlogConfiguration : EntityTypeConfiguration<Blog>
{
    internal BlogConfiguration()
    {
        HasKey(b => b.BlogId);
        Property(b=>b.BlogId)
           .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以现在我们有了类型安全ID - 比较BlogId和PostId是编译时错误.我们不能将3添加到BlogId.空的枚举可能看起来有点奇怪,但这更像是一个实现细节.我们必须在映射中显式设置DatabaseGeneratedOption.Identity选项,但这是一次性的努力.

在我们开始将所有代码转换为此模式之前,是否有任何明显的问题?

编辑:我可能需要澄清为什么我们必须首先使用id而不是完整实体.有时我们需要匹配EF Linq查询中的实体 - 并且比较实体在那里不起作用.例如(基于博客示例并假设更丰富的域模型):在当前用户博客条目上查找评论.请记住,我们希望在数据库中执行此操作(我们有大量数据),并且我们假设没有直接的导航属性.并且当前的用户没有附加.一种天真的方法

from c in ctx.Comments where c.ParentPost.Blog.Author == currentUser 
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为您无法比较EF Linq中的实体.所以我们试试

from c in ctx.Comments where c.ParentPost.Blog.Id == currentUser.Id
Run Code Online (Sandbox Code Playgroud)

这编译并运行但是错误 - 它应该是

from c in ctx.Comments where c.ParentPost.Blog.Author.Id == currentUser.Id
Run Code Online (Sandbox Code Playgroud)

Typesafe id会抓住它.我们有比这更复杂的查询.尝试"查找当前用户博客条目的评论,这些博客条目是由当前用户以后没有自己评论过的特定其他用户".

此致,尼尔斯

L-F*_*our 3

这是一种有趣的方法,但问题是:值得吗?会产生什么后果?

你仍然可以做类似的事情

 if ((int)blog.BlogId == (int)comment.CommentId) { }
Run Code Online (Sandbox Code Playgroud)

就我个人而言,我会投入更多时间来教育人们、编写良好的测试和代码审查,而不是尝试添加某种形式的额外复杂性来影响您使用和查询实体的方式。

想想——例如:

  • 这种转换对 LINQ 查询的性能有什么影响?
  • 如果您通过 WCF 的 Web API 公开您的操作,您将如何强制执行此操作?
  • 这与导航属性一起工作吗?
  • 此外,您可以用作主键的类型也受到限制;我不相信这对指南有用。

额外保护的一种方法是让域层通过接受实体实例而不是 ID 来处理此类事情。