EF Code First:如何将虚拟集合设为私有,同时仍使其正确创建我的数据库模型?

发布于 2024-10-17 10:53:07 字数 1894 浏览 2 评论 0原文

我使用 Code First 自动生成数据库,效果非常好,当我添加一些测试数据时,会按预期生成一个 Orders 表和一个 OrderLines 表。

我有以下 Order 类:

public class Order
{
    public int OrderID { get; set; }

    public void AddItem(string productCode, int quantity)
    {
        var existingLine = OrderLines.FirstOrDefault(x => x.ProductOption.ProductCode == item.ProductCode);

        if (existingLine == null)
            OrderLines.Add(new OrderLine { ProductOption = item, Quantity = quantity });
        else
            existingLine.Quantity += quantity;
    }

    public void RemoveItem(string productCode)
    {
        OrderLines.Remove(OrderLines.Where(x => x.ProductOption.ProductCode == productCode).FirstOrDefault());
    }

    public virtual ICollection<OrderLine> OrderLines { get; set; }

    public Order()
    {
        OrderLines = new List<OrderLine>();
    }
}

我真正想要的是封装 OrderLines 集合,使该类的使用者无法直接向其中添加或删除项目(使用 ICollection 的 Add / Remove 方法),并强制它们使用我的自定义 AddItemRemoveItem 方法。

通常我可以将集合设为私有,但我不能这样做,因为 EF 需要虚拟它才能正确创建 OrderLines 表/外键。

这个答案 似乎建议将属性设置为 internal 就可以解决问题,但我尝试了,在这种情况下,不会创建 OrderLines 表。

有什么方法可以实现这一点,或者我应该以某种方式进行不同的设计吗?非常感谢任何帮助!

更新

经过更多搜索,我发现这个问题比我的表述更清楚;然而,这仍然没有答案。海报确实链接到此帖子似乎表明它不能真正按照我想的方式完成,但是有人有更多最新信息吗?

I am using Code First to automatically generate my database, and this works perfectly, generating an Orders table and an OrderLines table as expected when I add some test data.

I have the following Order class:

public class Order
{
    public int OrderID { get; set; }

    public void AddItem(string productCode, int quantity)
    {
        var existingLine = OrderLines.FirstOrDefault(x => x.ProductOption.ProductCode == item.ProductCode);

        if (existingLine == null)
            OrderLines.Add(new OrderLine { ProductOption = item, Quantity = quantity });
        else
            existingLine.Quantity += quantity;
    }

    public void RemoveItem(string productCode)
    {
        OrderLines.Remove(OrderLines.Where(x => x.ProductOption.ProductCode == productCode).FirstOrDefault());
    }

    public virtual ICollection<OrderLine> OrderLines { get; set; }

    public Order()
    {
        OrderLines = new List<OrderLine>();
    }
}

What I really want is to encapsulate the OrderLines collection, making it impossible for consumers of the class to directly add and remove items to/from it (using the Add / Remove methods of ICollection) and instead forcing them to use my custom AddItem and RemoveItem methods.

Normally I could just make the collection private, but I can't do that because it needs to be virtual for EF to correctly create the OrderLines table/foreign keys.

This answer seems to suggest that making the property internal would do the trick, but I tried, and in that case no OrderLines table is created.

Is there any way that this can be accomplished, or should I have designed this differently somehow? Any help much appreciated!

Update

After a bit more searching, I found this question which is rather more clearly stated than mine; however, it's still unanswered. The poster does link to this post which seems to suggest it can't really be done in the way I'm thinking of, but does anyone have any more up-to-date information?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

落墨 2024-10-24 10:53:07

我不知道是否可以按照您的要求进行操作,但我不确定这是最好的设计。我看到的问题是你正在将你的业务逻辑牢固地集成到你的业务实体中,我认为这会在未来变得混乱。

考虑以下场景。假设您有一个新要求,希望用户能够从订单中删除所有商品。对您的实体执行此操作的唯一方法是为您的 Order 类创建一个新的 RemoveAllItems() 方法来执行此操作。现在假设您有一个新要求,即从订单中删除特定类别中的所有商品。这意味着您必须添加另一个方法。

