Lazy Loading of Collection - how to get the items?

别来无恙 提交于 2019-12-04 05:02:19

Your DTO doesn't need to know about the repository itself. All it needs is a delegate that can provide it with the value for notes.

How about something like this:

public class MyDTOClass
{
    private ICollection<Note> _notes = null;

    public ICollection<Note> Notes
    {
        get
        {
            if (_notes == null)
            {
                if (notesValueProvider == null)
                    throw new InvalidOperationException("ValueProvider for notes is invalid");
                _notes = notesValueProvider();
            }
            return _notes;
        }
    }

    private Func<ICollection<Note>> notesValueProvider = null;

    public MyDTOClass(Func<ICollection<Note>> valueProvider)
    {
        notesValueProvider = valueProvider;
    }
}

Since by definition, your repository is supposed to provide you with an instance of the DTO, we should be able to pass in the value provider delegate like so:

public class Repository
{
    public MyDTOClass GetData()
    {
        MyDTOClass dto = new MyDTOClass(FetchNotes);
        return dto;
    }

    public ICollection<Note> FetchNotes()
    {
        return new List<Note>(200);
    }
}

Will this work for you?

If you can wait for .Net 4 (ie your not yet in production) Lazy(of T) is a new Lazy Loading feature in the .Net framework. http://msdn.microsoft.com/en-us/library/dd642331(VS.100).aspx

You're defeating the whole purpose of DTOs when you try to add lazy-loading logic to it. I think, you should have two separate objects: one with Notes, and another -- without them.

Another option is to use proxies that inherit from the objects you want to retrieve (following the lead of some object-relational mappers, like NHibernate).

This provides a degree of persistence ignorance by keeping the data access code separate from the domain model:

public class MyLazyDTOClass: MyDTOClass {   

    // Injected into the constructor by the MyDtoClass repository
    private INoteRepository noteRepository;        

    public ICollection<Note> Notes {
        get {
            if(base.Notes == null) {
                base.Notes = noteRepository.GetNotes(projectId);
            }
            return base.Notes;
        }
    }
}

MyDTOClassRepository declares the base object as its return type but returns the lazy object instead:

public MyDTOClassRepository {
    public MyDTOClass GetMyDTOClass(int id) {
        // ... Execute data access code to obtain state ...
        return new MyLazyDTOClass(this, state);
    }
}

MyDTOClass consumers don't need to know they're dealing with proxies, nor do they have to interact with repositories (except for whatever class makes the initial call of course).

After having roamed the astral realms for aeons in desperate search for an answer, I have come to the final conclusion that yes, it's unnecessary to pass a repository into your entity instance, because a repository for an entity type should always be a singleton.

So you could write in your entity class simply something like:

public class Monster
{
    public ReadOnlyCollection<Victim> _victims;
    public ReadOnlyCollection<Victim> Victims
    {
        get
        {
            if (this._victims == null) // Note: Thread-Safety left out for brevity
            {
                this._victims = VictimRepository.Instance.GetVictimsForMonster(this);
            }

            return this._victims;
        }
    }
}

This really solved all headaches for me.

The repository must be implemented in a way that it would always know what to do with the data.

Remember that one repository implementation would for example get the data from the database, while another might get it from a web service. Because of the loose coupling, the repository implementation module is easily replaceable and any data communication is even arbitrarily chainable.

If you have a scenario where you would say "but the repository can't be singleton because I have a complex scenario where I for example access more than one data source and it depends on the actual Monster instance where to get the Victims from", then I say well you must create a repository implementation which knows all data sources and keeps track of where the entity instances come from and where they will go, and so on...

If you feel this approach is not POCO enough, then you must create another loosely coupled Business Logic Layer which wraps or derives from the POCO entity and implements the repository interaction there.

I hope I could give a hint into a direction that feels right for you and anyone. I actually do believe this is the holy grail for multilayer/-tier development. Feel free to further discuss.

You can't achieve independence from your repository if you're lazy loading from it. You can keep it at arm's length by using a callback or proxy or letting NHibernate do the dirty work for you, but your DTO has to access the repository to load the Notes.

Your main goal appears to be "I want it to appear like a POCO to outside caller so I like to have a Property "Notes" rather than methods like "GetNotesForProject" on this or other classes." Can't you accomplish this with ninject and constructor injection? Once you've set up ninject you can call kernel.Get() to get a new instance that will not expose any reference to your repository.

You could have the Notes property take in an INoteRepository as a parameter. That way the calling code could pass in the correct instance of INoteRepository.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!