如何映射到实体框架code-第一的方针自我递推关系 [英] How to map recursive relation on self in Entity Framework code-first approach

查看:83
本文介绍了如何映射到实体框架code-第一的方针自我递推关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要的创建是递归的基本范畴。分类是根,如果 RootCategory_Id 设置为null,并且它属于一些其他类别,如果它设置为某个ID。我在添加类具有两个子类别种子()的方法来测试,它无法正常工作。 (我检查DB之后,还有被插入)

All I want to create is basic recursive category. Category is root if RootCategory_Id is set to null and it belongs to some other category if it is set to some id. I've added category with two child-categories in Seed() method to test and it does not work. (I've checked DB afterwards, there are inserted)

public class Category
{
    public int ID { get; set; }
    public Category RootCategory { get; set; } // This one works good, it also creates "RootCategory_Id" in database on "update-database"

    public ICollection<Category> ChildCategories { get; set; } // This is always null, how to map it correctly?

    public string Name { get; set; }
}

播种方法

protected override void Seed(Test.Infrastructure.TestDataContext context)
{
    context.Categories.Add(new Category() {
        Name = "First Category", ChildCategories = new List<Category>() {
            new Category(){
                Name = "Second Category"
            },
            new Category(){
                Name = "Third Category"
            }
        }
    });

    context.SaveChanges();
}

这是我的测试,它无法正常工作

public ActionResult Test()
{
    // After checking DB my root is 4, and two categories that have RootCategory_Id set to 4
    var c = _db.Categories.Where(x => x.ID == 4).Single();
    return Content(c.ChildCategories.FirstOrDefault().Name); // Always returns null, even c.ChildCategories.Count() returns 'null'
}

什么,我想实现图片

这是从生成的数据库,第一种方法使用LINQ到SQL


Picture of what I want to achieve

this was generated from database-first approach using linq-to-sql

推荐答案

我真的不希望在这里死灵线程,但我一直在寻找如何解决这个确切的问题的方法。

I didn't really want to necro-thread here, but I've been searching for methods on how to solve this exact problem.

下面是我的解决方案;这是一个有点长篇大论,但它允许一个更可扩展的方法,以code首先编程。它还引入了策略模式,允许SoC的,而其余的POCO越好。

Here's my solution; it's a bit long winded, but it allows a much more scalable approach to Code First programming. It also introduces the Strategy pattern to allow for SoC while remaining as POCO as possible.

IEntity接口:

/// <summary>
///     Represents an entity used with Entity Framework Code First.
/// </summary>
public interface IEntity
{
    /// <summary>
    ///     Gets or sets the identifier.
    /// </summary>
    /// <value>
    ///     The identifier.
    /// </value>
    int Id { get; set; }
}

IRecursiveEntity接口:

/// <summary>
///     Represents a recursively hierarchical Entity.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public interface IRecursiveEntity <TEntity> where TEntity : IEntity
{
    /// <summary>
    ///     Gets or sets the parent item.
    /// </summary>
    /// <value>
    ///     The parent item.
    /// </value>
    TEntity Parent { get; set; }

    /// <summary>
    ///     Gets or sets the child items.
    /// </summary>
    /// <value>
    ///     The child items.
    /// </value>
    ICollection<TEntity> Children { get; set; }
}

实体抽象类:

/// <summary>
///     Acts as a base class for all entities used with Entity Framework Code First.
/// </summary>
public abstract class Entity : IEntity
{
    /// <summary>
    ///     Gets or sets the identifier.
    /// </summary>
    /// <value>
    ///     The identifier.
    /// </value>
    public int Id { get; set; }
}

RecursiveEntity抽象类:

/// <summary>
///     Acts as a base class for all recursively hierarchical entities.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
public abstract class RecursiveEntity<TEntity> : Entity, IRecursiveEntity<TEntity> 
    where TEntity : RecursiveEntity<TEntity>
{
    #region Implementation of IRecursive<TEntity>

    /// <summary>
    ///     Gets or sets the parent item.
    /// </summary>
    /// <value>
    ///     The parent item.
    /// </value>
    public virtual TEntity Parent { get; set; }

    /// <summary>
    ///     Gets or sets the child items.
    /// </summary>
    /// <value>
    ///     The child items.
    /// </value>
    public virtual ICollection<TEntity> Children { get; set; }

    #endregion
}

注意:有人建议编辑这个职位对于这个类。这个班只接受 RecursiveEntity&LT; TEntity&GT; 作为约束,而不是 IEntity ,以便它被限制在仅处理递归实体。这有助于缓解类型不匹配的异常。如果你使用 IEntity 相反,你将需要添加一些异常处理来对抗错配碱基的类型。

Note: Some people have suggested edits to this post regarding this class. The class needs to only accept RecursiveEntity<TEntity> as a constraint, rather than IEntity so that it is constrained to only handle recursive entities. This helps alleviate type mismatch exceptions. If you use IEntity instead, you will need to add some exception handling to counter mismatched base types.

