Closed mrlife closed 3 years ago
What kind of application? If it's asp.net you could do it on the strartup. Otherwise in any place that is run before any audited action takes place, and that runs only once of course...
On the static constructor of your dbcontext is another choice, that will run the first time a dbcontext is created and only once.
@thepirat000 I'm on ASP.NET Core 5.0.4. I have an EF query that needs to run to look up the user information, so the static constructor or startup.cs aren't great options in this case. However, as a test, I removed the EF query and placed the Setup()
call in the static constructor, and the audit table e.g. Audit_Order is not created in the database.
I have Audit.EntityFramework.Core
v16.5.4 installed on a .NET/EF Core 5.0.4 project. I am inheriting Audit.EntityFramework.AuditDbContext
from the database context. And I have the below Setup()
call. Everything compiles fine, but the Audit_MyModel
table isn't created when I run a migration. Any thoughts on what I'm doing wrong?
static MyContext()
{
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef
.AuditTypeExplicitMapper(m => m
.Map<MyModel, Audit_MyModel>()
.AuditEntityAction<AuditBase>((evt, entry, auditEntity) =>
{
auditEntity.AuditDate = DateTime.Now;
auditEntity.AuditAction = entry.Action; // Insert, Update, Delete
})
)
);
}
Audit table creation is up to you, the library will not create the tables.
Why do you say the startup is not a great option to put the configuration?
Audit table creation is up to you, the library will not create the tables.
Do users of the library typically do this with DbSet
s?
Why do you say the startup is not a great option to put the configuration?
I would need to access user claims through DI and to look up information about the user, i.e.
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef
.AuditTypeExplicitMapper(m => m
.Map<Order, Audit_Order>()
.Map<OrderItem, Audit_OrderItem>()
.AuditEntityAction<IAudit>((evt, entry, auditEntity) =>
{
// access to claims through DI and to query a DbSet here
})
)
);
I don't know what the users of the library usually do. There are many data providers for the audit output saving, and there are many use cases for the EntityFramework data provider.
For the DI part, you should be able to access any of your registered services, or the IServiceProvider
as a parameter on your Startup.Configure()
method and then use it on the setup delegate, for example:
public void Configure(IApplicationBuilder app, IServiceProvider svcProvider)
{
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef
.AuditTypeExplicitMapper(m => m
.Map<Order, Audit_Order>()
.Map<OrderItem, Audit_OrderItem>()
.AuditEntityAction<IAudit>((evt, entry, auditEntity) =>
{
var svc = svcProvider.GetRequiredService<IYourService>();
// ...
})
)
);
// ...
}
or if it's a singleton, you could just:
public void Configure(IApplicationBuilder app, IYourService svc)
{
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef
.AuditTypeExplicitMapper(m => m
.Map<Order, Audit_Order>()
.Map<OrderItem, Audit_OrderItem>()
.AuditEntityAction<IAudit>((evt, entry, auditEntity) =>
{
// use svc here
})
)
);
// ...
}
Is the DbSet
you need to access on the same DbContext
that is being audited? If so, you can access it with GetDbContext()
method on the EF event:
using Audit.EntityFramework;
//...
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef
.AuditTypeExplicitMapper(m => m
.Map<Order, Audit_Order>()
.Map<OrderItem, Audit_OrderItem>()
.AuditEntityAction<IAudit>((evt, entry, auditEntity) =>
{
var svc = svcProvider.GetRequiredService<IYourService>();
var dbContext = evt.GetEntityFrameworkEvent().GetDbContext();
// ...
})
)
);
To help illustrate, here is a sample app. It's the official Blazor with EF Core sample with the following modifications to add in this library.
I added the package to the .csproj
, added AuditDbContext
for inheritance, created Audit_Contact
, and added the Setup()
call to the context's static constructor.
After running the app and deleting one of the records, this is the error:
UNIQUE constraint failed: Audit_Contacts.Id
You should remove any unique index based on the audited table ID on the audit table, since the audit table will potentially contain multiple rows for the same ID. I guess that's the problem
Or, if the Audit_Contact.Id is not mean to be mapped to the Contact.Id, then you should set the IgnoreMatchedProperties
config to true
Thank you, I fixed by adding an explicit key property to the Audit_*
model classes so that the Id
matched up with the audited model and EF Core uses the explicit key property for its tracking purposes.
e.g.
public class MyModel
{
public int Id { get; set; }
public string Property { get; set; }
}
public class Audit_MyModel : IAudit
{
[Key]
public int AuditId { get; set; }
public int Id { get; set; }
public string Property { get; set; }
public string AuditAction { get; set; }
public DateTime AuditDate { get; set; }
}
I think your library is wonderful and appreciate your work to create and maintain it.
Where can/should this get called? I could not find in the readme the location to call
Audit.Core.Configuration.Setup()
.I first tried calling
Audit.Core.Configuration.Setup()
in the database contextOnModelCreating()
, and the audit table was created. However, there were some null errors when trying to save to the audit table.Second, I tried tried calling
Audit.Core.Configuration.Setup()
in the database contextOnConfiguring()
, since I saw it in another issue. That resulted in the audit table being deleted, so that doesn't seem like the right place.Originally posted by @thepirat000 in https://github.com/thepirat000/Audit.NET/issues/247#issuecomment-539686730