虚拟代理是如何工作的?

4

我对虚拟代理还不能完全理解,尽管我已经阅读了大量文章并花费了数小时来寻找好的资料,但我仍然没有找到全面的东西。因此,在这里我会提出一个泛泛的要求,希望能得到更好的信息(可以在此处发布或提供链接)。我也会在下面添加一些详细内容,以更好地解释我想要的东西。

我有几个对象,它们之间有许多引用关系。为了简洁起见,我将使用一个具有基本父子关系的对象(Node)。当我将此对象从数据库中取出时,我想实现延迟加载。根据我所阅读的内容,虚拟代理将通过引用接口(INode)并根据需要获取数据成员来处理所有的延迟加载。【注意:我实际上没有INode类,但是当我在我的数据成员上放置虚拟关键字时,代理似乎确实被使用】

当我在我的类中将数据成员设置为虚拟时,似乎会产生一个代理。这是虚拟代理吗?它们是否实现了延迟加载?

我搜索了有关虚拟关键字的信息,但我找到的唯一文档是要在方法上使用它,这是用于继承,以便派生类可以覆盖函数,这与我想要的东西无关(我认为)。

这是我的当前Node.cs

[DataContract(IsReference=true)]
public partial class Node
{
  [DataMember]
  public long ID { get; private set; }
  [DataMember]
  public virtual Node Parent { get; set; }
  [DataMember]
  public virtual ICollection<Node> Children { get; set; }
}

基本上,我现在非常困惑,只需要一些关于这个主题的指导,甚至是一个在线资源,因为我找到的所有资源都没有什么帮助。
提前感谢。
2个回答

5
“虚拟”代理和延迟加载是与ORM工具相关的内容。实际上,代理并不是虚拟的,它是动态的,并遵循GoF定义的真正代理模式。
动态代理是由ORM工具在运行时创建的类(它没有在任何代码文件中定义)。它从您的实体派生,并覆盖导航属性。因此,它们必须是虚拟的才能使代理工作。代理在私有字段或任何更复杂的结构中保存导航属性的状态,如果首次访问该属性,则会看到该状态未加载,并触发从数据库加载并将状态更改为已加载。
无论如何,我不确定这如何涉及WCF,因为最佳实践是不要在WCF中使用延迟加载。为什么?
- 如果在服务器端使用延迟加载,则序列化将始终从数据库拉取整个对象图,因为序列化将访问每个导航属性并触发延迟加载,但然后它将开始序列化延迟加载的实体并访问其所有导航属性等。 - 在客户端使用延迟加载有些模糊不清。首先,在客户端使用延迟加载完全取决于您 - 您必须实现它。当使用服务时,您应始终遵循SOA原则之一:服务边界是显式的。这意味着您对象的用户应始终知道他正在进行远程调用而不是本地调用。分布式计算的主要目标是减少网络往返,因此应尽可能使用急切加载并在单个往返中传输所有所需数据,而不是使用延迟加载。对于从数据库加载,同样适用 - 在有意义时使用延迟加载,因为与数据库的往返可以是昂贵的操作。

  1. 如果我仍然要使用动态代理,那么如何进行序列化/反序列化?我看到了有关DataContractResolver的文章链接,但我从未使其正常工作。
  2. 我尝试通过DbContext.Configuration关闭延迟加载,但我的对象引用仍为空。WCF RIA是否不允许我关闭延迟加载?
- Alec
如果您正在使用WCF RIA服务,则应在问题和标签中提及它,因为它是非常特定的技术。 - Ladislav Mrnka
抱歉,我不是想打RIA :P。我在切换标签时迷失了方向,忘记了自己正在输入什么。我只是想说WCF。唯一让它不懒加载的方法是A.使用显式的.Include,但由于我的模型中有很多关系,这很麻烦;或者B.使用动态代理,但这会失败,因为我无法序列化动态代理以在我的服务中使用。我对所有这些都很新,并且基本上已经碰到了难题。 - Alec
正确的方法是使用.Include(),明确定义要传递给客户端的内容,否则惰性加载+序列化很容易拉取整个数据库。另一种方法是不发送实际的实体,并为传输数据定义特殊的数据传输对象 - 您将需要加载实体并将其转换为数据传输对象。但无论如何,这都回到了最初的开始 - 您知道要传输什么,因此这不是懒加载的场景。 - Ladislav Mrnka

1
我认为您想要一些私有字段来支持您的虚拟属性。在这些虚拟属性的get方法中,您可以检查私有字段,以查看它当前是否有效(是否已经从数据库获取,是否是最新的等),如果无效,则获取或重新获取。我认为这并不比这更复杂。
基础类:
private Node _Parent;
public virtual Node Parent { 
    get { return _Parent; } // Default no lazy fetch.
}

覆盖:

public override Node Parent {
    get {
        if (_Parent==null) // or out of date, dirty etc
            Do_db_get_of_parent();
        return _Parent;
    }
}

我不太明白这怎么解决我的问题,能否请您写一个简短的代码片段? - Alec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接