开发者

How can I use LazyLoading with WCF without causing circular references?

I am doing validation using DataAnnotation attributes on the Model classes, and the Model class is used for validation on both the Client and Server side of the application.

My problem is, I can't figure out how to lazy load my Model's properties without causing circular references

The libraries involved are:

  • WCF Service Library
  • Client-Side DataAccess Library
  • Models Library

Because the Models library is used on both the Client and Server side for data validation, I cannot reference the DataAccess library from within the Models library. Therefore, how can I setup lazy-loading?

For example, I have a ConsumerModel which has a property of PhoneNumbers which should be lazy loaded. How can I load the PhoneNumberModels from within the ConsumerModel without referencing the Client-Side DAL?

Client-side DAL:

using MyModels;

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

ConsumerModel:

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
            }
        }
    }
}

What are some alternative ways I could make this architecture work?

I would prefer to keep Validation with the Models, and to use the models from both the Client and Server side for validation. I would also prefer to keep using DataAnnotation for Validation.

EDIT

Here's my final solution based on Lawrence Wenham's answer if anyone is interested. I ended up using a delegate instead of an event.

DAL:

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.GetByC开发者_开发百科onsumerId((int)args);
        default:
            return null;
    }
}

Model Library:

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;
        }
    }
}


One way might be to raise an event in the get {} of your Model classes properties, and then implement a lazy-loading manager on the client side that has a reference to your DAL. EG:

public class LazyLoadEventArgs: EventArgs
{
    public object Data { get; set; }

    public string PropertyName { get; set; }

    public int Key { get; set; }
}

Then in your Model classes:

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;
    }
}

The handler for the LazyLoadData event would have the job of fetching the data from the client side's DAL, then storing it in the .Data property of LazyLoadEventArgs. EG:

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


Do not use "lazy loading" with WCF. Network communication is time expensive. If you plan to use PhoneNumbers your service should expose method which will return Customer with phone numbers. Other approach is using WCF Data Services which offers client side linq queries with ability to define eager loading by Expand method.

You should reduce service calls to minimum.

After reading again your question I don't understand why do you share model between service and client. Model is strictly client's feature. The only shared part should be DTOs.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