internal TikListMerge(ITikConnection connection, IEnumerable <TEntity> expected, IEnumerable <TEntity> original) { _connection = connection; _metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); _expected = expected; _original = original; }
private static ITikCommand CreateLoadCommandWithFilter <TEntity> (ITikConnection connection, ITikCommandParameter[] parameters) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); ITikCommand command = connection.CreateCommand(metadata.EntityPath + metadata.LoadCommand, metadata.LoadDefaultParameneterFormat); // =detail= if (metadata.IncludeDetails) { command.AddParameter("detail", "", TikCommandParameterFormat.NameValue); } //.proplist if (metadata.IncludeProplist) { command.AddParameter(TikSpecialProperties.Proplist, string.Join(",", metadata.Properties.Select(prop => prop.FieldName).ToArray()), TikCommandParameterFormat.NameValue); } //filter //parameters if (parameters != null) { foreach (ITikCommandParameter param in parameters) { command.Parameters.Add(param); } } return(command); }
/// <summary> /// Compares IDs (.id) of two instances of entity. /// </summary> /// <param name="entity1">First entity.</param> /// <param name="entity2">Seconf entity.</param> /// <returns>True if ids are equal.</returns> public static bool IdEquals <TEntity>(this TEntity entity1, TEntity entity2) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); string id1 = metadata.IdProperty.GetEntityValue(entity1); string id2 = metadata.IdProperty.GetEntityValue(entity2); return(string.Equals(id1, id2)); }
/// <summary> /// Creates entity string description (for logging, etc.) by its fields. /// </summary> /// <typeparam name="TEntity">Type of entity.</typeparam> /// <param name="entity">Entity instance.</param> /// <returns>Readable description of entity and its fields.</returns> public static string EntityToString <TEntity>(this TEntity entity) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); StringBuilder sb = new StringBuilder(typeof(TEntity).FullName + ":"); foreach (var property in metadata.Properties) { sb.AppendLine(string.Format(" {0}={1}", property.FieldName, property.GetEntityValue(entity))); } return(sb.ToString()); }
private static TEntity CreateObject <TEntity>(ITikReSentence sentence) where TEntity : new() { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); TEntity result = new TEntity(); foreach (var property in metadata.Properties) { property.SetEntityValue(result, GetValueFromSentence(sentence, property)); } return(result); }
/// <summary> /// Crates clone of given entity by its fields. /// </summary> /// <typeparam name="TEntity">Type of entity.</typeparam> /// <param name="entity">Entity to be cloned.</param> /// <returns>Cloned instance of entity.</returns> /// <remarks>Clones only fields marked with <see cref="TikPropertyAttribute"/>.</remarks> public static TEntity CloneEntity <TEntity>(this TEntity entity) where TEntity : new() { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); TEntity result = new TEntity(); //copy all "field" properties foreach (var property in metadata.Properties) { property.SetEntityValue(result, property.GetEntityValue(entity)); } return(result); }
/// <summary> /// Compares IDs (.id) of two instances of entity. /// </summary> /// <param name="entity1">First entity.</param> /// <param name="entity2">Seconf entity.</param> /// <returns>True if ids are equal.</returns> public static bool IdEquals <TEntity>(this TEntity entity1, TEntity entity2) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); if (!metadata.HasIdProperty) { throw new InvalidOperationException(string.Format("Can not compare ids of entity which doesn't contains property for '{0}' field.", TikSpecialProperties.Id)); } string id1 = metadata.IdProperty.GetEntityValue(entity1); string id2 = metadata.IdProperty.GetEntityValue(entity2); return(string.Equals(id1, id2)); }
/// <summary> /// Deletes entity (.id is the key) on mikrotik router. /// </summary> /// <typeparam name="TEntity">Deleted entity type.</typeparam> /// <param name="connection">Tik connection used to delete entity.</param> /// <param name="entity">Entity to be deleted (.id property is the key)</param> public static void Delete <TEntity>(this ITikConnection connection, TEntity entity) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); EnsureNotReadonlyEntity(metadata); string id = metadata.IdProperty.GetEntityValue(entity); if (string.IsNullOrEmpty(id)) { throw new ArgumentException("Entity has no .id (entity is not loaded from mikrotik router)", "entity"); } ITikCommand cmd = connection.CreateCommandAndParameters(metadata.EntityPath + "/remove", TikCommandParameterFormat.NameValue, TikSpecialProperties.Id, id); cmd.ExecuteNonQuery(); }
/// <summary> /// Compares two instances of entity by their fields and returns different fields (field names). /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="entity1">First entity.</param> /// <param name="entity2">Seconf entity.</param> /// <param name="skipIdCompare">If is true, than coparation of .id property is skipped.</param> /// <returns>List of different fields.</returns> /// <remarks>Compares only fields marked with <see cref="TikPropertyAttribute"/>.</remarks> public static IEnumerable <string> GetDifferentFields <TEntity>(this TEntity entity1, TEntity entity2, bool skipIdCompare = false) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); foreach (var property in metadata.Properties) { if (!skipIdCompare || property.FieldName != TikSpecialProperties.Id) { string prop1 = property.GetEntityValue(entity1); string prop2 = property.GetEntityValue(entity2); if (!string.Equals(prop1, prop2)) { yield return(property.FieldName); } } } }
/// <summary> /// List version of <see cref="Save"/> method. Saves differences between given <paramref name="modifiedList"/> and <paramref name="unmodifiedList"/>. /// Typical usage is: Load, create list clone, modify list, save diferences. /// </summary> /// <example> /// var list = connection.LoadList{FirewallAddressList}(connection.CreateParameter("list", listName), connection.CreateParameter("address", ipAddress)); /// var listClonedBackup = list.CloneEntityList(); //creates clone of all entities in list /// list.Add(new FirewallAddressList() {Address = ipAddress, List = listName, }); //insert /// list[0].Comment = "test comment"; //update /// list.RemoveAt(1); //delete /// connection.SaveListDifferences(list, listClonedBackup); /// </example> /// <typeparam name="TEntity">Saved entitie type.</typeparam> /// <param name="connection">Tik connection used to save.</param> /// <param name="modifiedList">List with modifications.</param> /// <param name="unmodifiedList">Original (cloned) unmodified list.</param> /// <seealso cref="TikEntityObjectsExtensions.CloneEntity"/> /// <seealso cref="TikEntityObjectsExtensions.CloneEntityList"/> /// <seealso cref="Save"/> public static void SaveListDifferences <TEntity>(this ITikConnection connection, IEnumerable <TEntity> modifiedList, IEnumerable <TEntity> unmodifiedList) where TEntity : new() { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); EnsureNotReadonlyEntity(metadata); EnsureHasIdProperty(metadata); var idProperty = metadata.IdProperty; var entitiesToCreate = modifiedList.Where(entity => string.IsNullOrEmpty(idProperty.GetEntityValue(entity))).ToList(); // new items in modifiedList Dictionary <string, TEntity> modifiedEntities = modifiedList .Where(entity => !string.IsNullOrEmpty(idProperty.GetEntityValue(entity))) .ToDictionary(entity => idProperty.GetEntityValue(entity)); //all entities from modified list with ids Dictionary <string, TEntity> unmodifiedEntities = unmodifiedList //.Where(entity => !string.IsNullOrEmpty(idProperty.GetEntityValue(entity))) - entity in unmodified list has id (is loaded from miktrotik) .ToDictionary(entity => idProperty.GetEntityValue(entity)); //all entities from unmodified list with ids //DELETE foreach (string entityId in unmodifiedEntities.Keys.Where(id => !modifiedEntities.ContainsKey(id))) //missing in modified -> deleted { Delete(connection, unmodifiedEntities[entityId]); } //CREATE foreach (TEntity entity in entitiesToCreate) { Save(connection, entity); } //UPDATE foreach (string entityId in unmodifiedEntities.Keys.Where(id => modifiedEntities.ContainsKey(id))) // are in both modified and unmodified -> compare values (update/skip) { TEntity modifiedEntity = modifiedEntities[entityId]; TEntity unmodifiedEntity = unmodifiedEntities[entityId]; if (!modifiedEntity.EntityEquals(unmodifiedEntity)) { Save(connection, modifiedEntity); } } //TODO support for order! }
/// <summary> /// Moves given <paramref name="entityToMove"/> before given <paramref name="entityToMoveBefore"/>. /// </summary> /// <typeparam name="TEntity">Moved entity type.</typeparam> /// <param name="connection">Tik connection used to move entity.</param> /// <param name="entityToMove">Entity to be moved.</param> /// <param name="entityToMoveBefore">Entity before which is given <paramref name="entityToMove"/> moved.</param> public static void Move <TEntity>(this ITikConnection connection, TEntity entityToMove, TEntity entityToMoveBefore) { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); EnsureSupportsOrdering(metadata); string idToMove = metadata.IdProperty.GetEntityValue(entityToMove); string idToMoveBefore = entityToMoveBefore != null?metadata.IdProperty.GetEntityValue(entityToMoveBefore) : null; ITikCommand cmd = connection.CreateCommandAndParameters(metadata.EntityPath + "/move", TikCommandParameterFormat.NameValue, "numbers", idToMove); if (entityToMoveBefore != null) { cmd.AddParameter("destination", idToMoveBefore); } cmd.ExecuteNonQuery(); }
/// <summary> /// Saves entity to mikrotik router. Does insert (/add) whan entity has empty id and update(/set + /unset) when id is present). /// Behavior of save is modified via <see cref="TikPropertyAttribute"/> on properties. /// See <see cref="TikPropertyAttribute.DefaultValue"/>, <see cref="TikPropertyAttribute.UnsetOnDefault"/>. /// </summary> /// <typeparam name="TEntity">Saved entitie type.</typeparam> /// <param name="connection">Tik connection used to save.</param> /// <param name="entity">Saved entity.</param> /// <param name="usedFieldsFilter">List of field names (on mikrotik) which should be modified. If is not null, only listed fields will be modified.</param> public static void Save <TEntity>(this ITikConnection connection, TEntity entity, IEnumerable <string> usedFieldsFilter = null) where TEntity : new() { var metadata = TikEntityMetadataCache.GetMetadata <TEntity>(); EnsureNotReadonlyEntity(metadata); string id; if (metadata.IsSingleton) { id = null; } else { EnsureHasIdProperty(metadata); id = metadata.IdProperty.GetEntityValue(entity); } if (!metadata.IsSingleton && string.IsNullOrEmpty(id)) { //create ITikCommand createCmd = connection.CreateCommand(metadata.EntityPath + "/add", TikCommandParameterFormat.NameValue); foreach (var property in metadata.Properties .Where(pm => !pm.IsReadOnly) .Where(pm => usedFieldsFilter == null || usedFieldsFilter.Contains(pm.FieldName, StringComparer.OrdinalIgnoreCase))) { if (!property.HasDefaultValue(entity)) { createCmd.AddParameter(property.FieldName, property.GetEntityValue(entity)); } } id = createCmd.ExecuteScalar(); if (metadata.HasIdProperty) { metadata.IdProperty.SetEntityValue(entity, id); // update saved id into entity } } else { //update (set+unset) ITikCommand setCmd = connection.CreateCommand(metadata.EntityPath + "/set", TikCommandParameterFormat.NameValue); if (!metadata.IsSingleton && usedFieldsFilter == null) { //compare state on mikrotik and update different fields only var unmodifiedEntity = connection.LoadById <TEntity>(id); //TODO some kind of "loaded entities" session cache could be used to avoid another load before save. usedFieldsFilter = entity.GetDifferentFields(unmodifiedEntity); } List <string> fieldsToUnset = new List <string>(); foreach (var property in metadata.Properties .Where(pm => !pm.IsReadOnly) .Where(pm => usedFieldsFilter == null || usedFieldsFilter.Contains(pm.FieldName, StringComparer.OrdinalIgnoreCase))) { if (property.HasDefaultValue(entity) && property.UnsetOnDefault) { fieldsToUnset.Add(property.FieldName); } else { setCmd.AddParameter(property.FieldName, property.GetEntityValue(entity)); //full update (all values) } } if (fieldsToUnset.Count > 0) { // this should also work (see http://forum.mikrotik.com/viewtopic.php?t=28821 ) //ip/route/unset //=.id = *1 //= value-name=routing-mark foreach (string fld in fieldsToUnset) { ITikCommand unsetCmd = connection.CreateCommand(metadata.EntityPath + "/unset", TikCommandParameterFormat.NameValue); unsetCmd.AddParameter(TikSpecialProperties.Id, id, TikCommandParameterFormat.NameValue); unsetCmd.AddParameter(TikSpecialProperties.UnsetValueName, fld); unsetCmd.ExecuteNonQuery(); } } if (setCmd.Parameters.Any()) { if (!metadata.IsSingleton) { setCmd.AddParameter(TikSpecialProperties.Id, id, TikCommandParameterFormat.NameValue); } setCmd.ExecuteNonQuery(); } } }