Ecco il mio primo post, nel mio primo blog...
Oggi volevo trattare con voi Parsing dei file di Mapping creati per NHibernate.
Innanzitutto, a cosa può servire? Nel mio caso ad esempio per effettuare delle ricerche utilizzando le expression di NHibernate in modo tale da poter generalizzare un form che, passando una entity specifica, mi indichi tutte le proprietà ad essa associate.
Iniziamo dunque dalla prima classe, presa direttamente dalla seguente pagina web :
http://weblogs.domain-driven.net/default,date,2006-09-25.aspx
Quindi convertiamo la classe in VB.NET e successivamente l’estendiamo.
TypeMappingParser.vb
Imports System
Imports System.Collections
Imports System.Reflection
Imports System.Reflection.Emit
Imports NHibernate
Imports NHibernate.Metadata
Imports NHibernate.Type
Imports NHibernate.Bytecode
Imports NHibernate.Mapping
Imports NHibernate.Loader
Public Enum AssociationType
OneToOne
OneToMany
ManyToOne
Component
End Enum
Public Class TypeMappingParser
Private _typeMappings As Hashtable
Public Sub New(ByVal factory As ISessionFactory)
ParseMappings(factory)
End Sub
Public Function GetTypeMappingInfo(ByVal typeName As String) As TypeMappingInfo
Return CType(_typeMappings(typeName), TypeMappingInfo)
End Function
Private Sub ParseMappings(ByVal factory As ISessionFactory)
_typeMappings = New Hashtable()
IdentifyMappedTypes(factory)
ParseMappedTypes(factory)
End Sub
Private Sub IdentifyMappedTypes(ByVal factory As ISessionFactory)
For Each type As Type In factory.GetAllClassMetadata().Keys
'Dim icmd As IClassMetadata = factory.GetClassMetadata(type)
Dim typeMappingInfo As TypeMappingInfo = New TypeMappingInfo(type)
_typeMappings.Add(type.Name, typeMappingInfo)
Next
End Sub
Private Sub ParseMappedTypes(ByVal factory As ISessionFactory)
For Each type As Type In factory.GetAllClassMetadata().Keys
ParsePropertiesAndAssociations(factory, type)
Next
End Sub
Private Sub ParsePropertiesAndAssociations(ByVal factory As ISessionFactory, ByVal type As Type)
Dim propertyIndex As Integer = 0
Dim classMeta As IClassMetadata = CType(factory.GetAllClassMetadata()(type), IClassMetadata)
Dim containingTypeMappingInfo As TypeMappingInfo = GetTypeMappingInfo(type.Name)
For Each propertyType As IType In classMeta.PropertyTypes
If (propertyType.IsAssociationType) Then
ParseEntity(factory, containingTypeMappingInfo, propertyType, classMeta.PropertyNames(propertyIndex))
ElseIf (propertyType.IsComponentType) Then
ParseComponentType(factory, containingTypeMappingInfo, propertyType, classMeta.PropertyNames(propertyIndex))
Else
ParseValue(containingTypeMappingInfo, propertyType, classMeta.PropertyNames(propertyIndex))
End If
propertyIndex += 1
Next
End Sub
Private Sub ParseComponentType(ByVal factory As ISessionFactory, ByVal containingTypeInfo As TypeMappingInfo, ByVal propertyType As IType, ByVal propertyName As String)
If (Not _typeMappings.Contains(propertyType.Name)) Then
Dim desc As TypeMappingInfo = New TypeMappingInfo(propertyType.ReturnedClass)
_typeMappings.Add(propertyType.Name, desc)
Dim entityPropInfo As New EntityPropertyInfo( _
containingTypeInfo, _
propertyType.ReturnedClass, _
propertyName, _
GetAssociationType(propertyType, False))
'entityPropInfo.TypeMappingInfo = desc
containingTypeInfo.AddProperty(entityPropInfo)
Dim compType As IAbstractComponentType = CType(propertyType, IAbstractComponentType)
For index As Integer = 0 To compType.PropertyNames.Length - 1
Dim subType As IType = compType.Subtypes(index)
If (subType.IsAssociationType) Then
ParseEntity(factory, desc, subType, compType.PropertyNames(index))
ElseIf (subType.IsComponentType) Then
ParseComponentType(factory, desc, subType, compType.PropertyNames(index))
Else
ParseValue(desc, subType, compType.PropertyNames(index))
End If
Next
Else
containingTypeInfo.AddProperty( _
New EntityPropertyInfo(containingTypeInfo, _
propertyType.ReturnedClass, _
propertyName, _
GetAssociationType(propertyType, False)))
End If
End Sub
Private Sub ParseValue(ByVal containingTypeMappingInfo As TypeMappingInfo, ByVal propertyType As IType, ByVal propertyName As String)
Dim info As ValuePropertyInfo = New ValuePropertyInfo(containingTypeMappingInfo, _
propertyType.ReturnedClass, _
propertyName)
containingTypeMappingInfo.AddProperty(info)
End Sub
Private Sub ParseEntity(ByVal factory As ISessionFactory, ByVal containingTypeInfo As TypeMappingInfo, _
ByVal propertyType As IType, ByVal propertyName As String)
If (propertyType.IsEntityType) Then
Dim info As EntityPropertyInfo = _
New EntityPropertyInfo(containingTypeInfo, _
propertyType.ReturnedClass, _
propertyName, _
GetAssociationType(propertyType, False))
containingTypeInfo.AddProperty(info)
Else
Dim collectionMeta As ICollectionMetadata = _
factory.GetCollectionMetadata((CType(propertyType, CollectionType)).Role)
If (TypeOf collectionMeta.ElementType Is ComponentType) Then
Dim componentType As ComponentType = CType(collectionMeta.ElementType, ComponentType)
Dim info As EntityPropertyInfo = _
New EntityPropertyInfo(containingTypeInfo, _
componentType.ReturnedClass, _
propertyName, _
GetAssociationType(componentType, True))
containingTypeInfo.AddProperty(info)
ElseIf (TypeOf collectionMeta.ElementType Is ManyToOneType) Then
Dim info As EntityPropertyInfo = New EntityPropertyInfo( _
containingTypeInfo, _
collectionMeta.ElementType.ReturnedClass, _
propertyName, _
GetAssociationType(collectionMeta.ElementType, True))
containingTypeInfo.AddProperty(info)
Else
Throw New ApplicationException("Collection type " & collectionMeta.ElementType.Name & " was not recognized")
End If
End If
End Sub
Private Function GetAssociationType(ByVal type As IType, ByVal isCollection As Boolean) As AssociationType
If (TypeOf type Is ManyToOneType AndAlso isCollection) Then
Return AssociationType.OneToMany
ElseIf (TypeOf type Is ManyToOneType) Then
Return AssociationType.ManyToOne
ElseIf (TypeOf type Is OneToOneType) Then
Return AssociationType.OneToOne
Else
Return AssociationType.OneToOne
End If
End Function
End Class
Si può notare che, nel costruttore della classe si passi l'ISessionFactory; tramite questa semplice interfaccia, disponibile in NHibernate, si riesce a sapere qualunque cosa di qualunque entity mappata correttamente nel nostro assembly.
Inizierà dunque il parsing veri e proprio costruendosi una struttura identificando da prima le varie entity e quindi le varie proprietà di ognuna di esse stabilendo le eventuali relazioni tra i vari oggetti.
Sucessivamente per avere le informazioni dell'entity A, ad esempio, basta richiamare il metodo GetTypeMappingInfo("A") il quale restituirà un oggetto di tipo TypeMappingInfo
TypeMappingInfo.vb
Public Class TypeMappingInfo
Private _type As Type
Private _listProperty As IList(Of IPropertyInfo)
Sub New(ByVal type As Type)
Me._type = type
Me._listProperty = New List(Of IPropertyInfo)
End Sub
Public Sub AddProperty(ByVal obj As IPropertyInfo)
Me._listProperty.Add(obj)
End Sub
Public ReadOnly Property ListProperty() As IList(Of IPropertyInfo)
Get
Return Me._listProperty
End Get
End Property
Public ReadOnly Property TypeClass() As System.Type
Get
Return Me._type
End Get
End Property
Public Overrides Function ToString() As String
Return Me._type.Name
End Function
End Class
Questa classe è il contenitore delle informazioni dell'entity richiesta. Oltretutto tramite la property TypeClass posso creare nuovi oggetti del tipo indicato.
Nel prossimo post vedo di allegare anche il resto del codice, con una semplice spiegazione del funzionamento dello stesso e del suo possibile utilizzo.
Un saluto a tutti
Alberto B.B.