Designing an object with a long running constructor

I have a class that is designed to quickly access some metadata for some specific files that are used multiple times in my application. Unfortunately, some metadata can only be retrieved with a very long method call.

I have another class that provides an async wrapper for a long running method (maybe 5 or more minutes depending on the file size), but I am trying to figure out how to call this async method and if it would be wise to put it in the constructor , or if there is a better design pattern for this scenario.

Here is some pseudo code to try and illustrate my question:

public class MetaData
{
    public string Data1 { get; private set; }
    public string Data2 { get; private set; }

    public MetaData(String filepath)
    {
        var extractor = new ExtractMetaData(filepath);  //create instance of class that fetches the metadata

        this.Data1 = extractor.GetData1(); // short running method

        extractor.Data2Received += Data2Received;  
        extractor.GetData2Async();  // long running method, called with via async method

    }        

    private void Data2Received(object sender, MetaDataEventArgs args)
    {
        this.Data2 = args.Data;  // finally set Data2 property
    }
}

class ExtractMetaData
{

    public event Data2ReceivedEventHandler Data2Received;

    public ExtractMetaData (string filePath) { }

    public String GetData1();  // very fast method to get Data1
    public void GetData2Async();  // very slow method to get Data2

}

      

What I am trying to figure out is there is a better way to do this?

There is no wait in my code right now to create MetaData

, but if someone tries to access the property MetaData.Data2

before the method GetData2Async()

returns and fires the event Data2Received

, they are going to get a response null

. But if they call, if after it is returned, it will contain the correct information. Since there is really no way to notify the user that this method has completed, I am worried that this will turn into a bad UI as they don't want to wait for the constructor, but must wait for all the properties to be set.

+3


source to share


3 answers


You first said that there is no way to notify the user of completion Data2

. This is not true, you can use any number of ways to notify the user about this, for example, an event or Task

.

But I think you should rebuild your class. You said that getting it Data2

takes a very long time, which most likely means that it is using a lot of resources. Because of this, I guess you shouldn't even try to initialize Data2

if you don't need to. And how do you know that? The user will have to tell you. And ideally, if the user doesn't want to Data2

, they shouldn't even have access to it, which means splitting MetaData

into two classes: something like BasicMetaData

and ExtendedMetaData

, which inherits from BasicMetaData

>.



In ExtendedMetaData

you will have some way to notify the user that initialization is complete (most likely with an event), or you can force the constructor to wait for the initialization to complete (you can use Monitor.Wait()

and Monitor.Pulse()

to do this).

Personally, I think the best option would be if you had a static factory method that would return Task<ExtendedMetaData>

. Thus, the user can wait for the result synchronously (using Result

) or asynchronously (using ContinueWith()

or await

where available). This would be especially useful in .Net 4.5 (because of await

), but also on .Net 4.0. Unfortunately the tag in your question indicates that you are using .Net 3.5, which does not Task

. I would recommend that you upgrade if possible.

+2


source


I think you need to pay attention to the following patterns: lazy loading (to call "long" methods only when you really need it) and proxies (to implement layer caching, hide the internal implementation if necessary, possibly several different patterns at the bottom parts of your object). If you decide to use multiple objects to provide all the functionality, then Facade can be a smart choice as well.



+1


source


You will receive several different answers to this question. Here's my example.

IMO, you shouldn't call any operation in your constructor like what you are doing right now. Anything in your constructor MetaData

shouldn't be there to begin with.

While you are instantiating the object, it can throw an exception, which is fine, but your object will not be instantiated. Some of the best practices. the constructor must be short and must ensure that the object graph is created after the constructor.

Look also at this question: How much code do you need to add to the constructor?

Also, you have to inject your dependencies and create methods to populate the data.

It would be more helpful if you could describe your problem in more detail.

You really need to streamline the process and design.

0


source







All Articles