Entity Framework Core - Audit - vb.net

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.

Related

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.

In EF how do I code the class to access the foreign key?

EF 6, code first technique.
Two classes defined as:
Public Class SqlLayerTypeSet
Public Overridable Property SqlLayerTypeSetID As Integer
Public Overridable Property Name As String
Public Overridable Property Desc As String
Public Property ConferenceTypes As New List(Of SqlConferenceType)
End Class
Public Class SqlConferenceType
Public Property ID As Integer
Public Property Name As String
Public Property Desc As String
End Class
After seeding the tables look, respectively, as this:
What do I add to Class SqlConferenceType so I can access the existing foreign key SqlLayerTypeSet_SqlLayerTypeSetID which points back to the SqlLayerTypeSet that SqlConferenceType is a member of.
When I add whatever it is supposed to be, will that require a new migration?
I had thought that EF allows you to define properties in the class using a convention that simply gives you access to the foreign key but doesn't actually change the table definition. The way I have been trying to add a property to the class is actually adding another column in the table when all I really want to do is access the column that EF code first already created.
I know this is a basic question, but I just can't seem to find the right SO post that explains it to me.
Added after initial post:
This is the line I added that adds access to the parent:
Public Overridable Property SqlLayerTypeSet As SqlLayerTypeSet
But if I try to add a specific property for the parent's key such as:
Public Property SqlLayerTypeSetID as Integer
That creates the extra column. Is it the case that the only property I can add to Class SqlConferencetype is the navigation property, and I simple have to retrieve the foreign key value using navigation as opposed to being able to define a property that simply holds the foreign key value?
Public Class SqlLayerTypeSet
Public Overridable Property SqlLayerTypeSetID As Integer
Public Overridable Property Name As String
Public Overridable Property Desc As String
<ForeignKey("SqlConferenceType")>
Public Overridable Property SqlConferenceTypeID as Integer 'Foreign key property
Public Overridable Property ConferenceTypes As New List(Of SqlConferenceType) 'Relationship property
End Class
Public Class SqlConferenceType
Public Property ID As Integer
Public Property Name As String
Public Property Desc As String
Public Overridable Property SqlLayerTypeSet as SqlLayerTypeSet 'Relationship property
End Class

Entity Framework load related objects without available navigation property (DTO)?

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?

The navigation property is not a declared property on type. Verify that it has not been explicitly excluded from the model

Usually, for such cases there is a messup in the declaration of Foreign Keys but I have run my eyes over this code countless times.
Manifests and BL are parent-child entities.
Namespace models
Public Class manifest
<DatabaseGenerated(DatabaseGeneratedOption.None)>
<Key>
Public Property id As Integer 'uses SQL sequence and not autogenerated
Public Property arrivalDate As Date
Public Overridable Property BL As IEnumerable(Of bl)
Public Overridable Property Vessel As vessel
end Class
End Namespace
And also
Namespace models
Public Class bl
<DatabaseGenerated(DatabaseGeneratedOption.None)>
<Key> <Column(Order:=2)>
Public Property blid As Integer
<Key> <Column(Order:=1)>
<ForeignKey("manifest")>
Public Property manifestid As Integer
<ForeignKey("manifestid")>
Public Overridable Property manifest As manifest
<Required>
Public Overridable Property container As IEnumerable(Of container)
End Class
End Namespace
But in the end, the outcome is "The navigation property 'BL' is not a declared property on type 'manifest'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property."
Any hint on what am I missing?

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.