使用 IEntity 将给予完全有效的code,但在任何情况下如预期将无法正常工作。用最上面的根可用,并非总是最好的做法,而在这种情况下,我们需要进一步限制向下的传承树比该根级别。我希望是有道理的。这是我与第一次打左右,但我不得不填充数据库时巨大的问题;尤其是在实体框架的迁移,那就是你有没有细粒度控制调试

Using IEntity will give perfectly valid code, but it will not work as expected under all circumstances. Using the topmost root available, isn't always the best practice, and in this case, we need to constrain further down the inheritence tree than at that root level. I hope that makes sense. It is something I played around with at first, but I had huge problems when populating the database; especially during Entity Framework Migrations, where you have no fine grained debugging control.

在测试过程中,它也似乎并没有玩 IRecursiveEntity&LT美观大方; TEntity&GT; 无论是。我可能很快就会回到这一点,因为我刷新使用它的一个旧的项目,但它写在这里的方式是完全有效和工作,我记得调整它,直到它的工作如预期。我觉得有一个折衷code优雅和继承层次结构,其中使用意味着更高的水平类铸件很多的属性 IEntity IRecursiveEntity&LT; IEntity&GT; ,这反过来,给了一个较低的性能,看着难看。

During testing, it also didn't seem to play nice with IRecursiveEntity<TEntity> either. I may return to this soon, as I'm refreshing an old project that uses it, but the way it is written here is fully valid and working, and I remember tweaking it until it worked as expected. I think there was a trade off between code elegance and inheritance heirarchy, where using a higher level class meant casting a lot of properties between IEntity and IRecursiveEntity<IEntity>, which in turn, gave a lower performance and looked ugly.

我已经从原来的问题中使用的示例...

I've used the example from the original question...

类别具体类:

public class Category : RecursiveEntity<Category>
{
    /// <summary>
    ///     Gets or sets the name of the category.
    /// </summary>
    /// <value>
    ///     The name of the category.
    /// </value>
    public string Name { get; set; }
}

我已经从非派生属性剥离出来一切从类分开。在类别派生从自身相关的通用继承其所有其他属性 RecursiveEntity 类。

I've stripped out everything from the class apart from non-derived properties. The Category derives all its other properties from its self-associated generic inheritance of the RecursiveEntity class.

为使整个事情更易于管理,我增加了一些扩展方法,轻松地新的儿童添加到任何父项。困难,我发现,是你需要设置一个一对多关系的两端,只是增加孩子列表中并没有允许你来处理他们,他们的目的的方式。这是一个简单的修复,节省从长远来看,一个巨大的时间。

To make the whole thing more managable, I've added some extension methods to easily add new children to any parent item. The difficulty, I found, was that you need to set both ends of the one-to-many relationship and merely adding the child to the list didn't allow you to handle them in the way they are intended. It's a simple fix that saves a huge amount of time in the long run.

RecursiveEntityEx静态类:

/// <summary>
///     Adds functionality to all entities derived from the RecursiveEntity base class.
/// </summary>
public static class RecursiveEntityEx
{
    /// <summary>
    ///     Adds a new child Entity to a parent Entity.
    /// </summary>
    /// <typeparam name="TEntity">The type of recursive entity to associate with.</typeparam>
    /// <param name="parent">The parent.</param>
    /// <param name="child">The child.</param>
    /// <returns>The parent Entity.</returns>
    public static TEntity AddChild<TEntity>(this TEntity parent, TEntity child)
        where TEntity : RecursiveEntity<TEntity>
    {
        child.Parent = parent;
        if (parent.Children == null)
            parent.Children = new HashSet<TEntity>();
        parent.Children.Add(child);
        return parent;
    }

    /// <summary>
    ///     Adds child Entities to a parent Entity.
    /// </summary>
    /// <typeparam name="TEntity">The type of recursive entity to associate with.</typeparam>
    /// <param name="parent">The parent.</param>
    /// <param name="children">The children.</param>
    /// <returns>The parent Entity.</returns>
    public static TEntity AddChildren<TEntity>(this TEntity parent, IEnumerable<TEntity> children)
        where TEntity : RecursiveEntity<TEntity>
    {
        children.ToList().ForEach(c => parent.AddChild(c));
        return parent;
    }
}

一旦你拥有了这一切就绪后,就可以播种正是如此吧:

Once you have all of that in place, you can seed it thusly:

种子法

/// <summary>
///     Seeds the specified context.
/// </summary>
/// <param name="context">The context.</param>
protected override void Seed(Test.Infrastructure.TestDataContext context)
{
    // Generate the root element.
    var root = new Category { Name = "First Category" };

    // Add a set of children to the root element.
    root.AddChildren(new HashSet<Category>
    {
        new Category { Name = "Second Category" },
        new Category { Name = "Third Category" }
    });

    // Add a single child to the root element.
    root.AddChild(new Category { Name = "Fourth Category" });

    // Add the root element to the context. Child elements will be saved as well.
    context.Categories.AddOrUpdate(cat => cat.Name, root);

    // Run the generic seeding method.
    base.Seed(context);
}

这篇关于如何映射到实体框架code-第一的方针自我递推关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