C# 如何在实体框架代码优先方法中映射自身的递归关系

C# 如何在实体框架代码优先方法中映射自身的递归关系,c#,asp.net,asp.net-mvc,entity-framework,asp.net-mvc-4,C#,Asp.net,Asp.net Mvc,Entity Framework,Asp.net Mvc 4,我只想创建基本的递归类别。如果RootCategory\u Id设置为null,则Category为root;如果设置为某个Id,则Category属于其他类别。我在Seed()方法中添加了两个子类别的Category进行测试,但它不起作用。(之后我检查了DB,插入了) 类别模型 我想要达到的目标的图片 这是使用linq到sql的数据库优先方法生成的 延迟加载是打开还是关闭 您可能需要在查询中包含子关系,如下所示 _db.Categories.Include("ChildCategories"

我只想创建基本的递归类别。如果
RootCategory\u Id
设置为null,则Category为root;如果设置为某个Id,则Category属于其他类别。我在
Seed()
方法中添加了两个子类别的Category进行测试,但它不起作用。(之后我检查了DB,插入了)

类别模型 我想要达到的目标的图片 这是使用linq到sql的数据库优先方法生成的

延迟加载是打开还是关闭

您可能需要在查询中包含子关系,如下所示

_db.Categories.Include("ChildCategories").FirstOrDefault(x => x.ID == 4) 

我真的不想在这里用necro线程,但我一直在寻找解决这个确切问题的方法

这是我的解决方案;这有点冗长,但它允许更具可伸缩性的代码优先编程方法。它还引入了允许SoC同时尽可能保持POCO的策略模式

步骤1:创建实体原语和接口。 用户界面:

/// <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; }
}
/// <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; }
}
/// <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
}
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; }
}
/// <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;
    }
}
除了非派生属性之外,我已经从类中剥离了所有内容。
Category
从它对
RecursiveEntity
类的自关联泛型继承中派生出它的所有其他属性

步骤3:扩展方法(可选)。 为了使整个过程更易于管理,我添加了一些扩展方法,可以轻松地向任何父项添加新的子项。我发现,困难在于,你需要设置一对多关系的两端,而仅仅将孩子添加到列表中并不能让你按照预期的方式处理他们。这是一个简单的修复,从长远来看可以节省大量时间

RecursiveEntityEx静态类:

/// <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; }
}
/// <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; }
}
/// <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
}
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; }
}
/// <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;
    }
}
//
///向从RecursiveEntity基类派生的所有实体添加功能。
/// 
公共静态类RecursiveEntityEx
{
/// 
///将新的子实体添加到父实体。
/// 
///要关联的递归实体的类型。
///父母。
///孩子。
///父实体。
公共静态TEntity AddChild(此TEntity父级,TEntity子级)
其中tenty:RecursiveEntity
{
child.Parent=Parent;
if(parent.Children==null)
parent.Children=newhashset();
parent.Children.Add(child);
返回父母;
}
/// 
///将子实体添加到父实体。
/// 
///要关联的递归实体的类型。
///父母。
///孩子们。
///父实体。
公共静态TEntity AddChildren(此TEntity父级,IEnumerable子级)
其中tenty:RecursiveEntity
{
children.ToList().ForEach(c=>parent.AddChild(c));
返回父母;
}
}
一旦你把这些都准备好了,你就可以播种了:

种子方法

/// <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);
}
//
///为指定的上下文设置种子。
/// 
///上下文。
受保护的覆盖无效种子(Test.Infrastructure.TestDataContext上下文)
{
//生成根元素。
var root=新类别{Name=“First Category”};
//向根元素添加一组子元素。
root.AddChildren(新哈希集)
{
新类别{Name=“第二类别”},
新类别{Name=“第三类别”}
});
//向根元素添加一个子元素。
root.AddChild(新类别{Name=“第四类别”});
//将根元素添加到上下文中。子元素也将被保存。
context.Categories.AddOrUpdate(cat=>cat.Name,root);
//运行泛型种子设定方法。
种子(上下文);
}

堆栈溢出上没有线程坏死。如果有什么值得鼓励的,比如这应该是一个答案和信息的存储库,那么新的信息总是受欢迎的。还有一个注意事项,当使用
where tenty:RecursiveEntity
时,它抛出一个编译器错误,说
对我来说没有从tenty到ienty的装箱转换或类型参数转换,它给出了警告/信息,而不是错误。蓝色,而不是黄色或红色的VS。有没有一种更优雅的方式来做到这一点,而不打破无休止的铸件干?