NHibernate - how to audit all fields of entity? - c#

I'm using NHibernate and looking for a solution that will allow me to audit changes to all fields in entity. I want to be able to create a history table for every entity i.e. Users -> UsersHistory that will have same structure as Users table and additional fields such as operation type (update, delete), userid of user that made change, etc. I don't want to define such class for every entity. I'm looking for something like History<T> (i.e. History<User>) because these entries don't belong to my domain and will only be used to prepare list of changes made to the entity. I also think that it would be better to create inserts to these tables in code rather than creating sql triggers. Basically, I just need to create a copy of record in history table on update or delete and I want the insert to be generated by NHibernate. I will also need to read records from history tables - as I said these tables will consist of entity fields and some common history fields.
I cannot find guidance on how to create such solution. All I can find is adding UserModified, UpdatedTimestamp etc. if I already have such fields on entity. However, I need full history of entity not just the information who last changed the entry.
Thanks in advance for help.

There is cool, open source audit trail for NHibernate called nhibernate.envers https://bitbucket.org/RogerKratz/nhibernate.envers , so you do not have to reinvent the wheel.
It integrates transparently into NHibernate, no changes to your domain model or mappings.
It's as simple as, adding the reference and call:
var enversConf = new FluentConfiguration();
enversConf.Audit<User>();
nhConf.IntegrateWithEnvers(enversConf);
whereas nhConf is your NHibernate config object.
For every change on your object a new revision is created, you can ask Envers to retrieve a revision by calling:
var reader = AuditReaderFactory.Get(session);
var userInRevOne = reader.Find<User>(user.Id, 1);
or list all revisions etc. The revision data itself can be enriched with a username, userid, timestamp etc. (whatever you can think off).
EDIT:
And it is available at NuGet: http://nuget.org/packages/NHibernate.Envers

I think the best solution is using Event Listeners:
http://darrell.mozingo.net/2009/08/31/auditing-with-nhibernate-listeners/
I wrote something similar to above (modified after finding that blog) except I store the result in XML.
e.g:
public void OnPostUpdate(PostUpdateEvent updateEvent)
{
if (updateEvent.Entity is AuditItem)
return;
var dirtyFieldIndexes = updateEvent.Persister.FindDirty(updateEvent.State, updateEvent.OldState, updateEvent.Entity, updateEvent.Session);
var data = new XElement("AuditedData");
foreach (var dirtyFieldIndex in dirtyFieldIndexes)
{
var oldValue = GetStringValueFromStateArray(updateEvent.OldState, dirtyFieldIndex);
var newValue = GetStringValueFromStateArray(updateEvent.State, dirtyFieldIndex);
if (oldValue == newValue)
{
continue;
}
data.Add(new XElement("Item",
new XAttribute("Property", updateEvent.Persister.PropertyNames[dirtyFieldIndex]),
new XElement("OldValue", oldValue),
new XElement("NewValue", newValue)
));
}
AuditService.Record(data, updateEvent.Entity, AuditType.Update);
}
Audit Service just builds add some additional data such as IP Address, User (if any), was it a system/service update or actioned via a website or user, etc.
Then in my DB i Store the XML like:
<AuditedData>
<Item Property="Awesomeness">
<OldValue>above average</OldValue>
<NewValue>godly</NewValue>
</Item>
<Item Property="Name">
<OldValue>Phill</OldValue>
<NewValue>Phillip</NewValue>
</Item>
</AuditedData>
I also have Insert/Delete listeners.

What you are looking for are Event Listeners (unfortunately i cannot link to relevant docs because nhforge.org wiki is experiencing NRE...).
Check Complex NHibernate Auditing

Here's a complete example of how to do this: http://www.shawnduggan.com/?p=89.
Also covered in this post: Audit logging nhibernate

Related

Live Transfer of data from one provider to another in Entity Framework

