【问题标题】:How can I use LazyLoading with WCF without causing circular references?如何在不导致循环引用的情况下将 LazyLoading 与 WCF 一起使用?
【发布时间】:2011-03-23 00:33:14
【问题描述】:

我正在使用 Model 类上的 DataAnnotation 属性进行验证,并且 Model 类用于在应用程序的客户端和服务器端进行验证。

我的问题是,我不知道如何在不引起循环引用的情况下延迟加载模型的属性

涉及的库有:

  • WCF 服务库
  • 客户端数据访问库
  • 模型库

因为模型库在客户端和服务器端都用于数据验证,所以我无法从模型库中引用 DataAccess 库。那么,如何设置延迟加载呢?

例如,我有一个 ConsumerModel,它有一个 PhoneNumbers 属性,应该延迟加载。如何在不引用客户端 DAL 的情况下从 ConsumerModel 中加载 PhoneNumberModel?

客户端 DAL:

using MyModels;

public class ConsumerDataAccess
{
    public ConsumerModel GetConsumerById(int id)
    {
        ConsumerDTO dto = WCFService.GetConsumer(id);
        return new ConsumerModel(dto);
    }
}

消费者模型:

public class ConsumerModel
{
    public ObservableCollection<PhoneNumberModel> _phoneNumbers;

    public ObservableCollection<PhoneNumberModel> PhoneNumbers
    {
        get
        {
            if (_phoneNumbers == null)
            {
                // Can't reference DataAccess Library since that would cause a Circular Reference
            }
        }
    }
}

我可以通过哪些替代方法使这种架构发挥作用?

我更愿意使用模型进行验证,并使用客户端和服务器端的模型进行验证。我还希望继续使用 DataAnnotation 进行验证。

编辑

如果有人感兴趣,这是我根据 Lawrence Wenham 的回答得出的最终解决方案。我最终使用了委托而不是事件。

达尔:

public class ConsumerDataAccess
{
    public ConsumerModel GetConsumerById(int id)
    {
        ConsumerDTO dto = WCFService.GetConsumer(id);
        ConsumerModel rtnValue = new ConsumerModel(dto);
        ConsumerModel.LazyLoadData = LazyLoadConsumerData;
        return rtnValue;
    }
}

private object LazyLoadConsumerData(string key, object args)
{
    switch (key)
    {
        case "Phones":
            return PhoneDataAccess.GetByConsumerId((int)args);
        default:
            return null;
    }
}

模型库:

public class ConsumerModel
{
    public delegate object LazyLoadDataDelegate(string id, object args);
    public LazyLoadDataDelegate LazyLoadData { get; set; }

    public ObservableCollection<PhoneNumberModel> _phoneNumbers;

    public ObservableCollection<PhoneNumberModel> PhoneNumbers
    {
        get
        {
            if (_phoneNumbers == null && LazyLoadData != null)
            {
                _phoneNumbers = (ObservableCollection<PhoneNumberModel>)
                        LazyLoadData("Phones", ConsumerId);
            }
            return _phoneNumbers;
        }
    }
}

【问题讨论】:

    标签: c# wcf architecture mvvm lazy-loading


    【解决方案1】:

    一种方法可能是在您的模型类属性的get {} 中引发一个事件,然后在客户端实现一个延迟加载管理器,该管理器引用您的 DAL。例如:

    public class LazyLoadEventArgs: EventArgs
    {
        public object Data { get; set; }
    
        public string PropertyName { get; set; }
    
        public int Key { get; set; }
    }
    

    然后在您的模型类中:

    public event EventHandler<LazyLoadEventArgs> LazyLoadData;
    
    public ObservableCollection<PhoneNumberModel> PhoneNumbers
    {
        get
        {
            if (_phoneNumbers == null)
            {
                LazyLoadEventArgs args = new LazyLoadEventArgs {
                    PropertyName = "PhoneNumbers",
                    Key = this.Id
                };
                LazyLoadData(this, args);
                if (args.Data != null)
                   this._phoneNumbers = args.Data as ObservableCollection<PhoneNumberModel>;
            }
            return _phoneNumbers;
        }
    }
    

    LazyLoadData 事件的处理程序负责从客户端的 DAL 获取数据,然后将其存储在 LazyLoadEventArgs 的 .Data 属性中。例如:

    private void Model_HandleLazyLoadData(object sender, LazyLoadEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "PhoneNumbers":
                e.Data = DAL.LoadPhoneNumbers(e.Key);
                break;
            ...
        }
    }
    

    【讨论】:

    • 我不确定我是否理解...实际上是什么调用 DAL 来加载 PhoneNumber 数据?
    • 查看编辑后的答案。 LazyLoadData 的事件处理程序称为 DAL。您需要找出识别要加载哪些数据的最佳方法;在我的示例中,我只使用了字符串和某种“键”,但您可以使用枚举,将其与 if (sender is ConsumerModel) 等内容结合使用。它会将 DAL 排除在模型之外。
    • 那不会还是给我一个循环引用的问题吗?模型需要了解 Observers 库,Observers 需要了解 Models 库。我最初的问题是我无法从模型库中引用 DAL,所以我无法将两者结合起来。
    • 不,不应该,因为您将事件处理程序放在模型库之外。每当您从服务中获取对象时,您都会先将处理程序附加到该对象上,然后再对它进行任何操作。
    【解决方案2】:

    不要对 WCF 使用“延迟加载”。网络通信很费时间。如果您打算使用PhoneNumbers,您的服务应该公开方法,该方法将返回带有电话号码的Customer。其他方法是使用WCF Data Services,它提供客户端linq 查询,能够通过Expand 方法定义预加载。

    您应该将服务调用降至最低。

    再次阅读您的问题后,我不明白您为什么要在服务和客户端之间共享模型。模型是严格的客户特征。唯一共享的部分应该是 DTO。

    【讨论】:

    • PhoneNumbers 就是一个简单的例子。一个更好的例子可能是文档,因为在大多数情况下,文档都是大文件,我不想在每种情况下都检索它们。用户可以自定义他们的 UI,因此屏幕上有文档部分的用户需要加载文档,而其他用户则不需要。
    • 听起来像是不同的视图,它们有不同的模型和不同的方法来检索数据。
    • 每个数据部分都有不同的视图。用户可以根据需要使用的信息将面板拖放到屏幕上。在几乎所有情况下,面板都包含与消费者有关的信息。当他们加载消费者时,我只需要根据他们屏幕上的面板加载位于他们屏幕上的信息。
    • 在这种情况下使用@Lawrence 提供的基于事件的模型(或观察者模式的任何实现)。
    • 我看到了你的更新。我想在应用程序的客户端和服务器端都使用模型验证。
    猜你喜欢
    • 2011-11-20
    • 2016-09-15
    • 2018-07-13
    • 2021-01-12
    • 2017-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多