这会导致类变得非常臃肿,并且您会遇到一个主要问题。如果您(或其他开发人员)想要查看一个实体并确定它的数据结构,您无法一目了然,因为它与业务逻辑如此紧密地交织在一起。

我建议您将实体保留为纯数据结构,并公开它们的所有关系。然后,您需要创建一个服务层,它可以由实际执行业务功能的小型或大型类(无论您如何组织它们)组成。例如,您可以有一个 OrderItemService 类,它具有用于添加、编辑和删除订单中的项目的方法。您的所有业务逻辑都在此类中执行,您只需强制只允许服务类与数据库实体交互。

现在,如果您正在寻找特定业务流程的执行方式,您知道要查看服务层类,如果您想查看数据结构或实体的组织方式,您会查看实体。这使一切保持干净且易于维护。

I don't know if it's possible to do what you are asking or not, but I'm not sure it's the best design. The problem that I am seeing is you are firmly integrating your business logic into your business entities, and I think this will turn into confusion down the road.

Take the following scenario under consideration. Say you have a new requirement where you want users to be able to remove all items from an order. The only way to do it with your entity is to create a new RemoveAllItems() method to your Order class which does that. Now say you have a new requirement to Remove all items from an order that are in a specific category. That then means that you have to add yet another method.

This causes really bloated classes, and there is one major issue you will come up with. If you (or another developer) want to look at an entity and determine it's data structure, you can't at a glance because it's so intertwined with business logic.

What I would suggest is that you keep your entities as pure data structures, keeping all their relationships public. Then you need to create a service layer, which can consist of small or big classes (however you want to organize them) that actually perform the business functions. So for example, you can have a OrderItemService class, which has methods for adding, editing, and removing items from an order. All your business logic is performed in this class, and you just have to enforce that only service classes are allowed to interact with db entities.

Now, if you are looking for how a particular business process is performed, you know to look in the service layer classes, and if you want to look at how a data structure or entity is organized, you look at the entity. This keeps everything clean and very mantainable.

猫烠⑼条掵仅有一顆心 2024-10-24 10:53:07

我远不是代码第一方面的专家,我还没有尝试过以下方法,但是否可以使用 ReadOnlyCollectionBase 并创建类似于 这篇 MSDN 文章?

I'm far from an expert on code first and I haven't tried the following but is it possible to use the ReadOnlyCollectionBase and create a read only list similar to this MSDN article?

旧瑾黎汐 2024-10-24 10:53:07

那么你可以做的就是将你的集合设置为私有,并在 OnModelCreating 中使用 Fluent API 建立关系,如下所示,我不知道这是否有效,只需尝试一下:

public class YourContext : DbContext
{
   public DbSet<Order> Orders { get; set; }
   public DbSet<OrderLine> OrderLines { get; set; }

   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
       modelBuilder.Entity<Order>() 
           .HasMany(o => o.OrderLines) 
           .WithRequired(l => l.OrderId) 
           .HasForeignKey(l => l.OrderId);

   }
}

这将使你的 OrderLines 为只读:

public class YourContext : DbContext
{
   public DbSet<Order> Orders { get; set; }

   public DbSet<OrderLine> OrderLines 
   { 
      get { return set<OrderLine>(); }
   }
}

I希望这可以帮助您,请查看此博客文章:EF 功能 CTP5:流畅的 API 示例

Well what you can do is set your collection as private and make the relationship using fluent API in the OnModelCreating, as shown below, I don't know if this will work, just make a try:

public class YourContext : DbContext
{
   public DbSet<Order> Orders { get; set; }
   public DbSet<OrderLine> OrderLines { get; set; }

   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
       modelBuilder.Entity<Order>() 
           .HasMany(o => o.OrderLines) 
           .WithRequired(l => l.OrderId) 
           .HasForeignKey(l => l.OrderId);

   }
}

This will make your OrderLines as readonly:

public class YourContext : DbContext
{
   public DbSet<Order> Orders { get; set; }

   public DbSet<OrderLine> OrderLines 
   { 
      get { return set<OrderLine>(); }
   }
}

I hope this can help you, please take a look a this blog post: EF Feature CTP5: Fluent API Samples

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文