I apologise if this has been asked already, I am struggling greatly with the terminology of what I am trying to find out about as it conflicts with functionality in Entity Framework.
What I am trying to do:
I would like to create an application that on setup gives the user to use 1 database as a "trial"/"startup" database, i.e. non-production database. This would allow a user to trial the application but would not have backups etc. in no way would this be a "production" database. This could be SQLite for example.
When the user is then ready, they could then click "convert to production" (or similar), and give it the target of the new database machine/database. This would be considered the "production" environment. This could be something like MySQL, SQLServer or.. whatever else EF connects to these days..
The question:
Does EF support this type of migration/data transfer live? Would it need another app where you could configure the EF source and EF destination for it to then run through the process of conversion/seeding/population of the data source to another data source?
Why I have asked here:
I have tried to search for things around this topic, but transferring/migration brings up subjects totally non-related, so any help would be much appreciated.
From what you describe I don't think there is anything out of the box to support that. You can map a DbContext to either database, then it would be a matter of fetching and detaching entities from the evaluation DbContext and attaching them to the production one.
For a relatively simple schema / object graph this would be fairly straight-forward to implement.
ICollection<Customer> customers = new List<Customer>();
using(var context = new AppDbContext(evalConnectionString))
{
customers = context.Customers.AsNoTracking().ToList();
}
using(var context = new AppDbContext(productionConnectionString))
{ // Assuming an empty database...
context.Customers.AddRange(customers);
}
Though for more complex models this could take some work, especially when dealing with things like existing lookups/references. Where you want to move objects that might share the same reference to another object you would need to query the destination DbContext for existing relatives and substitute them before saving the "parent" entity.
ICollection<Order> orders = new List<Order>();
using(var context = new AppDbContext(evalConnectionString))
{
orders = context.Orders
.Include(x => x.Customer)
.AsNoTracking()
.ToList();
}
using(var context = new AppDbContext(productionConnectionString))
{
var customerIds = orders.Select(x => x.Customer.CustomerId)
.Distinct().ToList();
var existingCustomers = context.Customers
.Where(x => customerIds.Contains(x.CustomerId))
.ToList();
foreach(var order in orders)
{ // Assuming all customers were loaded
var existingCustomer = existingCustomers.SingleOrDefault(x => x.CustomerId == order.Customer.CustomerId);
if(existingCustomer != null)
order.Customer = existingCustomer;
else
existingCustomers.Add(order.Customer);
context.Orders.Add(order);
}
}
This is a very simple example to outline how to handle scenarios where you may be inserting data with references that may, or may not exist in the target DbContext. If we are copying across Orders and want to deal with their respective Customers we first need to check if any tracked customer reference exists and use that reference to avoid a duplicate row being inserted or throwing an exception.
Normally loading the orders and related references from one DbContext should ensure that multiple orders referencing the same Customer entity will all share the same entity reference. However, to use detached entities that we can associate with the new DbContext via AsNoTracking(), detached references to the same record will not be the same reference so we need to treat these with care.
For example where there are 2 orders for the same customer:
var ordersA = context.Orders.Include(x => x.Customer).ToList();
Assert.AreSame(orders[0].Customer, orders[1].Customer); // Passes
var ordersB = context.Orders.Include(x => x.Customer).AsNoTracking().ToList();
Assert.AreSame(orders[0].Customer, orders[1].Customer); // Fails
Even though in the 2nd example both are for the same customer. Each will have a Customer reference with the same ID, but 2 different references because the DbContext is not tracking the references used. One of the several "gotchas" with detached entities and efforts to boost performance etc. Using tracked references isn't ideal since those entities will still think they are associated with another DbContext. We can detach them, but that means diving through the object graph and detaching all references. (Do-able, but messy compared to just loading them detached)
Where it can also get complicated is when possibly migrating data in batches (disposing of a DbContext regularly to avoid performance pitfalls for larger data volumes) or synchronizing data over time. It is generally advisable to first check the destination DbContext for matching records and use those to avoid duplicate data being inserted. (or throwing exceptions)
So simple data models this is fairly straight forward. For more complex ones where there is more data to bring across and more relationships between that data, it's more complicated. For those systems I'd probably look at generating a database-to-database migration such as creating INSERT statements for the desired target DB from the data in the source database. There it is just a matter of inserting the data in relational order to comply with the data constraints. (Either using a tool or rolling your own script generation)

