I have the following configuration to set NH and Envers up:
var properties = new Dictionary<string, string>();
properties[NHibernate.Cfg.Environment.Dialect] = "NHibernate.Dialect.MsSql2008Dialect";
properties[NHibernate.Cfg.Environment.ConnectionDriver] = "NHibernate.Driver.SqlClientDriver";
properties[NHibernate.Cfg.Environment.Hbm2ddlAuto] = "update";
properties[NHibernate.Cfg.Environment.FormatSql] = "true";
properties[NHibernate.Cfg.Environment.ShowSql] = "true";
properties[NHibernate.Cfg.Environment.ConnectionString] = "Data Source=localhost;Initial Catalog=OU_KASH;Integrated Security=True;Asynchronous Processing=true";
var cfg = new Configuration();
cfg.Configure()
.SetProperties(properties)
.AddAssembly(typeof(AliasTb).Assembly.FullName)
;
cfg.SetEnversProperty(ConfigurationKey.StoreDataAtDelete, true);
cfg.SetEnversProperty(ConfigurationKey.AuditStrategy, typeof(NHibernate.Envers.Strategy.ValidityAuditStrategy));
cfg.SetEnversProperty(ConfigurationKey.TrackEntitiesChangedInRevision, true);
cfg.SetEnversProperty(ConfigurationKey.GlobalWithModifiedFlag, true);
cfg.IntegrateWithEnvers(new AttributeConfiguration());
var factory = cfg.BuildSessionFactory();
If I leave off the
cfg.SetEnversProperty(ConfigurationKey.AuditStrategy, typeof(NHibernate.Envers.Strategy.ValidityAuditStrategy));
it works fine, but with that option I get the following exception when I commit my transaction (which updates a CompanyAddressTb object):
ERROR NHibernate.AssertionFailure - An AssertionFailure occurred - this may indicate a bug in NHibernate or in your custom types.
System.InvalidOperationException: Cannot find previous revision for entity NhDoodling.Entities.Domain.CompanyAddressTb_AUD and id 12962904
at NHibernate.Envers.Strategy.ValidityAuditStrategy.updateLastRevision(ISession session, AuditConfiguration auditCfg, IList l, Object id, String auditedEntityName, Object revision, Boolean throwIfNotOneEntry)
at NHibernate.Envers.Strategy.ValidityAuditStrategy.Perform(ISession session, String entityName, Object id, Object data, Object revision)
at NHibernate.Envers.Synchronization.Work.AbstractAuditWorkUnit.Perform(ISession session, Object revisionData)
at NHibernate.Envers.Synchronization.AuditProcess.executeInSession(ISession executeSession)
at NHibernate.Envers.Synchronization.AuditProcess.DoBeforeTransactionCompletion()
at NHibernate.Envers.Synchronization.AuditProcessManager.<>c__DisplayClass4.<Get>b__0()
at NHibernate.Engine.ActionQueue.BeforeTransactionCompletionProcessQueue.BeforeTransactionCompletion()
Any help will be greatly appreciated.
If you start to use Envers on a non empty db, you need to manually insert current data in audit tables.
Read more here.
Related
I am setting the type for sql query result set as List in java. I am trying to convert it into
a dto.
When I see the List<Object[]> structure from query. It shows
resultList-ArrayList<E>
[0....9999]
[0..99]
[0]=Object[3]
[0]="jjj"
[1]="8787"
[2]="7686"
So is this expected. How can I access the object values here(jjj,8787...) by setting it to dto.
I tried something like this
List<Dto> dtoList = resultList.stream().map(obj->{
Dto dt = new Dto()
dt.setName(obj[0]);
).collect(Collectors.toList())
This is not correct as I am not able to access the object
Should I do another level of iteration in order to reach that object or is my generic type for result set is right
Thanks
Try this:
Object[] inner = new Object[]{"jjj", "8787", "7686"};
Object[] outer = new Object[]{inner};
List<Object[]> resultList = new ArrayList<>();
resultList.add(outer);
List<Dto> dtos;
dtos = resultList.stream()
.flatMap((Object[] objArr) -> {
Object[] subArr = (Object[]) objArr[0];
return Arrays.asList(subArr).stream()
.map(obj -> obj.toString());
})
.map(name -> {
Dto dto = new Dto();
dto.setName(name);
return dto;
})
.collect(Collectors.toList());
For auditing reasons I am overriding the SaveChanges function. However, I want to capture to original and current values as the original object (i.e person) so that I can serialize both the before and after.
Public Overrides Function SaveChanges() As Integer
ChangeTracker.DetectChanges()
Dim ctx As ObjectContext = DirectCast(Me, IObjectContextAdapter).ObjectContext
Dim objectStateEntryList As List(Of ObjectStateEntry) = ctx.ObjectStateManager.
GetObjectStateEntries(EntityState.Added Or EntityState.Modified Or EntityState.Deleted).ToList()
For Each ent As ObjectStateEntry In objectStateEntryList
If Not ent.IsRelationship Then
Dim objectType As Type = ObjectContext.GetObjectType(ent.Entity.GetType)
Dim audit As New Audit With {
.ObjectId = ent.EntityKey.EntityKeyValues.First.Value,
.ObjectType = ObjectContext.GetObjectType(ent.Entity.GetType).Name,
.User = (From u In Users Where u.Username = My.User.Name).First
}
With audit
Select Case ent.State
Case EntityState.Added
.Action = "Created"
.Detail = "Record created"
Case EntityState.Deleted
.Action = "Deleted"
.Detail = "Record deleted"
Case EntityState.Modified
Dim newObj As String = SerializeToString(
Convert.ChangeType(ent.Entity, objectType)
)
.Action = "Modified"
.Detail = newObj.ToString
End Select
End With
End If
Next
Return MyBase.SaveChanges()
End Function
That's how far I got, but when I try and ChangeType it throws "Object must implement IConvertible".
The last time I worked on a project that absolutely needed to track every change, we used a history table and an ON UPDATE trigger. Changing data would fire the trigger, which would then copy the original row into the history table.
This is 100% EF compatible, but you need to set it up separately for each table.
This doesn't exactly answer your main question but may help you to improve your auditing.
Disclaimer: I'm the owner of the project Entity Framework Plus
I recommend you to look at our EF+ Audit Feature, all auditing information can be easily retrieved using this library.
// 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)
{
}
}
// CALL your serializer here
SerializeToString(entries, ...);
The code is Open Source.
I have the following method which is called from Ajax:
[Authorize]
[ValidateInput(false)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public JsonNetResult CreateOrUpdateTimeRecord(TimeRecord tr)
{
TimeRecord trLocal;
if (tr.Id == -1 || tr.Id == 0)
{
trLocal = new TimeRecord
{
Description = tr.Description,
StartTime = tr.StartTime,
EndTime = tr.EndTime,
User =new myTimeMvc.Models.NHibernate.Models.User {Id = tr.User.Id},// _userRepo.Get(tr.User.Id),
Hdt = new Hdt {Id = tr.Hdt.Id}//_hdtRepo.Get(tr.Hdt.Id)
};
_timeRepo.Insert(trLocal);
}
else
{
trLocal = _timeRepo.Get(tr.Id);
trLocal.Description = tr.Description;
trLocal.StartTime = tr.StartTime;
trLocal.EndTime = tr.EndTime;
_timeRepo.Update(trLocal);
}
...
}
As you can see my TimeRecord has a reference to User and Hdt. Now I started to work with NHibernate Profiler which complains when I resolve my properties by loading them from their coresponding repositories. Which is clear to me since I actually don't need to query the database for that since I have the ID's for this objects.
User = _userRepo.Get(tr.User.Id),
Hdt = _hdtRepo.Get(tr.Hdt.Id)
But I'm not 100% sure if I can use this instead:
User =new myTimeMvc.Models.NHibernate.Models.User {Id = tr.User.Id},,
Hdt = new Hdt {Id = tr.Hdt.Id}
I guess NHibernate lazy proxies work the same way since they only contain just the ID of the related object and load the rest when it is needed. Do I have to attach this "new" oject anyway to my session?
Can someone tell me what is the correct way to do this?
Cheers,
Stefan
There are a few ways how to achieve that. One of them could be using the Load() method. Check Ayendes post: NHibernate – The difference between Get, Load and querying by id, an extract:
Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
Other words, we can do something like this
User = _userRepo.Load(tr.User.Id),
Hdt = _hdtRepo.Load(tr.Hdt.Id)
Where the Load would be encapsulating the session.Load()
i am trying to update record with nHibernate. I tried several solutions, but none of them work (shows no error, bu data is alsno not updated).
First code:
MyRepository rep = new MyRepository(GetCurrentSession());
UserPost post = rep.GetById(id);
post.ValidTo = date;
rep.Update(post);
Second code:
ISession session = GetCurrentSession();
MyRepository rep = new MyRepository(GetCurrentSession());
UserPost post = rep.GetById(id);
post.ValidTo = date;
rep.Update(post);
session.Update(post);
session.Transaction.Commit();
session = null;
Maybe somedy has a suggestion?
1) You need to flush the session if you are not using a transaction`:
var post = _session.Load<Post>(id); //assumes this record exists in the db
post.SomeAttribute=somenewvalue;
_session.SaveOrUpdate(post);
_session.Flush;
2) I don't see a transaction being started? You need to start a transaction to commit.
using(var transaction = _session.BeginTransaction()){
_session.SaveOrUpdate(post);
transaction.commit();
}
using(var transaction = _session.BeginTransaction()){
_session.SaveOrUpdate(post);
transaction.commit();
}
I had this Batch Update returns rowcount = 0 but expected is 1 exception. But this works
Is UserPost mapped correctly? Are you using .hbm.xml files (note the hbm) and the xml file is marked as an embedded resource?
In my experience if an entity is not mapped NHibernate doesn't complain nor throw an error.
Actually looking at your code in more detail you are not calling session.Save
If you want to update some persist entity's fields you shouldn't call session.Update() or session.SaveOrUpdate(), you can use session.Flush() or transactions:
MyRepository rep = new MyRepository(GetCurrentSession());
UserPost post = rep.GetById(id);
post.ValidTo = date;
rep.Flush(); // session.Flush()
OR
using(var transaction = _session.BeginTransaction()){
UserPost post = rep.GetById(id);
post.ValidTo = date;
transaction.commit();
}
I am trying to update 2 tables: RESTAURANT and HOURS. The tables shere the REST_ID key. I get an error on the line with the arrow (==>): Sorry, I'm trying to teach myself this stuff and it's the first time I've tried a multi-table insert.
The object could not be added or attached because its EntityReference
has an EntityKey property value that does not match the EntityKey for
this object.
RESTAURANT addRest = new RESTAURANT();
addRest.REST_NAME = r_name;
addRest.REST_STREET1 = r_street;
addRest.CITY_ID = c_id;
addRest.REST_PHONE = r_phone;
addRest.REST_WEBSITE = r_web;
addRest.HOUR = new HOUR();
addRest.HOUR.HOURS_SUN = h_su;
addRest.HOUR.HOURS_MON = h_mo;
addRest.HOUR.HOURS_TUE = h_tu;
addRest.HOUR.HOURS_WED = h_we;
addRest.HOUR.HOURS_THU = h_th;
addRest.HOUR.HOURS_FRI = h_fr;
addRest.HOUR.HOURS_SAT = h_sa;
addRest.HOURReference.EntityKey = new EntityKey("FVTCEntities.HOURS", "HOURS", 1);
==> db.AddToRESTAURANTs(addRest);
db.SaveChanges();
That isn't LINQ to SQL. It's Entity Framework.
You don't need to set the EntityKey, usually. Just set the HOUR properties like any POCO type. Ignore EntityKey unless you have a very specific reason to set it.