So I'm confused about Doctrine 2's lifecycle events. I have a subscriber event class that implements prePersit, preUpdate, postUpdate, and postLoad. Basically what I'm trying to do is to encrypt an entity's particular value on "prePersist" and "preUpdate" (when updating a value), and decrypt the value on "postLoad".
The decryption is working perfectly. However, I'm currently having two problems with these lifecycle events:
when "preUpdate" is triggered, it encrypts the value correctly (looking at my debugger) but once it gets committed to the database, I can see the that value is in clear text. What's the deal here?
I have also implemented the "postUpdate" event, which I was thinking that it would be triggered after the update is committed to the database, which would give me chance to decrypt the value for viewing on the UI, but following the debugger this is not case. The preUpdate is triggered first when doing an update, then followed by postUpdate, and finally this changes are committed to the database. Which event life cycle should I be using for this case?
The signature on preUpdate must be on type PreUpdateEventArgs.
This link has more info on to get it working.
Related
i had trouble with an entity who was not flushed correctly.
In a service, i setted somme values. before flushing them in the service, i call another service and i saw there was a listener linked.
In this listener, there was a postPersist method in witch was called "$entityManager->flush();"
It was the source of my problem.
I found this post : Doctrine inserting in postPersist event
So, i deleted the flush who was done in the postPersist.
But i don't understand the need of the method postFlush.
In my case, data is flushed even if i don't have this method. how is it possible that the properties setted in the listener are flushed correctly without this event ?
If i look other entry points, i see that i need to declare the postFlush event and i see the need of this method.
thanks for your help
No, you don't need to flush in a postPersist event, because it will be done soon, just after Persist. You don't need to use all the functions of the list, neither declaring them.
ps.: Only if you need to get/set data before persist/flush. You would need to compute changes then get them in the action 'couple' (e.g prePersist and postPersist, preUpdate and postUpdate).
docs says:
Changes to fields of the passed entities are not recognized by the
flush operation anymore, use the computed change-set passed to the
event to modify primitive field values.
and
getEntityChangeSet() to get a copy of the changeset array. Changes to
this returned array do not affect updating.
PostFlush
The postFlush is made at the end of a flush operation. According to the docs this event is not a lifecycle callback. You can use it to set something after registering, or even send notifications, clearly with postFlush you won't need to worry about lifeCycle events.
postFlush - The postFlush event occurs at the end of a flush
operation. This event is not a lifecycle callback.
For postPersist in the docs
postPersist - The postPersist event occurs for an entity after the
entity has been made persistent. It will be invoked after the database
insert operations. Generated primary key values are available in the
postPersist event.
Here you can have ids if you need before flush.
You can check the docs about LifeCycleEvents here:
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events
I'm trying to persist a History entity whenever a Message gets updated. I have too much going on behind the scenes to post all the code here and for it to make sense, but I've basically tracked the issue down to the UnitOfWork::commit method. There, the UOW first loops through the entityInsertions, and finding nothing, continues on to the entityUpdates. There the UOW's entityInsertions gets updated, but since it's already past that loop, it doesn't pick up that it still needs to persist some entities. Is there any way to force the UOW to "restart" this process? If so, how? I'm using Doctrine 2.4.
Thanks for any help!
This might be the dirtiest solution ever, but what I ended up doing was basically the following...
Create an onFlush event subscriber
Inject the entire container into the subscriber (seeing as injecting only the entity manager will result in a circular reference error)
Loop through the UnitOfWork's scheduledEntityUpdates and scheduledEntityInserts (I wasn't interested in deletes)
Handle each scheduled update or insert which you are interested in (in my case, I marked each entity I was interested in with a LoggableInterface, just to know which entities are loggable)
Handle the relevant object with a handler chain (This was just my own algorithm, yours may not require this. This was set up to handle logging of different LoggableInterface objects in different ways)
Persist the entity (the actual history event) via the entity manager, and do the following:
$classMeta = $this->entityManager->getClassMetadata(get_class($historyEntity));
$this->entityManager->getUnitOfWork()->computeChangeSet($classMeta, $historyEntity);
Profit
Hope this helps somebody!
I have a table with a trigger that points to an assembly:
CREATE TRIGGER [dbo].[triggername] ON [dbo].[tablename]
WITH EXECUTE AS CALLER
AFTER DELETE, UPDATE
NOT FOR REPLICATION
AS EXTERNAL NAME [Namofassembly].[blahblah].[blahblah]
We also using code first EF in .net 4.
When I use delete everything works fine but the trigger does not get called.
dataRepo.UsersPermanentAuditAssignments.Remove(isInsertFound)
When I use update I get a permissions error. This is either when I try it through the object model or a dataRepo.Database.ExecuteSqlCommand(updateSql)
System.Data.SqlClient.SqlException: The context transaction which was active before entering user defined routine, trigger or aggregate "name" has been ended inside of it, which is not allowed. Change application logic to enforce strict transaction nesting.
Everything works fine when I run the queries via the sql management studio.
I also am not able to change this configuration so while I don't care for this design I am not able to change it.
My questions are:
1> Why would the delete not get logged but work?
2> Do I need to add something extra to my repo configuration object that will allow this to work? Do I need to add some transaction like unitofwork before I start this since it has a trigger maybe?
I have figured out the causes of this issue.
It relates to having a composite primary key (station,user) and trying to update one of the values.
I could not update any column of the primary key, ie change the user assigned to a station.
The trigger failure masked the issue of not being able to update a value inside the key.
My experiments show the following for the compositekey/pk update:
Method History Trigger Result
EF.SaveChanges Enabled Fail at trigger
EF.SaveChanges Disabled Fail at trigger
EF.ExecuteSQLCommand(sql) Enabled Fail at trigger
EF.ExecuteSQLCommand(sql) Disabled Works
Unfortunately, I don't have the ability to change to a surrogate with a unique index which would work. Also, the trigger CLR prevents me from using DataBase.ExecuteSQLCommand(sql) also which I believe is actually a problem with the CLR of which I have not ability to modify.
So my advise (that I can't take) is if you get this use a surrogate key and a unique index instead of combining the 2.
If anyone knows any way to allow EF to allow you to change a value inside a composite/primary key please comment.
I feel like I may be overlooking a fundamental concept of the page life-cycle here and have been (either because I can't figure out the right keywords or it hasn't been asked) unable to locate an existing answer so forgive me if this has been asked.
Basically, I need to persist a mutable object between the client side and the server side. Since the viewstate is encrypted/serialized and the session state is server-side only, my solution was to use a hidden field--easy enough, right? Well here's my problem... it seems as though it's working but the data isn't being propagated as I would've expected.
My expectation was this:
Page is loaded for the first time. Server-side class recognizes that the hidden field is empty, initializes the container class, serializes the class to a JSON string and writes that value to the hidden field.
Page_Init: Unavailable.
Page_Load: Unavailable.
Page_LoadComplete: Available.
Server processing completes, object is now available for use by client code.
Object in hidden field is mutated by client code. Client code then fires a postback to the server (via a button).
Server-side processing begins...
Page_Init: Unavailable.
Page_Load: Available, including client-side changes.
Page_LoadComplete: Available, including client-side changes.
All is right in the world, a double-rainbow shines outside my window and a magical unicorn gives me a wink and a nod.
My observation is this:
Page is loaded for the first time. Server-side class recognizes that the hidden field is empty, initializes the container class, serializes the class to a JSON string and writes that value to the hidden field.
Page_Init: Unavailable. (As expected)
Page_Load: Unavailable. (As expected)
Page_LoadComplete: Available. (As expected)
Server processing completes, object is now available for use by client code.
Object in hidden field is mutated by client code. Client code then fires a postback to the server (via a button).
Server-side processing begins...
Page_Init: Unavailable. (As expected)
Page_Load: Available, but not updated with changes made on the client-side. (Unexpected).
Page_LoadComplete: Available, including client-side changes. (As expected)
A dark cloud forms over my cubicle and I begin to contemplate whether or not my laptop would survive the second-story fall off the balcony.
Conclusion
This is causing me a bit of confusing for a couple reasons... the first is that I've never used the "LoadComplete" event before and can't seem to find any examples that suggest it's necessary to or even that it should be done that way. The second is that by the time load complete is raised, other events that rely on the data from the client side have already been fired.
Any help/explanation/suggestion; hell, even criticism is appreciated!
Thanks, Jason
I'm answering this in the hope that this helps save someone else a few hours. After much trial and finally success, I learned that you can get a HiddenField value during the OnInit event. Given a HiddenField with an ID of hidValue, the key line is:
string strValue = Request.Form[hidValue.UniqueID].ToString();
I've ripped a lot of hair on ASP.NET lifecycle :-). I would advise you this:
bind to control events
avoid binding or overriding to page
events
In this case, you should have a protected HiddenField declared in your page/user control. So you really want to bind to the ValueChanged event, and forget about the rest.
Explaination
You can update HiddenField values in javascript and get them back at the server.
If you want your object to be available after Load, using LoadComplete is ok.
If you want this object to be available to all controls when they load, the earliest you can get the data from inputs is by overloading PreLoad and creating your object there.
There is no problem with your logic.
Conclusion
There is some bug in your implementation of it.
Lets take a look at the code now.
I have a composite control that has a couple of private fields that reference values in the cache and these private fields are called during the constructor method. Since a string key is used to identify the value in the cache, I must have a way of storing that string key in such a way that it is available at the time the control is instantiated, and I have to be able to reference it on postbacks without it changing.
In addition, this key is generated the first time the control is loaded, but it should not be changed again after that first time.
How can I accomplish this?
I have already tried saving it to viewstate, but that doesn't work because viewstate is not yet available at the time the control is instantiated.
I have tried using a private field and then checking against Page.IsPostback in the constructor and if it isn't postback, I assign a value to the private field, but on subsequent postbacks it looses it's value, and I can't reassign it in the Page.IsPostBack again because it is an autogenerated GUID.
This has got to be something folks have had to do before....
There isn't a lot of state info available during control construction at all, so this could be difficult. Is there some reason you can't move your code which accesses the Cache'ed info into the control's Init event?
I assume you can't use Session because the information stored is related to that specific request/postback. If it's not specific to that request, using Session could be a possibility - but I think you may encounter other problems trying to deal with control state so early in the lifetime.
After seeing your comment to the other answer; you should be able to move your code that checks for the cached datasource into the control's Init or even Load event, so the state will be available.
Also, incidentally; are you sure you really need to cache this data? That could end up taking up a lot of server memory.
Have you tried Session?
You can store anything you like in the session object for one particular user, maintaining the value / object between postbacks.
If you want to store on a global basis and not per ser basis, try Application
Although this isn't the best solution (rearranging your logic to fit the lifecycle model generally is), have you tried accessing the Request directly? I once really wanted to get the selected value off a DropDownList very early in the lifecycle so I could adjust some elements in the building, and I did it like this:
myDropDownList.SelectedValue = Page.Request.Form[myDropDownList.UniqueID];
So instead of waiting for the viewstate to load the server-side proxie's values, I just got it myself from the client-side control value that was passed in on the post. I probably would do things differently if I redesigned that page, but it seems to have worked out alright for now and it solved the problem I was having.