scubasteve
scubasteve

Reputation: 2858

How do I add a lazy loaded column in EntitySpaces?

If you do not have experience with or aren't currently using EntitySpaces ("ES") ORM this question is not meant for you.

I have a 10 year old application that after 4 years now needs my attention. My application uses a now defunct ORM called EntitySpaces and I'm hoping if you're reading this you have experience or maybe still use it too! Switching to another ORM is not an option at this time so I need to find a way to make this work.

Between the time I last actively worked on my application and now (ES Version 2012-09-30), EntitySpaces ("ES") has gone through a significant change in the underlying ADO.net back-end. The scenario that I'm seeking help on is when an entity collection is loaded with only a subset of the columns:

_products = new ProductCollection();
_products.Query.SelectAllExcept(_products.Query.ImageData);
_products.LoadAll();

I then override the properties that weren't loaded in the initial select so that I may lazyload them in the accessor. Here is an example of one such lazy-loaded property that used to work perfectly.

public override byte[] ImageData
{
    get
    {
        bool rowIsDirty = base.es.RowState != DataRowState.Unchanged;

        //  Check if we have loaded the blob data
        if(base.Row.Table != null && base.Row.Table.Columns.Contains(ProductMetadata.ColumnNames.ImageData) == false)
        {
            //  add the column before we can save data to the entity
            this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
        }

        if(base.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull)
        {
            //  Need to load the data
            Product product = new Product();
            product.Query.Select(product.Query.ImageData).Where(product.Query.ProductID == base.ProductID);
            if(product.Query.Load())
            {
                if (product.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull == false)
                {
                    base.ImageData = product.ImageData;

                    if (rowIsDirty == false)
                    {
                        base.AcceptChanges();
                    }
                }
            }
        }

        return base.ImageData;
    }
    set
    {
        base.ImageData = value;
    }
}

The interesting part is where I add the column to the underlying DataTable DataColumn collection:

this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));

I had to comment out all the ADO.net related stuff from that accessor when I updated to the current (and open source) edition of ES (version 2012-09-30). That means that the "ImageData" column isn't properly configured and when I change it's data and attempt to save the entity I receive the following error:

Column 'ImageData' does not belong to table .

I've spent a few days looking through the ES source and experimenting and it appears that they no longer use a DataTable to back the entities, but instead are using a 'esSmartDictionary'.

My question is: Is there a known, supported way to accomplish the same lazy loaded behavior that used to work in the new version of ES? Where I can update a property (i.e. column) that wasn't included in the initial select by telling the ORM to add it to the entity backing store?

Upvotes: 0

Views: 284

Answers (1)

scubasteve
scubasteve

Reputation: 2858

After analyzing how ES constructs the DataTable that is uses for updates it became clear that columns not included in the initial select (i.e. load) operation needed to be added to the esEntityCollectionBase.SelectedColumns dictionary. I added the following method to handle this.

/// <summary>
/// Appends the specified column to the SelectedColumns dictionary. The selected columns collection is 
/// important as it serves as the basis for DataTable creation when updating an entity collection. If you've 
/// lazy loaded a column (i.e. it wasn't included in the initial select) it will not be automatically 
/// included in the selected columns collection. If you want to update the collection including the lazy 
/// loaded column you need to use this method to add the column to the Select Columns list.
/// </summary>
/// <param name="columnName">The lazy loaded column name. Note: Use the {yourentityname}Metadata.ColumnNames 
/// class to access the column names.</param>
public void AddLazyLoadedColumn(string columnName)
{
    if(this.selectedColumns == null)
    {
        throw new Exception(
            "You can only append a lazy-loaded Column to a partially selected entity collection");
    }

    if (this.selectedColumns.ContainsKey(columnName))
    {
        return;
    }
    else
    {
        //  Using the count because I can't determine what the value is supposed to be or how it's used. From 
        //  I can tell it's just the number of the column as it was selected: if 8 colums were selected the 
        //  value would be 1 through 8 - ??
        int columnValue = selectedColumns.Count;
        this.selectedColumns.Add(columnName, columnValue);
    }
}

You would use this method like this:

public override System.Byte[] ImageData
{
    get
    {
        var collection = this.GetCollection();
        if(collection != null)
        {
            collection.AddLazyLoadedColumn(ProductMetadata.ColumnNames.ImageData);
        }
...

It's a shame that nobody is interested in the open source EntitySpaces. I'd be happy to work on it if I thought it had a future, but it doesn't appear so. :(

I'm still interested in any other approaches or insight from other users.

Upvotes: 0

Related Questions