在自引用表中急切地将Linq加载到SQL实体

时间:2009-08-21 08:20:35

标签: c# linq-to-sql eager-loading self-reference

我有2个相关的Linq to SQL问题。请看下面的图片,看看我的模型是什么样的。

问题1

我正在尝试计算如何在我的User.AddedByUser类/表上加载User字段。此字段是根据User.AddedByUserId字段上的关系生成的。该表是自引用的,我试图找出如何让Linq to SQL急切地加载User.AddedByUser属性,即每当加载/获取任何User实体时,它也必须获取User.AddedByUser和User.ChangedByUser。但是,据我所知,这可能会成为一个递归问题...

更新1.1:

我尝试使用DataLoadOptions,如下所示:

var options = new DataLoadOptions();
options.LoadWith<User>(u => u.ChangedByUser);
options.LoadWith<User>(u => u.AddedByUser);

db = new ModelDataContext(connectionString);
db.LoadOptions = options;

但这不起作用,我在第2行得到以下异常:

System.InvalidOperationException occurred
  Message="Cycles not allowed in LoadOptions LoadWith type graph."
  Source="System.Data.Linq"
  StackTrace:
       at System.Data.Linq.DataLoadOptions.ValidateTypeGraphAcyclic()
       at System.Data.Linq.DataLoadOptions.Preload(MemberInfo association)
       at System.Data.Linq.DataLoadOptions.LoadWith[T](Expression`1 expression)
       at i3t.KpCosting.Service.Library.Repositories.UserRepository..ctor(String connectionString) in C:\Development\KP Costing\Trunk\Code\i3t.KpCosting.Service.Library\Repositories\UserRepository.cs:line 15
  InnerException:

异常是不言自明的 - 对象图不允许是循环的。 另外,假设第2行没有抛出异常,我很确定第3行会,因为它们是重复键。

更新1.2

以下内容也不起作用(不与上面的 Update 1.1 结合使用):

var query = from u in db.Users
            select new User()
            {
                Id = u.Id,
                // other fields removed for brevityy
                AddedByUser = u.AddedByUser,
                ChangedByUser = u.ChangedByUser,

            };
return query.ToList();

它引发了以下不言自明的例外:

System.NotSupportedException occurred
Message="Explicit construction of entity type 'i3t.KpCosting.Shared.Model.User' in query is not allowed."

我现在真的不知道如何解决这个问题。请帮忙!

问题2

在我的数据库中的每个其他表上,因此在Linq to SQL模型中,我有两个字段Entity.ChangedByUser(链接到Entity.ChangedByUserId外键/关系)和Entity.AddedByUser(链接到Entity.AddedByUserId外键/关系)

如何让Linq to SQL为我加载这些字段?我是否需要对查询进行简单的连接?或者还有其他方法吗?

Linq to SQL eager loading on self referencing table http://img245.imageshack.us/img245/5631/linqtosql.jpg

3 个答案:

答案 0 :(得分:4)

Any type of cycles just aren't allowed。由于LoadWith<T>AssociateWith<T>适用于上下文中的每个类型,因此没有内部方法来阻止无限循环。更准确地说,它只是混淆了如何创建SQL,因为SQL Server没有CONNECT BYCTEs真正超过了Linq可以使用提供的框架自动生成的内容。

您可以使用的最佳选项是为两个子项手动执行1级联接到用户表,并使用匿名类型返回它们。对不起,这不是一个干净/简单的解决方案,但到目前为止Linq确实提供了所有这些解决方案。

答案 1 :(得分:3)

也许你可以试着退后一步,看看你想要做什么与关系?我假设你想在例如用户中向用户显示这些信息。 “8小时前由Iain Galloway修改过。”

可能会有以下工作吗? : -

var users = from u in db.Users
            select new
            {
              /* other stuff... */
              AddedTimestamp = u.AddedTimestamp,
              AddedDescription = u.AddedByUser.FullName,
              ChangedTimestamp = u.ChangedTimestamp,
              ChangedDescription = u.ChangedByUser.FullName
            };

我在那里使用匿名类型(imo)清晰度。如果您愿意,可以将这些属性添加到用户类型中。

至于你的第二个问题,你的普通LoadWith(x =&gt; x.AddedByUser)等应该可以正常工作 - 尽管我倾向于直接在数据库中存储描述字符串 - 你需要权衡在ChangedByUser.FullName更改时你的描述更新和必须做一些复杂的事情之间,如果ChangedByUser被删除可能违反直觉(例如ON DELETE CASCADE,或在你的代码中处理null ChangedByUser)。

答案 2 :(得分:0)

不确定Linq to Sql是否存在此问题的解决方案。如果您使用的是Sql Server 2005,则可以定义一个(递归的)Stored Procecdure,它使用公共表表达式来获取您想要的结果,然后使用DataContext.ExecuteQuery执行该结果。