v4 OrmLite model from generic type breaks saving of boxed DTOs

We use the following code to commit multiple DTO changes in a single transaction from a smart client application. Users can make many changes including creates, updates and deletes before hitting the Save button which then sends everything to the database. For example, we commit by calling conn.SaveDTOs(clients, orders, invoices); the code processes deletes from the last parameter to the first one and insert/updates from the first to the last to comply with FK relationships.

Our BaseDTO has a property to flag changes. It is the base type for DTOs generated from our database with a t4 template.

public static void SaveDTOs(this IDbConnection db, params IEnumerable<BaseDTO>[] dtoList)
{
    using (var tran = db.OpenTransaction())
    {
        //First Delete in reverse for FK constraints
        for (int i = dtoList.Length - 1; i >= 0; i--)
            foreach (var dto in dtoList[i])
                if (dto.UpdateMode == EUpdateMode.Delete)
                    db.Delete(dto);

        //Then Insert and Update in sequence
        foreach (var dtos in dtoList)
            foreach (var dto in dtos)
            {
                if (dto.UpdateMode == EUpdateMode.Insert)
                    db.Insert(dto);
                else if (dto.UpdateMode == EUpdateMode.Modify)
                    db.Update(dto);
            }

        //Commit the transaction
        tran.Commit();
    }
}

This worked well in the ServiceStack.OrmLite v3 and fails after we recently bought & upgraded to the commercial version. v3 code used below line to determine the model definition.

var modelDef = objWithProperties.GetType().GetModelDefinition();

It fails in the new versions where modelDef is based on TypeOf(T) (the boxed BaseDTO type in this case) instead of the actual object type.

Is there a way to achieve above save logic without duplicating and moving the logic and transaction up to calling code? Is there a better way to achieve the same outcome?

Changing the calling code to below is not nearly as tidy as the original conn.SaveDTOs(clients, orders, invoices); :blush:

using (var tran = conn.OpenTransaction())
{
conn.ProcessDTODeletes(invoices);
conn.ProcessDTODeletes(orders);
conn.ProcessDTODeletes(customers);
conn.ProcessDTOInsertAndUpdates(customers);
conn.ProcessDTOInsertAndUpdates(orders);
conn.ProcessDTOInsertAndUpdates(invoices);
}

Thanks for your assistance,
Carl

It looks like all your calls to this method are of the same type so I would refactor your method so it’s like:

public static void SaveDTOs(this IDbConnection db, params IEnumerable<T>[] dtoList)
{
    using (var tran = db.OpenTransaction())
    {
        //First Delete in reverse for FK constraints
        for (int i = dtoList.Length - 1; i >= 0; i--)
            foreach (var dto in dtoList[i])
            {
                var row = dto as BaseDTO;
                if (row.UpdateMode == EUpdateMode.Delete)
                    db.Delete(dto);
            }

        //Then Insert and Update in sequence
        foreach (var dtos in dtoList)
            foreach (var dto in dtos)
            {
                var row = dto as BaseDTO;
                if (row.UpdateMode == EUpdateMode.Insert)
                    db.Insert(dto);
                else if (row.UpdateMode == EUpdateMode.Modify)
                    db.Update(dto);
            }

        //Commit the transaction
        tran.Commit();
    }
}

Otherwise you can use db.CreateTypedApi() to return a Typed API for a runtime type, e.g:

foreach (var dto in dtos)
{
    var typedApi = db.CreateTypedApi(dto.GetType());
    if (dto.UpdateMode == EUpdateMode.Insert)
        typedApi.Insert(dto);
    else if (dto.UpdateMode == EUpdateMode.Modify)
        typedApi.Update(dto);
}