/// <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); //} }