/// <summary> /// Gathers dependencies of ColumnInfo, this includes all [Sql] properties on any object in data export / catalogue databases /// which references the fully qualified name of the ColumnInfo as well as it's immediate network friends that should share it's /// runtime name e.g. CatalogueItem and ExtractionInformation. /// </summary> /// <param name="c"></param> /// <returns></returns> public GatheredObject GatherDependencies(ColumnInfo c) { var allObjects = GetAllObjectsInAllDatabases(); var propertyFinder = new AttributePropertyFinder <SqlAttribute>(allObjects); var root = new GatheredObject(c); foreach (var o in allObjects) { //don't add a reference to the thing we are gathering dependencies on! if (Equals(o, c)) { continue; } foreach (var propertyInfo in propertyFinder.GetProperties(o)) { var sql = (string)propertyInfo.GetValue(o); if (sql != null && sql.Contains(c.Name)) { root.Children.Add(new GatheredObject(o)); } } } return(root); }
/// <summary> /// Copies all properties not marked with [NoMappingToDatabase] or [Relationship] from the this object to the <paramref name="to"/> object. /// Also skips 'Name' and 'ID' /// </summary> /// <param name="to"></param> /// <param name="copyName"></param> /// <param name="save"></param> protected void CopyShallowValuesTo(DatabaseEntity to, bool copyName = false, bool save = true) { if (GetType() != to.GetType()) { throw new NotSupportedException(string.Format("Object to must be the same Type as us, we were '{0}' and it was '{1}'", GetType().Name, to.GetType().Name)); } var noMappingFinder = new AttributePropertyFinder <NoMappingToDatabase>(to); var relationsFinder = new AttributePropertyFinder <RelationshipAttribute>(to); foreach (var p in GetType().GetProperties()) { if (p.Name.Equals("ID")) { continue; } if (p.Name.Equals("Name") && !copyName) { continue; } if (noMappingFinder.GetAttribute(p) != null || relationsFinder.GetAttribute(p) != null) { continue; } p.SetValue(to, p.GetValue(this)); } if (save) { to.SaveToDatabase(); } }
/// <summary> /// Updates the user configurable (non ID) properties of the object <pararef name="o"/> to match the <paramref name="shareDefinition"/> /// </summary> /// <param name="o"></param> /// <param name="shareDefinition"></param> public void ImportPropertiesOnly(IMapsDirectlyToDatabaseTable o, ShareDefinition shareDefinition) { if (shareDefinition.Type != o.GetType()) { throw new Exception("Share Definition is not for a " + o.GetType()); } AttributePropertyFinder <RelationshipAttribute> relationshipPropertyFinder = new AttributePropertyFinder <RelationshipAttribute>(o); AttributePropertyFinder <DoNotImportDescriptionsAttribute> skipPropertyFinder = new AttributePropertyFinder <DoNotImportDescriptionsAttribute>(o); //for each property that isn't [NoMappingToDatabase] foreach (var kvp in shareDefinition.GetDictionaryForImport()) { if (kvp.Key == "Name") { continue; } var prop = o.GetType().GetProperty(kvp.Key); //If the property is a relationship e.g. _ID skip it if (relationshipPropertyFinder.GetAttribute(prop) != null) { continue; } //do we skip this property? var skip = skipPropertyFinder.GetAttribute(prop); if (skip != null) { //yes but only if blank if (skip.AllowOverwriteIfBlank) { //is it currently not null? (if so skip) var oldVal = prop.GetValue(o); if (!(oldVal == DBNull.Value || oldVal == null || string.IsNullOrWhiteSpace(oldVal.ToString()))) { continue; } } else { continue; //always skip } } SetValue(prop, kvp.Value, o); } }
/// <summary> /// Creates a sharing export (<see cref="ObjectExport"/>) for the current <see cref="GatheredObject.Object"/> and then serializes it as a <see cref="ShareDefinition"/>. /// This includes mapping any [<see cref="RelationshipAttribute"/>] properties on the <see cref="GatheredObject.Object"/> to the relevant Share Guid (which must /// exist in branchParents). /// /// <para>ToShareDefinitionWithChildren if you want a full list of shares for the whole tree</para> /// </summary> /// <param name="shareManager"></param> /// <param name="branchParents"></param> /// <returns></returns> public ShareDefinition ToShareDefinition(ShareManager shareManager, List <ShareDefinition> branchParents) { var export = shareManager.GetNewOrExistingExportFor(Object); Dictionary <string, object> properties = new Dictionary <string, object>(); Dictionary <RelationshipAttribute, Guid> relationshipProperties = new Dictionary <RelationshipAttribute, Guid>(); AttributePropertyFinder <RelationshipAttribute> relationshipFinder = new AttributePropertyFinder <RelationshipAttribute>(Object); AttributePropertyFinder <NoMappingToDatabase> noMappingFinder = new AttributePropertyFinder <NoMappingToDatabase>(Object); //for each property in the Object class foreach (PropertyInfo property in Object.GetType().GetProperties()) { //if it's the ID column skip it if (property.Name == "ID") { continue; } //skip [NoMapping] columns if (noMappingFinder.GetAttribute(property) != null) { continue; } //skip IRepositories (these tell you where the object came from) if (typeof(IRepository).IsAssignableFrom(property.PropertyType)) { continue; } RelationshipAttribute attribute = relationshipFinder.GetAttribute(property); //if it's a relationship to a shared object if (attribute != null && (attribute.Type == RelationshipType.SharedObject || attribute.Type == RelationshipType.OptionalSharedObject)) { var idOfParent = property.GetValue(Object); Type typeOfParent = attribute.Cref; var parent = branchParents.SingleOrDefault(d => d.Type == typeOfParent && d.ID.Equals(idOfParent)); //if the parent is not being shared along with us if (parent == null) { //if a reference is required (i.e. not optional) if (attribute.Type != RelationshipType.OptionalSharedObject) { throw new SharingException("Property " + property + " on Type " + Object.GetType() + " is marked [Relationship] but we found no ShareDefinition amongst the current objects parents to satisfy this property"); } } else { relationshipProperties.Add(attribute, parent.SharingGuid); } } else { properties.Add(property.Name, property.GetValue(Object)); } } return(new ShareDefinition(export.SharingUIDAsGuid, Object.ID, Object.GetType(), properties, relationshipProperties)); }
public void UpsertAndHydrate <T>(T toCreate, ShareDefinition shareDefinition) where T : class, IMapsDirectlyToDatabaseTable { IRepository repo; if (RepositoryLocator.CatalogueRepository.SupportsObjectType(typeof(T))) { repo = RepositoryLocator.CatalogueRepository; } else if (RepositoryLocator.DataExportRepository.SupportsObjectType(typeof(T))) { repo = RepositoryLocator.DataExportRepository; } else { throw new NotSupportedException("No Repository supported object type '" + typeof(T) + "'"); } //Make a dictionary of the normal properties we are supposed to be importing Dictionary <string, object> propertiesDictionary = shareDefinition.GetDictionaryForImport(); //for finding properties decorated with [Relationship] var finder = new AttributePropertyFinder <RelationshipAttribute>(toCreate); //If we have already got a local copy of this shared object? //either as an import or as an export T actual = (T)GetExistingImportObject(shareDefinition.SharingGuid) ?? (T)GetExistingExportObject(shareDefinition.SharingGuid); //we already have a copy imported of the shared object if (actual != null) { //It's an UPDATE i.e. take the new shared properties and apply them to the database copy / memory copy //copy all the values out of the share definition / database copy foreach (PropertyInfo prop in TableRepository.GetPropertyInfos(typeof(T))) { //don't update any ID columns or any with relationships on UPDATE if (propertiesDictionary.ContainsKey(prop.Name) && finder.GetAttribute(prop) == null) { SetValue(prop, propertiesDictionary[prop.Name], toCreate); } else { prop.SetValue(toCreate, prop.GetValue(actual)); //or use the database one if it isn't shared (e.g. ID, MyParent_ID etc) } } toCreate.Repository = actual.Repository; //commit the updated values to the database repo.SaveToDatabase(toCreate); } else { //It's an INSERT i.e. create a new database copy with the correct foreign key values and update the memory copy //for each relationship property on the class we are trying to hydrate foreach (PropertyInfo property in TableRepository.GetPropertyInfos(typeof(T))) { RelationshipAttribute relationshipAttribute = finder.GetAttribute(property); //if it has a relationship attribute then we would expect the ShareDefinition to include a dependency relationship with the sharing UID of the parent //and also that we had already imported it since dependencies must be imported in order if (relationshipAttribute != null) { int?newValue; switch (relationshipAttribute.Type) { case RelationshipType.OptionalSharedObject: case RelationshipType.SharedObject: //Confirm that the share definition includes the knowledge that theres a parent class to this object if (!shareDefinition.RelationshipProperties.ContainsKey(relationshipAttribute)) { //if it doesn't but the field is optional, ignore it if (relationshipAttribute.Type == RelationshipType.OptionalSharedObject) { newValue = null; break; } else { //otherwise we are missing a required shared object being referenced. That's bad news. throw new Exception("Share Definition for object of Type " + typeof(T) + " is missing an expected RelationshipProperty called " + property.Name); } } //Get the SharingUID of the parent for this property Guid importGuidOfParent = shareDefinition.RelationshipProperties[relationshipAttribute]; //Confirm that we have a local import of the parent var parentImport = GetExistingImport(importGuidOfParent); //if we don't have a share reference if (parentImport == null) { //and it isn't optional if (relationshipAttribute.Type == RelationshipType.SharedObject) { throw new Exception("Cannot import an object of type " + typeof(T) + " because the ShareDefinition specifies a relationship to an object that has not yet been imported (A " + relationshipAttribute.Cref + " with a SharingUID of " + importGuidOfParent); } else { newValue = null; //it was optional and missing so just set to null } } else { newValue = parentImport.ReferencedObjectID; //we have the shared object } break; case RelationshipType.LocalReference: newValue = GetLocalReference(property, relationshipAttribute, shareDefinition); break; case RelationshipType.IgnoreableLocalReference: newValue = null; break; default: throw new ArgumentOutOfRangeException(); } //get the ID of the local import of the parent if (propertiesDictionary.ContainsKey(property.Name)) { propertiesDictionary[property.Name] = newValue; } else { propertiesDictionary.Add(property.Name, newValue); } } } //insert the full dictionary into the database under the Type repo.InsertAndHydrate(toCreate, propertiesDictionary); //document that a local import of the share now exists and should be updated/reused from now on when that same GUID comes in / gets used by child objects GetImportAs(shareDefinition.SharingGuid.ToString(), toCreate); } }
public NavigateToObjectUI(IActivateItems activator, string initialSearchQuery = null, RDMPCollection focusedCollection = RDMPCollection.None) : base(activator) { _coreIconProvider = activator.CoreIconProvider; _favouriteProvider = Activator.FavouritesProvider; _magnifier = FamFamFamIcons.magnifier; InitializeComponent(); CompletionAction = Emphasise; activator.Theme.ApplyTo(toolStrip1); _searchables = Activator.CoreChildProvider.GetAllSearchables(); _usefulPropertyFinder = new AttributePropertyFinder <UsefulPropertyAttribute>(_searchables.Keys); textBox1.Focus(); textBox1.Text = initialSearchQuery; textBox1.TextChanged += tbFind_TextChanged; textBox1.KeyUp += _scintilla_KeyUp; FetchMatches(initialSearchQuery, CancellationToken.None); StartPosition = FormStartPosition.CenterScreen; DoubleBuffered = true; _types = _searchables.Keys.Select(k => k.GetType()).Distinct().ToArray(); _typeNames = new HashSet <string>(_types.Select(t => t.Name)); foreach (Type t in StartingEasyFilters.SelectMany(v => v.Value)) { if (!_typeNames.Contains(t.Name)) { _typeNames.Add(t.Name); } } //autocomplete is all Type names (e.g. "Catalogue") + all short codes (e.g. "c") textBox1.AutoCompleteMode = AutoCompleteMode.Append; textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource; textBox1.AutoCompleteCustomSource.AddRange( _typeNames.Union(ShortCodes.Select(kvp => kvp.Key)).ToArray()); Type[] startingFilters = null; if (focusedCollection != RDMPCollection.None && StartingEasyFilters.ContainsKey(focusedCollection)) { startingFilters = StartingEasyFilters[focusedCollection]; } BackColorProvider backColorProvider = new BackColorProvider(); foreach (Type t in EasyFilterTypesAndAssociatedCollections.Keys) { var b = new ToolStripButton(); b.Image = activator.CoreIconProvider.GetImage(t); b.CheckOnClick = true; b.Tag = t; b.DisplayStyle = ToolStripItemDisplayStyle.Image; string shortCode = ShortCodes.Single(kvp => kvp.Value == t).Key; b.Text = $"{t.Name} ({shortCode})"; b.CheckedChanged += CollectionCheckedChanged; b.Checked = startingFilters != null && startingFilters.Contains(t); b.BackgroundImage = backColorProvider.GetBackgroundImage(b.Size, EasyFilterTypesAndAssociatedCollections[t]); toolStrip1.Items.Add(b); } }