Entity Framework load related objects without available navigation property (DTO)? - vb.net

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?

Related

How to link a view to a model in vb.net code First

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)

Repeater control with strongly typed objects entity framework

I am new to entity framework, and wondered if someone could help. I have 2 classes that define documents and then their versions (1 to many relationship).
I am using the asp:repeater to display the list of documents, but cannot access information re the versions.
The 2 classes are below
Public Class Document
<Key>
Public Property ID As Guid
Public Property Name As String
Public Property Description As String
Public Property CreationDate As Date
Public Property ReviewDate As Date
Public Property NextReview As Date
Public Overridable Property Versions As ICollection(Of DocumentVersion)
End Class
Public Class DocumentVersion
<Key>
Public Property VersionID As Guid
<ForeignKey("Document")>
Public Property DocumentID As Guid
Public Property FilePath As String
Public Property CreationDate As Date
Public Overridable Property Document As Document
End Class
In the asp:repeater I'm using <%#: Item.Name %> to display the document name, but cannot access <%#: Item.Versions.FilePath %> for example.
If I do include item.versions.filepath I get the error:
BC30456: 'FilePath' is not a member of 'ICollection(Of DocumentVersion)'.
Any help would be very appreciated.

Entity Framework Core - Audit

Is there a way to write an Audit log automatically?
I have created a DbSet(Of Protocols)
Here is my protocol class:
Public Class Protocol
Inherits ModelBase
Implements ILogicalTimestamp
<Key>
Public Property ProtocolId As Integer
<Required>
Public Property ProtocolText As String
Public Property CreatedByUserId As Integer?
<Required>
Public Property CreatedByUser As User
<Required>
<DefaultValue(0)>
Public Property Type As ProtocolType
Public Property LinkedToUserId As Integer?
Public Property LinkedToUser As User
Public Property LinkedToBusinessPartnerId As Integer?
Public Property LinkedToBusinessPartner As BusinessPartner
Public Property LinkedToClientId As Integer?
Public Property LinkedToClient As Client
Public Property LinkedToSettingId As Integer?
Public Property LinkedToSetting As Setting
Public Property LastUpdateTimestamp As Date? Implements ILogicalTimestamp.LastUpdateTimestamp
Public Property CreationTimestamp As Date? Implements ILogicalTimestamp.CreationTimestamp
Public Enum ProtocolType
AutoChanges = 0
Explicitly = 1
End Enum
End Class
How can i write changes in protocol?
And how can i get the current UserId into the SaveChanges method to fill CreatedByUserId property in my protocol class?
Maybe anyone has an idea to do that automatically.
You can create a custom DB Context inheriting from DbContext, and then override the SaveChanges() method, so you can manipulate the data before/after modification.
Or you can use a library like Audit.EntityFramework, or check its code (C# sorry).
Also the library EntityFramework-Plus can probably help.

Entity Framework Update Foreign Key constraint may cause multiple cascade paths

I'm working on a project that is using entity framework and I'm getting the following error when I try and update my database.
Introducing FOREIGN KEY constraint 'FK_dbo.VisualManualWebPageFiles_dbo.VisualManualHeaders_VisualManualHeaderID' on table 'VisualManualWebPageFiles' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint.
Here are the classes that are being used for this.
First With a VisualManualHeader class.
<Table("VisualManualHeaders")>
Public Class VisualManualHeader
<Key>
Public Property VisualManualHeaderID As Integer
<Required>
Public Property Name As String
Public Overridable Property VisualManualPages As ICollection(Of VisualManualPage)
Public Overridable Property VisualManualWebPageFiles As ICollection(Of VisualManualWebPageFile)
End Class
Then a VisualManualHeader has the associated VisualManualPages with it like the following.
<Table("VisualManualPages")>
Public Class VisualManualPage
Enum VisualManualPageType
VIDEO
IMAGE
TEXT
WEBPAGE
End Enum
<Key>
Public Property VisualManualPageID As Integer
<Required>
Public Property Title As String
Public Property VisualManualHeaderID As Integer
<ForeignKey("VisualManualHeaderID")>
Public Overridable Property VisualManualHeader As VisualManualHeader
Public Property Content As String
Public Property PageType As VisualManualPageType
Public Overridable Property VisualManualWebPageFiles As ICollection(Of VisualManualWebPageFile)
End Class
The pages can then have many VisualManualWebPageFiles associated to the page.
<Table("VisualManualWebPageFiles")>
Public Class VisualManualWebPageFile
<Key>
Public Property VisualManualWebPageFileID As Integer
<Required>
Public Property VisualManualHeaderID As Integer
<ForeignKey("VisualManualHeaderID")>
Public Overridable Property VisualManualHeader As VisualManualHeader
<Required>
Public Property VisualManualPageID As Integer
<ForeignKey("VisualManualPageID")>
Public Overridable Property VisualManualPage As VisualManualPage
Public Property FileLocation As String
End Class
I have a feeling the problem is in the VisualManualWebPageFiles class due to the foreign key of the VisualManualHeader as if I remove this I can then update my project.
Thanks.
I think the problem lies with the association of the entity classes. Taking a look at your code seems that VisualManualHeader and VisualManualPage are in Many-To-Many relation; i.e. one instance of VisualManualHeader can be associated with many instances of VisualManualPage, and the the reverse is also true.
If that is indeed the case then, the relation between 2 classes are represented in VisualManualWebPageFile correctly and there is no need of keeping unnecessary properties in both VisualManualHeader, and VisualManualPage. I have tried to remove the same as follows:
VisualManualHeader
<Table("VisualManualHeaders")>
Public Class VisualManualHeader
<Key>
Public Property VisualManualHeaderID As Integer
<Required>
Public Property Name As String
Public Overridable Property VisualManualWebPageFiles As ICollection(Of VisualManualWebPageFile)
End Class
VisualManualPage
<Table("VisualManualPages")>
Public Class VisualManualPage
Enum VisualManualPageType
VIDEO
IMAGE
TEXT
WEBPAGE
End Enum
<Key>
Public Property VisualManualPageID As Integer
<Required>
Public Property Title As String
Public Property Content As String
Public Property PageType As VisualManualPageType
Public Overridable Property VisualManualWebPageFiles As ICollection(Of VisualManualWebPageFile)
End Class
VisualManualWebPageFile
<Table("VisualManualWebPageFiles")>
Public Class VisualManualWebPageFile
<Key>
Public Property VisualManualWebPageFileID As Integer
<Required>
Public Property VisualManualHeaderID As Integer
<ForeignKey("VisualManualHeaderID")>
Public Overridable Property VisualManualHeader As VisualManualHeader
<Required>
Public Property VisualManualPageID As Integer
<ForeignKey("VisualManualPageID")>
Public Overridable Property VisualManualPage As VisualManualPage
Public Property FileLocation As String
End Class
You are basically correct. The WebPageFiles does not need a reference to the ManualHeader table - and it actually could cause errors.
Example:
ManualPage ID 10 is owned by ManualHeader 1. You add WebPageFiles ID 15 with ManualPage 1 - but there is nothing stopping you setting WebPageFiles.ManualHeaderID to 3 as long as ManualHeader 3 exists. You now have a WebPageFile which a bit confused about who its grandparent is.
The correct way is to set the parent ID only (ManualPage). This parent will contain the ID of its own parent (ManualHeader) and so the child (WebPageFile) can unambiguously fing its grandparent via its parent.

Map NHibernate entity to multiple tables based on parent

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