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.
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
youu can use join to map it to more than table
Related
I've an old programm with edmx. Inside this one, I've linked a class (Table) To a View (Table/filter on a value of a column)
I want migrate this project to code first.
I copy/paste the project delete edmx file and generate models from an existing database.
All is good except this link.
<Table("JoinAffectation")>
partial public Class JointAffectation
public property Id as Long
public IdRecherche as Integer 'the link with my view
<NotMapped>
<ForeignKey("Id")>
PUBLIC OVERRIDABLE PROperty RechercheJoint as ViewRechercheJoint
But When I try to use function of automatical sort/filter using expression
I've error : The specified type member 'RechercheJoint' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
If I removed I error saying I don't same comumn and property... Also , How Can I stipulate RechercheJoint is mapped on IdRecherche
thanks for your help
Finally Using modelbuilder, I can join my view and my table like in edmx
<Table("JointAffectation")>
Partial Public Class JointAffectation
Public Property Id As Long
Public Property IdTypeJoint As Long
Public Property IdRecherche As Integer
Public Overridable Property JointType As JointType
<ForeignKey("Id")>
Public Overridable Property RechercheJoint As ViewRechercheJoint
End Class
<Table("ViewRechercheJoint")>
Partial Public Class ViewRechercheJoint
<Key>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Property Id As Integer
<StringLength(50)>
Public Property Libelle As String
<ForeignKey("IdRecherche")>
Public Overridable Property JointAffectations As ICollection(Of JointAffectation)
End Class
modelBuilder.Entity(Of JointAffectation)() _
.HasRequired(Function(e) e.RechercheJoint) _
.WithMany(Function(e) e.JointAffectations) _
.HasForeignKey(Function(e) e.IdRecherche)
Consider the following classes representing an Ordering system:
Public Class OrderBase
Public MustOverride Property OrderItem as OrderItemBase
End Class
Public Class OrderItemBase
End Class
Now, suppose we want to extend these classes to a more specific set of order classes, keeping the aggregate nature of OrderBase:
Public Class WebOrder
Inherits OrderBase
Public Overrides Property OrderItem as WebOrderItem
End Property
End Class
Public Class WebOrderItem
Inherits OrderItemBase
End Class
The Overriden property in the WebOrder class will cause an error stating that the return type is different from that defined in OrderBase... however, the return type is a subclass of the type defined in OrderBase. Why won't VB allow this?
You can't do that - it's changing the signature defined on the base. To do what you are trying to do you need to use generics:
Public Class OrderBase(Of T As IOrderItem)
Public ReadOnly Property OrderItems As IList(Of T)
End Class
My Visual Basic is rusty so hopefully that is accurate...
You cannot change the signature of your class upon overriding it. You can, however, return a derived type:
Public Overrides Property OrderItem() as OrderItemBase
Get
Return New WebOrderItem()
End Get
End Property
Public Sub Whatever()
Dim item As WebOrderItem = DirectCast(OrderItem, WebOrderItem)
End Sub
Alternatively, if you want to enforce the types more strictly, use generics with generic type constraints, as shown below:
Public MustInherit Class OrderBase(Of T As OrderItemBase)
Public MustOverride ReadOnly Property OrderItem() As T
End Class
Public Class OrderItemBase
End Class
Public Class WebOrder(Of T As WebOrderItem)
Inherits OrderBase(Of T)
Public Overrides ReadOnly Property OrderItem() As T
Get
Return New WebOrderItem()
End Get
End Property
End Class
Public Class WebOrderItem
Inherits OrderItemBase
End Class
Or do this if you don't want WebOrder to be a generic class as well:
Public Class WebOrder
Inherits OrderBase(Of WebOrderItem)
Public Overrides ReadOnly Property OrderItem() As WebOrderItem
Get
Return New WebOrderItem()
End Get
End Property
End Class
One approach is to have a protected overridable method, and then have a public non-overridable method which calls the overridable one. Any time the return value for the function in the derived class should change, have a notoverridable override of the overridable method call a new overridable method which returns the more refined type, and also shadow the earlier version of the public function with one that uses the new override. If vb.net allowed one class to both override and shadow the same member, things would be much cleaner, but there's no way to do that.
Public Class CarFactory
Protected Overridable Function DerivedMakeCar() as Car
' make a car
End Function
Public Function MakeCar() as Car
Return DerivedMakeCar()
End Function
End Class
Public Class FordFactory
Inherits CarFactory
Protected Overrides Function DerivedMakeCar() As Car
Return DerivedMakeFord()
End Function
Protected Overridable Function DerivedMakeFord() As Ford
' Make a Ford
End Function
Public Shadows Function MakeCar() As Ford
Return DerivedMakeFord()
End Function
End Class
A simpler alternative in some cases may be to have a public overridable MakeCar() function which always returns an object of type Car, but have a FordFactory also include a MakeFord() function which returns a Ford.
The overridden MakeCar() function would be NotOverridable and would simply call MakeFord. In some ways, the latter approach can be cleaner, but if there's a common naming convention (e.g. factories have a MakeProduct method which returns the most derived type) it may be useful to use Shadows.
I have some objects in EF using model first, most of the objects are related:
Partial Public Class PersonLog
Public Property Id As System.Guid
Public Property ExternalEmailGuid As System.Guid
Public Overridable Property Person As Person
End Class
Partial Public Class EmailIn
Public Property Id As System.Guid
Public Property PersonID As System.Guid
End Class
Partial Public Class EmailOut
Public Property Id As System.Guid
Public Property PersonID As System.Guid
End Class
Such that working with a Person object i can easily grab the associated PersonLogs
_person.PersonLogs.OrderByDescending(Function(c) c.CreateDate).ToList()
However i need to grab the associated EmailIn or EmailOut objects associated with that PersonLog. No association was added between them and i'm not quite sure why.
Is there a way to load all of the associated EmailIn & EmailOut objects without iterating through each PersonLog item and performing a lookup? The relationship between PersonLog and EmailIn or EmailOut is 1:1
The approach I was considering was creating a new class that takes the current context and PersonLog as constructors and has public properties PersonLog, EmailIn and EmailOut, like this:
Public Class PersonLogDTO
Public Sub New(obj As PersonLog, cxt As DB.DBContainer)
_obj = obj
_cxt = cxt
End Sub
Public ReadOnly Property LeadLog As PersonLog
Get
Return _obj
End Get
End Property
Public ReadOnly Property EmailOut As EmailOut
Get
Return _cxt.EmailOut.Find(_obj.ExternalEmailGuid)
End Get
End Property
Got to be a better way, right?
I'm toying around with a 3rd party library and something has me absolutely puzzled.
When I instantiate this class, immediately all of the properties of the class throw exceptions before any more code even runs. Why is this happening? It's not just this TypedSegmentPWK class, it's all of the typedsegment classes.. of which there are many.
Simple instantiation fails
Imports OopFactory.X12.Parsing
Imports OopFactory.X12.Parsing.Model
Imports OopFactory.X12.Parsing.Model.Typed
....
Dim test As New TypedSegmentPWK
test.PWK04_EntityIdentifierCode = "blah"
Assigning a value to PWK04_EntityIdentifierCode or any other property of test fails with a null reference exception.
TypedSegmentPWK:
Namespace OopFactory.X12.Parsing.Model.Typed
Public Class TypedSegmentPWK
Inherits TypedSegment
Public Sub New()
Public Property PWK01_ReportTypeCode As String
Public Property PWK02_ReportTransmissionCode As String
Public Property PWK03_ReportCopiesNeeded As Integer?
Public Property PWK04_EntityIdentiferCodeEnum As EntityIdentifierCode
Public Property PWK04_EntityIdentifierCode As String
Public Property PWK05_IdentificationCodeQualifier As String
Public Property PWK05_IdentificationCodeQualifierEnum As IdentificationCodeQualifier
Public Property PWK06_IdentificationCode As String
Public Property PWK07_Description As String
Public Property PWK08_ActionsIndicated As String
Public Property PWK09_RequestCategoryCode As String
End Class
End Namespace
TypedSegment:
Namespace OopFactory.X12.Parsing.Model
Public MustInherit Class TypedSegment
Protected Sub New(segmentId As String)
Public Event Initialized As EventHandler
Public Event Initializing As EventHandler
Protected Overridable Sub OnInitialized(e As EventArgs)
Protected Overridable Sub OnInitializing(e As EventArgs)
End Class
End Namespace
Full source here: https://github.com/KeyMarkInc/OopFactory.X12
All the properties reference _segment defined in TypedSegment, e.g.
public string PWK04_EntityIdentifierCode
{
get { return _segment.GetElement(4); }
set { _segment.SetElement(4, value); }
}
However, the _segment variable is not initialized until TypedSegment.Initialize(Container parent, X12DelimiterSet delimiters) is called...
internal void Initialize(Container parent, X12DelimiterSet delimiters)
{
OnInitializing(new EventArgs());
_segment = new Segment(parent, delimiters, _segmentId);
OnInitialized(new EventArgs());
}
This is an internal method, so presumably something in this framework is supposed to call it, and not you as the user. So, I would guess the answer is that you are using the TypedSegmentPWK class incorrectly, although I don't know what the correct way is.
I'm refactoring, and have run into a roadblock.
Background:
I have a base class and several inherited derived classes. The derived classes don't always need to have the same properties. If any properties are shared among the derived classes, those properties would live at the base class level ('Contents', for example).
Similarly, GoodDocument below has 'GoodThings' but would not want/need to have 'BadThings'.
I want to treat instances of both 'GoodDocument' and 'BadDocument' as type 'Document'
public mustinherit class Document
public property Contents as string
public sub new()...
end class
public class GoodDocument
inherits Document
public property GoodThings as string
public sub new()...
end class
public class BadDocument
inherits Document
public property BadThings as string
public sub new()...
end class
The 'DocumentWriter' class will also have several derived classes: ('GoodDocumentWriter' and 'BadDocumentWriter').
I need to pass around the DocumentWriter.Doc as a 'Document' to a number of other places in the code. Doc.GoodThings would only be called from within an instance of either 'GoodDocument' or 'GoodDocumentWriter'.
public mustinherit class DocumentWriter
public property Doc as Document
public sub new()...
end class
public class GoodDocumentWriter
inherits DocumentWriter
public sub new
mybase.Doc = new GoodDocument
end sub
end class
public class BadDocumentWriter
inherits DocumentWriter
public sub new
mybase.Doc = new BadDocument
end sub
end class
Question:
Is there a design pattern that allows for derived classes to have members that don't exist at the base class level?
Do all properties have to live at the base class level?
Revised
I was trying to be brief with my initial question and I made the mistake of over simplifying the situation. In short, I did realize that it should be possible to have different properties on each of the derived classes. (I typed that in a tongue-in-cheek manor and didn't mean to keep it in the final post).
I realize now that the problem that I was experiencing was really symptomatic of a larger issue which needed addressing.
It appears that I was encountering compiler complaints that could be corrected by further refactoring and looser coupling. While others answered the basic question that I posed, Ryan Gross' example really helped kick start some new ideas.
Thanks!
What you should do in this case is define the operations that can be performed on instances of Document in an interface. In your case maybe there is a WriteThings operation, so you would have:
public interface Writeable {
public sub WriteThings();
}
Then in your derived classes you would implement the method to utilize the internal data of the class. For example:
public mustinherit class Document implements Writeable
public property Contents as string
public sub new()...
public sub WriteThings();
end class
public class GoodDocument
inherits Document
public property GoodThings as string
public sub new()...
public sub WriteThings()
//Do something with GoodThings
end sub
end class
public class BadDocument
inherits Document
public property BadThings as string
public sub WriteThings()
//Do something with BadThings
end sub
public sub new()...
end class
Finally, client code that needs to call WriteThings accesses it through an interface:
public mustinherit class DocumentWriter
public property Doc as Writable
public sub new()...
public sub PerformWrite()
Doc.WriteThings();
end sub
end class
It is generally not a good idea to build several parallel class hierarchies. In this case, one DocumentWriter class should be able to write any class that implements Writeable by invoking its WriteThings method.
If all the properties live at the base class level, then I'm not sure what the point of a derived class would be. :) You'd be able to do everything with the base class.
So, yes. If something is applicable only to GoodDocument and not to Document, then it should be in GoodDocument.
To answer your question specifically:
Yes, you just create multiple layers in the inheritance hierarchy: You have a base class, and then many two “branches” (good and bad, to use your terminology). Any properties that are only relevant to either branch, you declare in the class that inherits from the base class. Those properties will only be visible to that class and any classes inheriting from it.
No, properties can be declared anywhere within your inheritance hierarchy.