65.9K
CodeProject is changing. Read more.
Home

Using Lazy for properties - Hack and slash attempt

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Aug 19, 2011

CPOL

1 min read

viewsIcon

9940

Getting Lazy to work for me.

Introduction

OK, I love the idea of the Lazy<T> class, but it never seems to do what I want it to. Assuming you don't think I'm wanting too much. If you have a simpler way, please let me know!

Background

I'm using the lazy loading for properties of a data or business entity. Example non-lazy classes:

class User { int id; UserType userType; }  
class UserType { int id; string Name; }

This is what I would want my lazy loading to handle for the above example:

  1. new User() --> UserType should be set to null.
  2. User.PopulateBy(id) --> Gets info for User, including the ID of the UserType. UserType should lazy load from the ID if someone requests that property.
  3. User.UserTypeId = Id --> Same as #2, but emphasizing that the UserType can be changed.
  4. User.UserType = UserType --> Sets the UserType. Does not need to go to the database.
  5. Save() --> Gets the UserTypeId. It should not need to load the UserType from the database.

So Lazy<T> isn't specific enough to do what I want. I tried to implement it, but it got messy. Take a peek.

Code

Here's what my User class looks like:

public class User {
    public int Id { get; set; }
    public string Name { get; set; }

    //assuming: passing the UserType constructor
    //an Id causes it to populate itself.
    private LazyProperty<UserType> _userType = 
      new LazyProperty<UserType>((id) => new UserType(id), (u) => u.Id);
    public int? UserTypeId {
        get { return _userType.Id; }
        set { _userType.Set(value); }
    }
    public UserType UserType {
        get { return _userType.Value; }
        set { _userType.Set(value); }
    }
}

To make the above work, below is the code that I came up with:

using System;
using System.Threading;

public class LazyProperty<T> where T : class {
    public delegate T CreateProperty<T>(int id);
    public delegate int GetId(T item);

    #region members
    int? _id = null;
    bool? _isThreadSafe;
    T _value;
    Lazy<T> _Lazy;
    GetId _GetId;
    CreateProperty<T> _createEntity;
    LazyThreadSafetyMode? _mode;
    #endregion

    #region Properties
    public int? Id { 
        get { return _id; }
    }
    public T Value { 
        get {
            if (_Lazy == null)
                return _value;
            else
                return _Lazy.Value;
        } 
    }
    public bool IsValueCreated {
        get {
            if (_Lazy == null)
                return true;
            else
                return _Lazy.IsValueCreated;
        }
    }
    #endregion
    
    private T doCreate(){
        return _createEntity(Id.Value);
    }

    public LazyProperty(CreateProperty<T> creator, GetId getId) : 
           this(creator, getId, null, null) { }
    public LazyProperty(CreateProperty<T> creator, GetId getId, 
           bool isThreadSafe) : this(creator, getId, isThreadSafe, null) { }
    public LazyProperty(CreateProperty<T> creator, GetId getId, 
           LazyThreadSafetyMode mode) : this(creator, getId, null, mode) { }
    private LazyProperty(CreateProperty<T> creator, GetId getId, 
            bool? isThreadSafe, LazyThreadSafetyMode? mode) {
        _createEntity = creator;
        _isThreadSafe = isThreadSafe;
        _mode = mode;
        _GetId = getId;
        doSet(null, null);
    }
    public void Set(int? id) {
        doSet(id, null);
    }
    public void Set(T value) {
        int? id = null;
        if (value != null)
            id = _GetId(value);
        doSet(id, id == null ? null : value);
    }
    private void doSet(int? id, T value) {
        _id = id;
        _value = value;
        if (id == null || value != null)
            _Lazy = null;
        else if (_mode.HasValue)
            _Lazy = new Lazy<T>(doCreate, _mode.Value);
        else if (_isThreadSafe.HasValue)
            _Lazy = new Lazy<T>(doCreate, _isThreadSafe.Value);
        else
            _Lazy = new Lazy<T>(doCreate);
    }
}

Conclusion

Again, this seems like I needed to do a lot of work to make things work for me. How would you do it better?

History

(Made code slightly cleaner... it's too late on a Friday for this.)