![](/img/trans.png)
[英]How to MAP select stored procedure in Entity Framework 6 code-first approach?
[英]How to map recursive relation on self in Entity Framework code-first approach
我想要创建的只是基本的递归类别。 如果RootCategory_Id
设置为null,则类别为root,如果将其设置为某个id,则属于某个其他类别。 我在Seed()
方法中添加了两个子类别的类别进行测试,但它不起作用。 (之后我检查了数据库,插入了)
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-to-sql从数据库优先方法生成的
我真的不想在这里使用necro-thread,但我一直在寻找解决这个问题的方法。
这是我的解决方案; 它有点冗长,但它允许Code First编程采用更加可扩展的方法。 它还引入了策略模式以允许SoC尽可能保持为POCO。
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<TEntity>
作为约束,而不是IEntity
因此它被约束为仅处理递归实体。 这有助于缓解类型不匹配异常。 如果您改为使用IEntity
,则需要添加一些异常处理来对抗不匹配的基类型。
使用IEntity
将提供完全有效的代码,但在任何情况下它都无法正常工作。 使用最可用的根,并不总是最佳实践,在这种情况下,我们需要进一步限制继承树而不是在根级别。 我希望这是有道理的。 这是我最初玩的东西,但在填充数据库时我遇到了很多问题; 特别是在实体框架迁移期间,您没有精细的调试控件。
在测试期间,它似乎也不适合IRecursiveEntity<TEntity>
。 我可能会很快回到这里,因为我正在刷新一个使用它的旧项目,但它在这里写的方式是完全有效和有效的,我记得调整它直到它按预期工作。 我认为在代码优雅和继承heirarchy之间存在折衷,其中使用更高级别的类意味着在IEntity
和IRecursiveEntity<IEntity>
之间IEntity
大量属性,这反过来又提供了较低的性能并且看起来很丑陋。
我用过原来问题的例子......
类别混凝土类:
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; }
}
除了非衍生属性之外,我已经从课堂上删除了所有内容。 Category
通过RecursiveEntity
类的自关联泛型继承派生其所有其他属性。
为了使整个事物更容易管理,我添加了一些扩展方法,以便轻松地将新子项添加到任何父项。 我发现,困难在于你需要设置一对多关系的两端,只是将子项添加到列表中就不允许你按照预期的方式处理它们。 这是一个简单的修复,从长远来看可以节省大量时间。
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;
}
}
一旦你掌握了所有这些,你就可以如此种下它:
种子方法
/// <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);
}
懒惰加载打开或关闭?,
您可能需要在查询中包含子关系,如此
_db.Categories.Include("ChildCategories").FirstOrDefault(x => x.ID == 4)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.