/// <summary>
        /// Ensure the specified object exists
        /// </summary>
        public static TModel EnsureExists <TModel>(this TModel me, SQLiteDataContext context, bool createIfNotExists = true) where TModel : IIdentifiedEntity
        {
            // Me
            var vMe = me as IVersionedEntity;

            // We have to find it
            var idpInstance = SQLitePersistenceService.GetPersister(me.GetType());

            var existing = me.TryGetExisting(context);


            // Existing exists?
            if (existing == null && !m_readonlyTypes.Contains(typeof(TModel)) && createIfNotExists)
            {
                IIdentifiedEntity inserted = ((TModel)idpInstance.Insert(context, me)) as IIdentifiedEntity;
                me.Key = inserted.Key;

                if (vMe != null)
                {
                    vMe.VersionKey = (inserted as IVersionedEntity).VersionKey;
                }
                existing = inserted;
            }
            else if (existing == null)
            {
                throw new KeyNotFoundException($"Object {me} not found in database and is restricted for creation");
            }

            return(existing == null ? me : (TModel)existing);
        }
        /// <summary>
        /// Try get by classifier
        /// </summary>
        public static IIdentifiedEntity TryGetExisting(this IIdentifiedEntity me, SQLiteDataContext context, bool forceDbSearch = false)
        {
            // Is there a classifier?
            var idpInstance = SQLitePersistenceService.GetPersister(me.GetType()) as ISQLitePersistenceService;

            if (idpInstance == null)
            {
                return(null);
            }
            //if (me.Key?.ToString() == "e4d3350b-b0f5-45c1-80ba-49e3844cbcc8")
            //    System.Diagnostics.Debugger.Break();

            IIdentifiedEntity existing = null;

            if (!forceDbSearch)
            {
                if (me.Key != null)
                {
                    existing = context.TryGetCacheItem(me.Key.Value);
                }
                if (existing != null)
                {
                    return(existing);
                }
                else if (!context.Connection.IsInTransaction)
                {
                    existing = context.TryGetData(me.Key.Value.ToString()) as IdentifiedData;
                }
                else if (me.Key.HasValue)
                {
                    existing = context.FindTransactedItem(me.Key.Value);
                }
                if (forceDbSearch && me.Key.HasValue)
                {
                    ApplicationContext.Current.GetService <IDataCachingService>().Remove(me.Key.Value);
                }
            }

            // Is the key not null?
            if (me.Key != Guid.Empty && me.Key != null && existing == null)
            {
                existing = idpInstance.Get(context, me.Key.Value) as IIdentifiedEntity;
            }
            else
            {
                var classAtt = me.GetType().GetTypeInfo().GetCustomAttribute <KeyLookupAttribute>();
                if (classAtt != null && existing == null)
                {
                    // Get the domain type
                    var dataType = SQLitePersistenceService.Mapper.MapModelType(me.GetType());
                    var tableMap = TableMapping.Get(dataType);

                    // Get the classifier attribute value
                    var    classProperty   = me.GetType().GetRuntimeProperty(classAtt.UniqueProperty);
                    object classifierValue = classProperty.GetValue(me); // Get the classifier

                    // Is the classifier a UUID'd item?
                    if (classifierValue is IIdentifiedEntity)
                    {
                        classifierValue = (classifierValue as IIdentifiedEntity).Key.Value;
                        classProperty   = me.GetType().GetRuntimeProperty(classProperty.GetCustomAttribute <SerializationReferenceAttribute>()?.RedirectProperty ?? classProperty.Name);
                    }

                    // Column
                    var column = tableMap.GetColumn(SQLitePersistenceService.Mapper.MapModelProperty(me.GetType(), dataType, classProperty));
                    // Now we want to query
                    SqlStatement stmt = new SqlStatement().SelectFrom(dataType)
                                        .Where($"{column.Name} = ?", classifierValue).Build();

                    var mapping    = context.Connection.GetMapping(dataType);
                    var dataObject = context.Connection.Query(mapping, stmt.SQL, stmt.Arguments.ToArray()).FirstOrDefault();
                    if (dataObject != null)
                    {
                        existing = idpInstance.ToModelInstance(dataObject, context) as IIdentifiedEntity;
                    }
                }
            }
            if (existing != null && me.Key.HasValue)
            {
                context.AddData(me.Key.Value.ToString(), existing);
            }

            return(existing);
        }
        /// <summary>
        /// Load specified associations
        /// </summary>
        public static void LoadAssociations <TModel>(this TModel me, SQLiteDataContext context, params string[] excludeProperties) where TModel : IIdentifiedEntity
        {
            //using (context.LockConnection())
            //{
            if (me == null)
            {
                throw new ArgumentNullException(nameof(me));
            }
            else if (me.LoadState == LoadState.FullLoad ||
                     context.DelayLoadMode == LoadState.PartialLoad && context.LoadAssociations == null)
            {
                return;
            }
            else if (context.Connection.IsInTransaction)
            {
                return;
            }

#if DEBUG
            /*
             * Me neez all the timez
             *
             * /\_/\
             * >^.^<.---.
             * _'-`-'     )\
             * (6--\ |--\ (`.`-.
             *   --'  --'  ``-'
             */
            Stopwatch sw = new Stopwatch();
            sw.Start();
#endif

            // Cache get classification property - thiz makez us fasters
            PropertyInfo classProperty = null;
            if (!s_classificationProperties.TryGetValue(typeof(TModel), out classProperty))
            {
                classProperty = typeof(TModel).GetRuntimeProperty(typeof(TModel).GetTypeInfo().GetCustomAttribute <ClassifierAttribute>()?.ClassifierProperty ?? "____XXX");
                if (classProperty != null)
                {
                    classProperty = typeof(TModel).GetRuntimeProperty(classProperty.GetCustomAttribute <SerializationReferenceAttribute>()?.RedirectProperty ?? classProperty.Name);
                }
                lock (s_lockObject)
                    if (!s_classificationProperties.ContainsKey(typeof(TModel)))
                    {
                        s_classificationProperties.Add(typeof(TModel), classProperty);
                    }
            }

            // Classification property?
            String classValue = classProperty?.GetValue(me)?.ToString();

            // Cache the props so future kitties can call it
            IEnumerable <PropertyInfo> properties = null;
            var propertyCacheKey = $"{me.GetType()}.FullName[{classValue}]";
            if (!s_runtimeProperties.TryGetValue(propertyCacheKey, out properties))
            {
                lock (s_runtimeProperties)
                {
                    properties = me.GetType().GetRuntimeProperties().Where(o => o.GetCustomAttribute <DataIgnoreAttribute>() == null && o.GetCustomAttributes <AutoLoadAttribute>().Any(p => p.ClassCode == classValue || p.ClassCode == null) && typeof(IdentifiedData).GetTypeInfo().IsAssignableFrom(o.PropertyType.StripGeneric().GetTypeInfo())).ToList();

                    if (!s_runtimeProperties.ContainsKey(propertyCacheKey))
                    {
                        s_runtimeProperties.Add(propertyCacheKey, properties);
                    }
                }
            }

            // Load fast or lean mode only root associations which will appear on the wire
            if (context.LoadAssociations != null)
            {
                var loadAssociations = context.LoadAssociations.Where(o => o.StartsWith(me.GetType().GetTypeInfo().GetCustomAttribute <XmlTypeAttribute>()?.TypeName))
                                       .Select(la => la.Contains(".") ? la.Substring(la.IndexOf(".") + 1) : la).ToArray();
                if (loadAssociations.Length == 0)
                {
                    return;
                }
                properties = properties.Where(p => loadAssociations.Any(la => la == p.Name)).ToList();
            }

            // Iterate over the properties and load the properties
            foreach (var pi in properties)
            {
                if (excludeProperties?.Contains(pi.Name) == true)
                {
                    continue;
                }
                // Map model type to domain
                var adoPersister = SQLitePersistenceService.GetPersister(pi.PropertyType.StripGeneric());

                // Loading associations, so what is the associated type?
                if (typeof(IList).GetTypeInfo().IsAssignableFrom(pi.PropertyType.GetTypeInfo()) &&
                    adoPersister is ISQLiteAssociativePersistenceService &&
                    me.Key.HasValue) // List so we select from the assoc table where we are the master table
                {
                    // Is there not a value?
                    var assocPersister = adoPersister as ISQLiteAssociativePersistenceService;

                    // We want to que   ry based on our PK and version if applicable
                    decimal?versionSequence = (me as IVersionedEntity)?.VersionSequence;
                    var     assoc           = assocPersister.GetFromSource(context, me.Key.Value, versionSequence);

                    ConstructorInfo ci = null;
                    if (!m_constructors.TryGetValue(pi.PropertyType, out ci))
                    {
                        var type = pi.PropertyType.StripGeneric();
                        while (type != typeof(Object) && ci == null)
                        {
                            ci   = pi.PropertyType.GetTypeInfo().DeclaredConstructors.FirstOrDefault(o => o.GetParameters().Length == 1 && o.GetParameters()[0].ParameterType == typeof(IEnumerable <>).MakeGenericType(type));
                            type = type.GetTypeInfo().BaseType;
                        }
                        if (ci != null)
                        {
                            lock (s_lockObject)
                                if (!m_constructors.ContainsKey(pi.PropertyType))
                                {
                                    m_constructors.Add(pi.PropertyType, ci);
                                }
                        }
                        else
                        {
                            throw new InvalidOperationException($"This is odd, you seem to have a list with no constructor -> {pi.PropertyType}");
                        }
                    }

                    //var listValue = Activator.CreateInstance(pi.PropertyType, assoc);
                    var listValue = ci.Invoke(new object[] { assoc });
                    pi.SetValue(me, listValue);
                }
                else if (typeof(IIdentifiedEntity).GetTypeInfo().IsAssignableFrom(pi.PropertyType.GetTypeInfo())) // Single
                {
                    // Single property, we want to execute a get on the key property
                    var redirectAtt = pi.GetCustomAttribute <SerializationReferenceAttribute>();
                    if (redirectAtt == null)
                    {
                        continue; // cannot get key property
                    }
                    // We want to issue a query
                    var keyProperty = pi.DeclaringType.GetRuntimeProperty(redirectAtt.RedirectProperty);
                    var keyValue    = keyProperty?.GetValue(me);
                    if (keyValue == null ||
                        Guid.Empty.Equals(keyValue))
                    {
                        continue; // No key specified
                    }
                    // This is kinda messy.. maybe iz to be changez
                    object value = null;
                    if (!context.Data.TryGetValue(keyValue.ToString(), out value))
                    {
                        value = adoPersister.Get(context, (Guid)keyValue);
                        context.AddData(keyValue.ToString(), value);
                    }
                    pi.SetValue(me, value);
                }
            }
#if DEBUG
            sw.Stop();
            s_tracer.TraceVerbose("Load associations for {0} took {1} ms", me, sw.ElapsedMilliseconds);
#endif

            me.LoadState = excludeProperties.Length > 0 ? LoadState.PartialLoad : LoadState.FullLoad;

            ApplicationContext.Current.GetService <IDataCachingService>()?.Add(me as IdentifiedData);
            //}
        }