EF6 Code First Audit Table Per Entity

I have a requirement that feels like it probably has a simpler solution with EF than what we're currently using.
Essentially, as an auditing requirement, for any entity that inherits from a given base class, I need to create both the entity's table itself, but also a table that's identical, but with 3 additional columns - a FK back to the original entity's table, a description (e.g. "Modified", "Added", "Deleted") and an XML column that will contain a serialized version of the state of the entity.
At present, we're manually adding the entities to create the audit tables (currently inherit from an AuditableEntity class and developers have to manually ensure that other fields match the original entity) and using migrations to add T-SQL triggers to the entity tables to update the data in the audit tables on any insert, update, delete.
I'd prefer if I could somehow get EF to automatically create/migrate the audit tables based on the entity tables without having to manually sync them, and likewise use an interceptor or something similar to update the audit table on insert/update/delete of an entity rather than using triggers. Does anyone know if this is possible, or done anything similar? In the past, the closest I've come is a single, common audit history table which wasn't too bad.
Disclaimer: I'm the owner of the project Entity Framework Plus
This project may answer to your requirement. You can access to all auditing information like entity name, action name, property name, original and current values, etc.
A lot of options is available like an AutoSave all information in the database.
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntityContext();
// ... ctx changes ...
var audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.SaveChanges(audit);
// Access to all auditing information
var entries = audit.Entries;
foreach(var entry in entries)
{
foreach(var property in entry.Properties)
{
}
}
Documentation: EF+ Audit
You could create one table with the columns:
Id
TableName
Action (Add, update, delete)
IdOfRecord
XmlSerialized
DateChanges (use datetime2)
Then override SaveChanges() to write each change to that one table.
No need to mess around with keeping Audit table schema up to date when running migrations etc

Entity Framework updating two databases

As I've mentioned in a couple other questions, I'm currently trying to replace a home-grown ORM with the Entity Framework, now that our database can support it.
Currently, we have certain objects set up such that they are mapped to a table in our internal database and a table in the database that runs our website (which is not even in the same state, let alone on the same server). So, for example:
Part p = new Part(12345);
p.Name = "Renamed part";
p.Update();
will update both the internal and the web databases simultaneously to reflect that the part with ID 12345 is now named "Renamed part". This logic only needs to go one direction (internal -> web) for the time being. We access the web database through a LINQ-to-SQL DBML and its objects.
I think my question has two parts, although it's possible I'm not asking the right question in the first place.
Is there any kind of "OnUpdate()" event/method that I can use to trigger validation of "Should this be pushed to the web?" and then do the pushing? If there isn't anything by default, is there any other way I can insert logic between .SaveChanges() and when it hits the database?
Is there any way that I can specify for each object which DBML object it maps to, and for each EF auto-generated property which property on the L2S object to map to? The names often match up, but not always so I can't rely on that. Alternatively, can I modify the L2S objects in a generic way so that they can populate themselves from the EF object?
Sounds like a job for Sql Server replication.
You don't need to inter-connect the two together as it seems you're saying with question 2.
Just have the two separate databases with their own EF or L2S models and abstract them away using repositories with domain objects.
This is the solution I ended up going with. Note that the implementation of IAdvantageWebTable is inherited from the existing base class, so nothing special needed to be done for EF-based classes, once the T4 template was modified to inherit correctly.
public partial class EntityContext
{
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
var modified = this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added); // Get the list of things to update
var result = base.SaveChanges(options); // Call the base SaveChanges, which clears that list.
using (var context = new WebDataContext()) // This is the second database context.
{
foreach (var obj in modified)
{
var table = obj.Entity as IAdvantageWebTable;
if (table != null)
{
table.UpdateWeb(context); // This is IAdvantageWebTable.UpdateWeb(), which calls all the existing logic I've had in place for years.
}
}
context.SubmitChanges();
}
return result;
}
}

C# linq to sql - selecting tables dynamically

