Code to traverse all object graph and remove IsSoftDeleted?

Hi. I am looking for any existing code/implementations whereby SS traverses through an entire object graph and touches each element, that I can reference and adapt to remove all nested softdeleted records that have been loaded.

Internally, I’ve been writing some extension methods to facade all the load operations to ensure that they are consistent in terms of loading only non-soft-deleted items, e.g

/// <summary>
/// Handle SoftDeletable table selections. Use like:
/// var userRoles = Db.SoftSelect&lt;UserAuthRole&gt;();
/// </summary>
/// <typeparam name="TTable"></typeparam>
/// <param name="db"></param>
/// <returns></returns>
public static List<TTable> SoftSelect<TTable>(this IDbConnection db) where TTable : IHasSoftDelete
{
	return db.Select(db.From<TTable>());
}

rather than Db.Select(). Then, i can easily see/detect places see where I might have accidentally left in logic that wouldn’t cater for soft deletes, because my logic would be using all (only) Db.SoftSomeOperation. So far so good.

As you’ve noted elsewhere, the Ormlite logic that currently supports SoftDeletions only operates at the root object level, and does not filter out the child records that have been soft deleted. So

var parentsIdGt10 = Db.LoadSelect<MyTableWithChildren>(x => x.Id > 10);

would potentially include child references that implement ISoftDelete and are softdeleted.

For now, I am not going to bother trying to modify the sql query builder logic to attempt to do this at the db layer, but I would like to write some generic library to traverse an object graph and remove all softdeleted items in an automated fashion in the application layer.

I’m looking at https://github.com/ServiceStack/ServiceStack.OrmLite/blob/bf38ba48e24808afddd50ec9d1d6795ce4375e20/src/ServiceStack.OrmLite/Support/LoadReferences.cs

and guessing that would be a good place to start some sort of “given some object, now traverse through all its references, and that that’s references, etc, and remove any items that are IsDeleted==true” (i.e. to set a single softdeleted refernce property to null, and remove softdeleted items from child reference Lists, accordingly)?

Another possibility might be to examine some json serialization logic - do you have any suggested starting points other than those I just suggested? Somewhere you might already be doing something similar?

The results returned by OrmLite are disconnected POCOs, the fact they were populated by OrmLite is no longer relevant. LoadSelect only loads 1-level deep so you would only need to look at the direct child references and remove results you want using normal C# reflection.

So you would just use normal C# reflection to look for any IList properties and remove the items you want. You can use the non-generic IList.Remove() API to remove the elements you want. If your items implement IHasSoftDelete you can do something like:

var itemsToRemove = new List<object>();
foreach (var item in list)
{
    if (var item is IHasSoftDelete row && row.IsDeleted) {
        itemsToRemove.Add(item);
    }
}
foreach (var item in itemsToRemove)
{
    list.Remove(item);
}

I appreciate your feedback. However, thinking it over, I think that I am trying to apply a bandaid to my design by doing in-application-code-graph-traversal, when really, I want to push the efficient load all the way to the db.

After digging through the code a bit, I wonder if you’d be open to the idea of a slight shift (or supplementary/alternative approach) in the way that softdelete is implemented, which I think could be applied across all operations (relatively efficiently?), and if it works, I’d submit a pull request for inclusion in Ormlite.

Specifically, rather than the existing selectfilter approach

OrmLiteConfig.SqlExpressionSelectFilter = q =>
{
if (q.ModelDef.ModelType.HasInterface(typeof(ISoftDelete)))
    {
    q.Where<ISoftDelete>(x => x.IsDeleted != true);
}
};

, I was thinking about attempting to implement a new attribute, similar to the way that RowVersion is implemented.

so something like:

[IsSoftDelete]
public bool IsDeleted {get;set;}

I (think) this would then involve adding a new property to the ModelDefinition class, to track the IsDeleted field, and then, variously, when building up the database query strings, I could test for the existence of a field with that attribute and append it to the WHERE conditions accordingly.

So, for example, the method:

protected string GetRefFieldSql(ModelDefinition refModelDef, FieldDefinition refField)

from https://github.com/ServiceStack/ServiceStack.OrmLite/tree/bf38ba48e24808afddd50ec9d1d6795ce4375e20/src/ServiceStack.OrmLite/Support could append the softdelete condition, and likewise the the SqlExpressionSelectFilter or WhereFilters etc, would be able to to apply the same condition.

Thoughts?

I definitely don’t want any hard coded notions of Soft Delete baked into OrmLite, that’s specific to the application and everyone has their preferred way of implementing it. I’d be open to adding a custom filter to allow custom logic as long as it’s not too disruptive.

No worries. I’m not sure if I can come up with something generic enough for incorporation then, but I’ll take a crack when I have some free time at work and shoot you anything if it looks promising. Cheers.