4

I'm creating a domain model where entities often (but not always) have a member of type ActionLog.

ActionLog is a simple class which allows for an audit trail of actions being performed on an instance. Each action is recorded as an ActionLogEntry instance.

ActionLog is implemented (approximately) as follows:

public class ActionLog
{   
    public IEnumerable<ActionLogEntry> Entries
    {
        get { return EntriesCollection; }
    }

    protected ICollection<ActionLogEntry> EntriesCollection { get; set; }

    public void AddAction(string action)
    {
        // Append to entries collection.
    }
}

What I would like is to re-use this class amongst my entities and have the entries map to different tables based on which class they are logged against. For example:

public class Customer
{
    public ActionLog Actions { get; protected set; }
}

public class Order
{
    public ActionLog Actions { get; protected set; }
}

This design is suitable for me in the application, however I can't see a clear way to map this scenario to a database with NHibernate.

I typically use Fluent NHibernate for my configuration, but I'm happy to accept answers in more general HBM xml.

5
  • Is there a particular reason you want each type of action to go to a separate table? Commented Mar 15, 2010 at 17:24
  • It's a more natural fit for a relational structure. Relationally, you have Customers and CustomerActionLogEntries tables. Parallel to this you have Orders and OrderActionLogEntries tables. Currently my solution is to have a single ActionLogEntries table and use joining tables with Many-to-Many relationships in NHibernate. It's a bit too normalised for my tastes, requiring joins all over the place to get any meaningful data. Commented Mar 16, 2010 at 9:33
  • Did you ever get resolution for this? I'm about to post the same question. Commented Dec 26, 2011 at 21:57
  • I took the path of least-resistance and used the single table (to model the single type) with numerous joining tables (to model the relationships). Commented Dec 28, 2011 at 9:46
  • Thanks for the response. I found a solution that let me utilize my existing setup. Let me know what you think. Commented Dec 28, 2011 at 22:25

2 Answers 2

3

I was having the same problem and was about the post the same question hoping for an answer - but I found the solution with the help of the NH IRC channel on FreeNode.

My scenario has a Document. Various things will have Documents - like Reports, Items, etc. The only difference between Report.Documents and Item.Documents is that the document has a reference to its owner, and it is mapped to a different table.

The solution for this situation is mostly accomplished through .Net. Though - I don't think this solution would be possible with XML mappings.

The Document Class:

Public Class Document
    Public Overridable Property DocumentId As Integer
    Public Overridable Property Directory As String
    Public Overridable Property Name As String
    Public Overridable Property Title As String
    Public Overridable Property Revision As String
    Public Overridable Property Description As String
    Public Overridable Property Owner As String
    Public Overridable Property UploadedBy As String
    Public Overridable Property CreationDate As Date
    Public Overridable Property UploadDate As Date
    Public Overridable Property Size As Int64
    Public Overridable Property Categories As String
End Class

Then we inherit from this class for each of our additional Document types:

Public Class ReportDocument
    Inherits Document
    Public Overridable Property Report As Report
End Class

Public Class ItemDocument
    Inherits Document
    Public Overridable Property Item As Item
End Class

Here's where the "magic" happens. We're going to create a generic mapping that requires that the object being used inherits the Document class. This way, Fluent NHibernate can still find all the properties on the objects that inherit from the Document.

Public Class GenericDocumentMapping(Of T As Document)
    Inherits ClassMap(Of T)
    Public Sub New()
        Id(Function(x) x.DocumentId)
        Map(Function(x) x.Directory)
        Map(Function(x) x.Name)
        Map(Function(x) x.Title).Not.Nullable()
        Map(Function(x) x.Revision)
        Map(Function(x) x.Description)
        Map(Function(x) x.Owner)
        Map(Function(x) x.UploadedBy)
        Map(Function(x) x.CreationDate).Not.Nullable()
        Map(Function(x) x.UploadDate).Not.Nullable()
        Map(Function(x) x.Size)
        Map(Function(x) x.Categories)
    End Sub
End Class

You'll notice that this class has no reference to which table it is being mapped to, nor the parent object that each different version will use. Now, we use this generic mapping for each of our special types, and specify the table and map the parent object we created in each class type we created.

Public Class ReportDocumentMapping
    Inherits GenericDocumentMapping(Of ReportDocument)
    Public Sub New()
        MyBase.New()
        References(Function(x) x.Item).Column("ReportID")
        Table("ReportDocuments")
    End Sub
End Class

Public Class ItemDocumentMapping
    Inherits GenericDocumentMapping(Of ItemDocument)
    Public Sub New()
        MyBase.New()
        References(Function(x) x.Item).Column("ItemID")
        Table("ItemDocuments")
    End Sub
End Class

I think this method reduces a lot of code. Now, if you want to make sweeping changes to the document type - you only have to modify the Document class, and the GenericDocumentMapping class.

In my situation - I also just map Documents to a specific table. This is done the same way as the others - inherit from the GenericDocumentMapping and specify the table. The only difference is I don't reference a parent object.

Public Class DocumentMapping
    Inherits GenericDocumentMapping(Of Document)
    Public Sub New()
        MyBase.New()
        Table("Documents")
    End Sub
End Class
Sign up to request clarification or add additional context in comments.

1 Comment

This answer presents a reasonable compromise and quite a clever approach to the problem.
0

youu can use join to map it to more than table

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.