/// <summary>
        /// Perform the actual update.
        /// </summary>
        /// <param name="context">Context.</param>
        /// <param name="data">Data.</param>
        public TData Update(SQLiteDataContext context, TData data)
        {
            // JF- Probably no need to do this now
            // Make sure we're updating the right thing
            //if (data.Key.HasValue)
            //{
            //    var cacheItem = ApplicationContext.Current.GetService<IDataCachingService>()?.GetCacheItem(data.GetType(), data.Key.Value);
            //    if (cacheItem != null)
            //    {
            //        cacheItem.CopyObjectData(data);
            //        data = cacheItem as TData;
            //    }
            //}

            var retVal = this.UpdateInternal(context, data);

            //if (retVal != data) System.Diagnostics.Debugger.Break();
            context.AddCacheCommit(retVal);
            return(retVal);
        }
            /// <summary>
            /// Update the specified object
            /// </summary>
            protected override TModel UpdateInternal(SQLiteDataContext context, TModel data)
            {
                if (data.IsEmpty())
                {
                    return(data);
                }

                foreach (var rp in this.m_properties)
                {
                    var instance = rp.GetValue(data);
                    if (instance != null && rp.Name != "SourceEntity") // HACK: Prevent infinite loops on associtive entities
                    {
                        instance = ModelExtensions.TryGetExisting(instance as IIdentifiedEntity, context);
                        if (instance != null)
                        {
                            rp.SetValue(data, instance);
                        }
                        ModelExtensions.UpdateParentKeys(data, rp);
                    }
                }
                return(base.UpdateInternal(context, data));
            }
            /// <summary>
            /// Ensure exists
            /// </summary>
            protected override TModel InsertInternal(SQLiteDataContext context, TModel data)
            {
                foreach (var rp in typeof(TModel).GetRuntimeProperties().Where(o => typeof(IdentifiedData).GetTypeInfo().IsAssignableFrom(o.PropertyType.GetTypeInfo())))
                {
                    if (rp.GetCustomAttribute <DataIgnoreAttribute>() != null)
                    {
                        continue;
                    }

                    var instance = rp.GetValue(data);
                    if (instance != null)
                    {
                        instance = ModelExtensions.TryGetExisting(instance as IIdentifiedEntity, context);
                        if (instance != null)
                        {
                            rp.SetValue(data, instance);
                        }
                        ModelExtensions.UpdateParentKeys(data, rp);
                    }
                }
                return(base.InsertInternal(context, data));
            }
            /// <summary>
            /// Ensure exists
            /// </summary>
            protected override TModel InsertInternal(SQLiteDataContext context, TModel data)
            {
                if (data.IsEmpty())
                {
                    return(data);
                }

                foreach (var rp in this.m_properties)
                {
                    var instance = rp.GetValue(data);
                    if (instance != null)
                    {
                        instance = ModelExtensions.TryGetExisting(instance as IIdentifiedEntity, context);
                        if (instance != null)
                        {
                            rp.SetValue(data, instance);
                        }

                        ModelExtensions.UpdateParentKeys(data, rp);
                    }
                }
                return(base.InsertInternal(context, data));
            }
 /// <summary>
 /// To model instance
 /// </summary>
 object ISQLitePersistenceService.ToModelInstance(object domainInstance, SQLiteDataContext context)
 {
     return(this.ToModelInstance(domainInstance, context));
 }
 /// <summary>
 /// Get the specified data
 /// </summary>
 object ISQLitePersistenceService.Get(SQLiteDataContext context, Guid id)
 {
     return(this.Get(context, id));
 }
 /// <summary>
 /// Obsolete
 /// </summary>
 object ISQLitePersistenceService.Obsolete(SQLiteDataContext context, object data)
 {
     return(this.Obsolete(context, (TData)data));
 }
 /// <summary>
 /// Insert the specified data
 /// </summary>
 object ISQLitePersistenceService.Insert(SQLiteDataContext context, object data)
 {
     return(this.Insert(context, (TData)data));
 }
        /// <summary>
        /// Query internal without caring about limiting
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expr"></param>
        /// <returns></returns>
        public IEnumerable <TData> Query(SQLiteDataContext context, Expression <Func <TData, bool> > expr)
        {
            int t;

            return(this.QueryInternal(context, expr, 0, -1, out t, Guid.Empty, false, null));
        }
 /// <summary>
 /// Performs the actual query
 /// </summary>
 /// <param name="context">Context.</param>
 /// <param name="query">Query.</param>
 protected abstract IEnumerable <TData> QueryInternal(SQLiteDataContext context, String storedQueryName, IDictionary <String, Object> parms, int offset, int count, out int totalResults, Guid queryId, bool countResults, ModelSort <TData>[] orderBy);
 /// <summary>
 /// Performs the actual obsoletion
 /// </summary>
 /// <param name="context">Context.</param>
 /// <param name="data">Data.</param>
 protected abstract TData ObsoleteInternal(SQLiteDataContext context, TData data);
 /// <summary>
 /// Performthe actual insert.
 /// </summary>
 /// <param name="context">Context.</param>
 /// <param name="data">Data.</param>
 protected abstract TData InsertInternal(SQLiteDataContext context, TData data);
 /// <summary>
 /// Froms the model instance.
 /// </summary>
 /// <returns>The model instance.</returns>
 /// <param name="modelInstance">Model instance.</param>
 public abstract Object FromModelInstance(TData modelInstance, SQLiteDataContext context);
 /// <summary>
 /// Maps the data to a model instance
 /// </summary>
 /// <returns>The model instance.</returns>
 /// <param name="dataInstance">Data instance.</param>
 public abstract TData ToModelInstance(Object dataInstance, SQLiteDataContext context);
            /// <summary>
            /// Get all the matching TModel object from source
            /// </summary>
            public IEnumerable GetFromSource(SQLiteDataContext context, Guid sourceId, decimal?versionSequenceId)
            {
                int tr = 0;

                return(this.Query(context, o => o.SourceEntityKey == sourceId, Guid.Empty, 0, 100, out tr, false, null));
            }
        /// <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);
            //}
        }
        /// <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>
 /// Performs the actual query
 /// </summary>
 /// <param name="context">Context.</param>
 /// <param name="query">Query.</param>
 protected abstract IEnumerable <TData> QueryInternal(SQLiteDataContext context, Expression <Func <TData, bool> > query, int offset, int count, out int totalResults, Guid queryId, bool countResults, ModelSort <TData>[] orderBy);