设置和动态扩展表差不多,添加了一个模板hbm.xml文件,用于创建动态hbm.xml,HibernateUtil无改动。
MappingManger添加了两个方法
1 public static void UpdateClassMapping(DynamicTestModel dynamicModel) 2 { 3 var session = HibernateUtil.Instance.CurrentSession; 4 var fileName = "hbm_template/Template.hbm.xml"; 5 var file = File.ReadAllText(fileName); 6 file = file.Replace("@assembly", dynamicModel.GetType().Assembly.GetName().Name) 7 .Replace("@namespace", dynamicModel.GetType().Namespace) 8 .Replace("@class", dynamicModel.EntityName) 9 .Replace("@table", "Dynamic_" + dynamicModel.EntityName); 10 11 var xDocument = new XmlDocument(); 12 xDocument.LoadXml(file); 13 14 var dynamicElements = xDocument.DocumentElement.GetElementsByTagName("dynamic-component"); 15 XmlElement dynamicElement = null; 16 if (dynamicElements.Count > 0) 17 { 18 dynamicElements[0].InnerXml = string.Empty; 19 dynamicElement = dynamicElements[0] as XmlElement; 20 } 21 22 foreach (DictionaryEntry property in dynamicModel.CustomProperties) 23 { 24 var newElement = CreatePropertyElement(xDocument, dynamicElement.NamespaceURI, property); 25 dynamicElement.AppendChild(newElement); 26 } 27 28 Console.WriteLine(xDocument.OuterXml); 29 xDocument.Save("hbm/" + dynamicModel.EntityName + ".hbm.xml"); 30 } 31 32 private static XmlElement CreatePropertyElement(XmlDocument document, string parentNamespace, DictionaryEntry property) 33 { 34 var element = document.CreateElement("property", parentNamespace); 35 36 element.SetAttribute("name", property.Key as string); 37 element.SetAttribute("column", property.Key as string); 38 element.SetAttribute("type", property.Value.GetType().Name); 39 element.SetAttribute("not-null", "false"); 40 return element; 41 }
CustomizableEntity类添加了一个属性
public string auto_id { get; set; }
创建类DynamicTestModel
1 namespace DynamicTableTest.DynamicTable 2 { 3 public class DynamicTestModel : CustomizableEntity 4 { 5 public string EntityName { get; set; } 6 7 8 public DynamicTestModel(string entityName) 9 { 10 // TODO: Complete member initialization 11 this.EntityName = entityName; 12 } 13 } 14 }
添加DynamicEntityTuplizer
1 using NHibernate; 2 using NHibernate.Mapping; 3 using NHibernate.Properties; 4 using NHibernate.Proxy; 5 using NHibernate.Proxy.Map; 6 using NHibernate.Tuple; 7 using NHibernate.Tuple.Entity; 8 using System.Collections.Generic; 9 10 namespace DynamicTableTest.DynamicTable 11 { 12 public class DynamicEntityTuplizer : AbstractEntityTuplizer 13 { 14 private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(PocoEntityTuplizer)); 15 16 private static readonly DynamicModelAccesser Accessor = new DynamicModelAccesser(); 17 private sealed class DynamicModelMapInstantiator : IInstantiator 18 { 19 private readonly string entityName; 20 private readonly HashSet<string> isInstanceEntityNames = new HashSet<string>(); 21 22 private PersistentClass mappingInfo; 23 24 public DynamicModelMapInstantiator() 25 { 26 entityName = null; 27 } 28 29 public DynamicModelMapInstantiator(PersistentClass mappingInfo) 30 { 31 this.mappingInfo = mappingInfo; 32 entityName = mappingInfo.EntityName; 33 isInstanceEntityNames.Add(entityName); 34 if (mappingInfo.HasSubclasses) 35 { 36 foreach (PersistentClass subclassInfo in mappingInfo.SubclassClosureIterator) 37 isInstanceEntityNames.Add(subclassInfo.EntityName); 38 } 39 } 40 41 public object Instantiate(object id) 42 { 43 return Instantiate(); 44 } 45 46 public object Instantiate() 47 { 48 var model = new DynamicTestModel(entityName); 49 return model; 50 } 51 52 public bool IsInstance(object obj) 53 { 54 var that = obj as DynamicTestModel; 55 if (that != null) 56 { 57 if (entityName == null) 58 { 59 return true; 60 } 61 string type = that.EntityName; 62 return type == null || isInstanceEntityNames.Contains(type); 63 } 64 else 65 { 66 return false; 67 } 68 } 69 } 70 71 public DynamicEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappingInfo) 72 : base(entityMetamodel, mappingInfo) 73 { 74 // NH different behavior fo NH-1587 75 Instantiator = BuildInstantiator(mappingInfo); 76 } 77 78 public override System.Type ConcreteProxyClass 79 { 80 get { return typeof(DynamicTestModel); } 81 } 82 83 public override bool IsInstrumented 84 { 85 get { return false; } 86 } 87 88 public override System.Type MappedClass 89 { 90 get { return typeof(DynamicTestModel); } 91 } 92 93 public override EntityMode EntityMode 94 { 95 get { return EntityMode.Map; } 96 } 97 98 protected override IGetter BuildPropertyGetter(NHibernate.Mapping.Property mappedProperty, PersistentClass mappedEntity) 99 { 100 return BuildPropertyAccessor(mappedProperty).GetGetter(null, mappedProperty.Name); 101 } 102 103 104 105 private IPropertyAccessor BuildPropertyAccessor(NHibernate.Mapping.Property property) 106 { 107 return Accessor; 108 } 109 110 protected override ISetter BuildPropertySetter(NHibernate.Mapping.Property mappedProperty, PersistentClass mappedEntity) 111 { 112 return BuildPropertyAccessor(mappedProperty).GetSetter(null, mappedProperty.Name); 113 } 114 115 protected override IInstantiator BuildInstantiator(PersistentClass mappingInfo) 116 { 117 return new DynamicModelMapInstantiator(mappingInfo); // new DynamicMapInstantiator(mappingInfo); 118 } 119 120 protected override IProxyFactory BuildProxyFactory(PersistentClass mappingInfo, IGetter idGetter, 121 ISetter idSetter) 122 { 123 IProxyFactory pf = new MapProxyFactory(); 124 try 125 { 126 //TODO: design new lifecycle for ProxyFactory 127 pf.PostInstantiate(EntityName, null, null, null, null, null); 128 } 129 catch (HibernateException he) 130 { 131 log.Warn("could not create proxy factory for:" + EntityName, he); 132 pf = null; 133 } 134 return pf; 135 } 136 } 137 }
DynamicInterceptor
1 using NHibernate; 2 using NHibernate.Mapping; 3 using System.Collections.Generic; 4 5 namespace DynamicTableTest.DynamicTable 6 { 7 public class DynamicInterceptor : EmptyInterceptor 8 { 9 private readonly string entityName; 10 private readonly HashSet<string> isInstanceEntityNames = new HashSet<string>(); 11 12 public DynamicInterceptor() 13 { 14 entityName = null; 15 } 16 17 public DynamicInterceptor(PersistentClass mappingInfo) 18 { 19 entityName = mappingInfo.EntityName; 20 isInstanceEntityNames.Add(entityName); 21 if (mappingInfo.HasSubclasses) 22 { 23 foreach (PersistentClass subclassInfo in mappingInfo.SubclassClosureIterator) 24 isInstanceEntityNames.Add(subclassInfo.EntityName); 25 } 26 } 27 28 public override string GetEntityName(object entity) 29 { 30 var dynamicEntity = entity as DynamicTestModel; 31 if (dynamicEntity == null) { 32 return base.GetEntityName(entity); 33 } 34 else 35 { 36 return dynamicEntity.EntityName; 37 } 38 } 39 } 40 }
DynamicModelAccesser
1 using NHibernate.Engine; 2 using NHibernate.Properties; 3 using System; 4 using System.Collections; 5 using System.Reflection; 6 7 namespace DynamicTableTest.DynamicTable 8 { 9 public class DynamicModelAccesser : IPropertyAccessor 10 { 11 private static readonly string CUSTOM_PROPERTY_NAME = "CustomProperties"; 12 private static readonly string AUTO_ID_NAME = "auto_id"; 13 14 public IGetter GetGetter(System.Type theClass, string propertyName) 15 { 16 return new DynamicGetter(propertyName); 17 } 18 19 public ISetter GetSetter(System.Type theClass, string propertyName) 20 { 21 return new DynamicSetter(propertyName); 22 } 23 24 public bool CanAccessThroughReflectionOptimizer 25 { 26 get { return false; } 27 } 28 29 [Serializable] 30 public sealed class DynamicSetter : ISetter 31 { 32 private readonly string name; 33 34 internal DynamicSetter(string name) 35 { 36 this.name = name; 37 } 38 39 public MethodInfo Method 40 { 41 get { return null; } 42 } 43 44 public string PropertyName 45 { 46 get { return null; } 47 } 48 49 public void Set(object target, object value) 50 { 51 if (CUSTOM_PROPERTY_NAME.Equals(name)) 52 { 53 (target as DynamicTestModel).CustomProperties = (IDictionary)value; 54 } 55 else if (AUTO_ID_NAME.Equals(name)) 56 { 57 (target as DynamicTestModel).auto_id = value as string; 58 }
59 else
60 { 61 (target as DynamicTestModel).CustomProperties[name] = value;
62 } 63 } 64 } 65 66 [Serializable] 67 public sealed class DynamicGetter : IGetter 68 { 69 private readonly string name; 70 71 internal DynamicGetter(string name) 72 { 73 this.name = name; 74 } 75 76 public MethodInfo Method 77 { 78 get { return null; } 79 } 80 81 public object GetForInsert(object owner, IDictionary mergeMap, ISessionImplementor session) 82 { 83 return Get(owner); 84 } 85 86 public string PropertyName 87 { 88 get { return null; } 89 } 90 91 public System.Type ReturnType 92 { 93 get { return typeof(object); } 94 } 95 96 public object Get(object target) 97 { 98 if (CUSTOM_PROPERTY_NAME.Equals(name)) 99 { 100 return (target as DynamicTestModel).CustomProperties; 101 } 102 else if (AUTO_ID_NAME.Equals(name)) 103 { 104 return (target as DynamicTestModel).auto_id; 105 } 106 return (target as DynamicTestModel).CustomProperties[name]; 107 } 108 } 109 } 110 }
模板hbm.xml
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping 3 xmlns="urn:nhibernate-mapping-2.2" 4 assembly="@assembly" 5 namespace="@namespace" 6 auto-import="true" 7 default-access="property" 8 default-cascade="none" 9 default-lazy="true"> 10 11 <class entity-name="@class" table="@table"> 12 <tuplizer class="DynamicEntityTuplizer" entity-mode="dynamic-map" /> 13 <id name="auto_id" column="auto_id" type="string"> 14 <generator class="uuid.hex" /> 15 </id> 16 <dynamic-component insert="true" name="CustomProperties" optimistic-lock="true" unique="false" update="true"> 17 </dynamic-component> 18 </class> 19 </hibernate-mapping>
最后是测试代码,QueryOver中where在这里会有问题,改用hql语句实现了
1 using DynamicTableTest.DynamicTable; 2 using System; 3 using System.Linq; 4 5 namespace DynamicTableTest 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 DynamicModelMethod(); 12 13 Console.Read(); 14 } 15 16 private static void DynamicModelMethod() 17 { 18 var entityName = "Test_Role"; 19 var columnName = "Name"; 20 var columnRole = "Role"; 21 var columnCreateTime = "Create_Time"; 22 23 var dynamicEntity = new DynamicTestModel(entityName); 24 var name = Guid.NewGuid().ToString(); 25 dynamicEntity.SetValueOfCustomField(columnName, name); 26 dynamicEntity.SetValueOfCustomField(columnRole, 31); 27 dynamicEntity.SetValueOfCustomField(columnCreateTime, DateTime.Now); 28 29 MappingManager.UpdateClassMapping(dynamicEntity); 30 HibernateUtil.Instance.Reset(); 31 32 var session = HibernateUtil.Instance.CurrentSession; 33 34 var trans = session.BeginTransaction(); 35 try 36 { 37 //add 38 var id = session.Save(entityName, dynamicEntity); 39 Console.WriteLine("first name: " + name); 40 41 //get 42 var role = (DynamicTestModel)session.Get(entityName, id); 43 var storedName = role.GetValueOfCustomField(columnName); 44 Console.WriteLine("second name: "+storedName); 45 46 //update 47 var newName = Guid.NewGuid().ToString(); 48 role.SetValueOfCustomField(columnName, newName); 49 session.SaveOrUpdate(entityName, role); 50 51 //get 52 role = (DynamicTestModel)session.Get(entityName, id); 53 storedName = role.GetValueOfCustomField(columnName); 54 Console.WriteLine("third name: " + storedName); 55 56 //delete 57 session.Delete(entityName, role); 58 role = (DynamicTestModel)session.Get(entityName, id); 59 Console.WriteLine(role == null); 60 61 var roles = session.QueryOver<DynamicTestModel>(entityName).List(); 62 foreach (var roleItem in roles) 63 { 64 Console.WriteLine(roleItem.ToString()); 65 } 66 Console.WriteLine("###########################################"); 67 roles = session.CreateQuery(string.Format("from {0} where {1}='{2}'", entityName, columnName, "abc")).Enumerable<DynamicTestModel>().ToList(); 68 69 foreach (var roleItem in roles) 70 { 71 Console.WriteLine(roleItem.ToString()); 72 } 73 74 trans.Commit(); 75 } 76 catch (Exception ex) 77 { 78 trans.Rollback(); 79 Console.WriteLine(ex.ToString()); 80 } 81 } 82 } 83 }
Over!