Fabio: Basicly I want to change the mapping of a dynamic component
because the database schema has changed, and do it at runtime. So I want
to add properties to the dynamic component in a for-loop.
Barry: Good find, didn't find that link so I implemented my own version,
it uses a template which I map by code and then I copy the template to
my variable number of attributes. This code works in my limited case,
but is not very general I suspect. Code is below.
modelMapper.Class<TestClass>(c =>
{
c.Table("TestClass");
...
c.Component(x => x.Properties,
new
{
TEMPLATE = ""
}
, dc =>
{
dc.Property(arg => arg.TEMPLATE);
});
});
...
HbmMapping mapping = modelMapper.CompileMappingForAllExplicitlyAddedEntities();
var dynamicComponentMappingChanger = new NHibernateDynamicComponentMappingChanger();
dynamicComponentMappingChanger.ClassName = "TestClass";
dynamicComponentMappingChanger.PropertyName = "Properties";
dynamicComponentMappingChanger.AddMappingChange("MyAttribute1", "TEMPLATE");
dynamicComponentMappingChanger.AddMappingChange("MyAttribute2", "TEMPLATE");
dynamicComponentMappingChanger.AddMappingChange("MyAttribute3", "TEMPLATE");
dynamicComponentMappingChanger.ChangeMapping(mapping);
public class NHibernateDynamicComponentMappingChanger
{
public string ClassName { get; set; }
public string PropertyName { get; set; }
private Dictionary<string, List<string>>
_mappingToChange = new Dictionary<string, List<string>>();
/// <summary>
/// First string is new property name, second string is template that is already mapped
/// </summary>
public void AddMappingChange(string newPropertyName, string nameOfPropertyToUseAsTemplate)
{
_mappingToChange.Add(nameOfPropertyToUseAsTemplate, newPropertyName);
}
public void ChangeMapping(HbmMapping mapping)
{
HbmClass hbmClass = mapping.RootClasses.Single(x => x.Name == ClassName);
HbmDynamicComponent dynamicComponent = (HbmDynamicComponent)
hbmClass.Properties.Single(x => x.Name == PropertyName);
object[] items = dynamicComponent.Items;
List<object> result = new List<object>();
foreach(Object o in items)
{
HbmProperty hbmProperty = o as HbmProperty;
if(hbmProperty != null)
{
List<string> list;
if(_mappingToChange.TryGetValue(hbmProperty.name, out list))
{
foreach(string s in list)
{
HbmProperty clone = ObjectCopier.Clone(hbmProperty);
clone.name = s;
result.Add(clone);
}
}
else
{
result.Add(hbmProperty);
}
} else
{
result.Add(o);
}
}
dynamicComponent.Items = result.ToArray();
}
}
/// <summary>
/// Extension functions that makes it possible to have multiple
values for a key in a Dictionary. Dictionary value type should be of
type List<>
/// </summary>
public static class MultimapExt
{
public static void Add<TKey, TValue, TCollection>(
this IDictionary<TKey, TCollection> dictionary,
TKey key,
TValue value
) where TCollection : ICollection<TValue>, new()
{
TCollection collection;
if(!dictionary.TryGetValue(key, out collection))
{
collection = new TCollection();
dictionary.Add(key, collection);
}
collection.Add(value);
}
public static bool Remove<TKey, TValue, TCollection>(
this IDictionary<TKey, TCollection> dictionary,
TKey key,
TValue value
) where TCollection : ICollection<TValue>
{
TCollection collection;
if(dictionary.TryGetValue(key, out collection))
{
bool removed = collection.Remove(value);
if(collection.Count == 0)
dictionary.Remove(key);
return removed;
}
return false;
}
}
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if(!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don't serialize a null object, simply return the default for that object
if(Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using(stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);