UnitOfWork with lazy loading and multiple data sources

I have business objects that are stored in two data stores. Part of the object is stored in Azure Table Storage and the other part is stored in Azure SQL. Most of the SQL is used in queries, while table storage is used for properties that take up a lot of space.

In most cases, only the SQL part of the object is used (in SQL queries). Table storage properties are only needed when someone explicitly requests this object. What I'm trying to achieve is a design that hides the fact that there are two data sources behind the business object by lazy loading the storage table properties (since they are not needed when doing SQL queries) and still doing code validation.

There are several POCOs in my current project that are created by the work unit. I don't want to create two POCOs, one for Table Storage and one for SQL, so I thought about the following design:

//Make the properties virtual
public class Customer
{
    public virtual string Name {get;set;} //Stored in SQL
    public virtual string Age {get;set;} //Stored in SQL
    public virtual string Details {get;set;} // This prop is stored in Table Storage
}

//Create a derived internal POCO that can notify when a property is asked
internal class CustomerWithMultipleStorage
{
    public event EventHandler OnDetailsGet;

    public override string Details
    {
         get { if (OnDetailsGet!=null) OnDetailsGet( ... ); /* rest of the code */ }
         set { /* code */ }
    }
}

      

All my data layer code will work with CustomerWithMultipleStorage

, and all "external" code outside the DL will use Customer

and no events will be displayed. Now, when the unit of work returns Customer

, it will only load the SQL properties and subscribe to Get events. If someone using the Client needs the rest of the properties, the event will be fired and the table store properties will be loaded.

What do you think of this project? Is this the correct approach? Do you know the best way to do this?

+3


source to share


1 answer


You can use Lazy<T>

with dependency injection. Please note that this is just to give you some ideas.

internal class CustomerWithMultipleStorage : Customer
{
    private readonly ISqlDataLayer _sqlDataLayer;
    private readonly ITableStorageDataLayer _tableStorageDataLayer;
    private readonly Lazy<string> _details;
    private string _detailsValue;

    public CustomerWithMultipleStorage(ISqlDataLayer sqlDataLayer, ITableStorageDataLayer tableStorageDataLayer)
    {
        _sqlDataLayer = sqlDataLayer;
        _tableStorageDataLayer = tableStorageDataLayer;

        _details = new Lazy<string>(() => return (string)_tableStorageDataLayer.GetValue<Customer>(this, "Details"));
    }

    public override string Details
    {
         get
         {
            return (_detailsValue ?? (_detailsValue = _details.Value));
         }
         set
         {
            _detailsValue = value;
            _tableStorageDataLayer.SetValue<Customer>(this, _detailsValue);
         }
    }
}

public interface ITableStorageDataLayer
{
    object GetValue<T>(T item, [CallerMemberName] string property = "");
    void SetValue<T>(T item, object value, [CallerMemberName] string property = "");
}

      



You can also just use a data layer with mapping data for each object (I'll provide examples later).

+1


source







All Articles