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);
}
}
}
On Tuesday, April 3, 2012 12:15:18 AM UTC+2, barry schulz wrote:
> I was able to add a dynamic-component and populate that component with the
> correct properties at runtime by using the following link as a starting
> point.
> http://www.junasoftware.com/blog/custom-domain-using-nhibernate-dynam...
> You can still use the by code configuration to setup your dynamic
> component. Notice the anonymous type has no properties (because they are
> not known).
> mapper.Class<Test>(map =>
> {
> map.Component("DynamicAttributes", new { },componentMap =>
> {
> componentMap.Access(Accessor.NoSetter); // I do not have a
> setter
> });
> });
> After you add your mappings to the Configuration, add the code seen in
> AddMappingExtensions (from the link). You'll have to modify it based on how
> you get your dynamic attribute metadata.
> Example:
> foreach (var classMapping in config.ClassMappings)
> {
> // only do this for the class named Test for testing...
> if (classMapping.EntityName != "YourCoolNamepsace.Test")
> {
> continue;
> }
> var componentProperty = classMapping.GetProperty("DynamicAttributes");
> var component = (Component)componentProperty.Value;
> //foreach (TODO: get user defined attributes from somewhere)
> {
> // CreateDynamicProperty is hard coded to work with string for testing...
> component.AddProperty(CreateDynamicProperty("SomeDynamicAttribute", classMapping));
> component.AddProperty(CreateDynamicProperty(
> "SomeOtherDynamicAttribute", classMapping)); } }