I have the following scenario: there are a database that generates a new logTable every year. It started on 2001 and now has 11 tables. They all have the same structure, thus the same fields, indexes,pk's, etc.
I have some classes called managers that - as the name says - manages every operation on this DB. For each different table i have a manager, except for this logTable which i have only one manager.
I've read a lot and tried different things like using ITable to get tables dynamically or an interface that all my tables implements. Unfortunately, i lose strong-typed properties and with that i can't do any searches or updates or anything, since i can't use logTable.Where(q=> q.ID == paramId).
Considering that those tables have the same structure, a query that searches logs from 2010 can be the exact one that searches logs from 2011 and on.
I'm only asking this because i wouldn't like to rewrite the same code for each table, since they are equal on it's structure.
EDIT
I'm using Linq to SQL as my ORM. And these tables uses all DB operations, not just select.
Consider putting all your logs in one table and using partitioning to maintain performance. If that is not feasible you could create a view that unions all the log tables together and use that when selecting log data. That way when you added a new log table you just update the view to include the new table.
EDIT Further to the most recent comment:
Sounds like you need a new DBA if he won't let you create new SPs. Yes I think could define an ILogTable interface and then make your log table classes implement it, but that would not allow you do GetTable<ILogTable>(). You would have to have some kind of DAL class with a method that created a union query, e.g.
public IEnumerable<ILogTable> GetLogs()
{
var Log2010 = from log in DBContext.2010Logs
select (ILogTable)log;
var Log2011 = from log in DBContext.2011Logs
select (ILogTable)log;
return Log2010.Concat(Log2011);
}
Above code is completely untested and may fail horribly ;-)
Edited to keep #AS-CII happy ;-)
You might want to look into the Codeplex Fluent Linq to SQL project. I've never used it, but I'm familiar with the ideas from using similar mapping techniques in EF4. YOu could create a single object and map it dynamically to different tables using syntax such as:
public class LogMapping : Mapping<Log> {
public LogMapping(int year) {
Named("Logs" + year);
//Column mappings...
}
}
As long as each of your queries return the same shape, you can use ExecuteQuery<Log>("Select cols From LogTable" + instance). Just be aware that ExecuteQuery is one case where LINQ to SQL allows for SQL Injection. I discuss how to parameterize ExecuteQuery at http://www.thinqlinq.com/Post.aspx/Title/Does-LINQ-to-SQL-eliminate-the-possibility-of-SQL-Injection.

Correct way to remove M:M in PLINQO?

Suppose you have this table structure:
Patient -> PatientTag -> Tag
A typical N:M relationship between patients and tags, PatientTag being the intermediate entity with both FKs. (PatientId and TagId).
I want to remove a specific tag, I have its ID. I’m doing this but I’d like to know if there’s a better way, since these are the 1st methods I write using PLINQO, I wouldn’t want to create bad practices from the beginning.
using ( MyDataContext dc = DataContextFactory.GetDataContext() )
{
var options = new DataLoadOptions();
options.LoadWith<Paciente>(p => p.PacienteTagList);
options.LoadWith<PacienteTag>(pt => pt.Tag);
dc.LoadOptions = options;
// Get the Tag we're going to remove from the DB.
var tag = dc.Manager.Tag.GetByKey( idTag);
// Remove each patient from the association.
foreach ( Paciente pac in tag.PacienteList1 )
{
// we need to retrieve it, won’t let us use the ‘pac’ object.
var pax = dc.Manager.Paciente.GetByKey( pac.IdPaciente );
pax.TagList.Remove(tag);
}
// now remove the tag
dc.Manager.Tag.Delete(tag.TagId);
// And commit the changes
dc.SubmitChanges();
}
Thanks for any insight on the subject.
I agree with tvanfosson do it on the database. Another way (may be safer imho) would be to create a strored procedure and call it from you code. Make sure its all wrapped up in a transaction that can deal with rollbacks in case something goes wrong
What about simply using a foreign key with cascading delete, then deleting the tag itself and letting the database take care of deleting all the references. If you wanted to make sure that it wasn't in use, you could check that there aren't any patients associated with it first, though you may need to wrap it in a transaction if there are other processes accessing the same database.

Categories

Resources