I am looking for a smarter way to dispatch a symfony event within entity manager transactional, so that in case of rollback it won't complain.
Here's the sample of code:
$this->em->transactional(
function () use ($xyz, $oldXyz) {
if ($oldXyz !== null) {
$this->doRemove($oldXyz);
}
$this->em->persist($xyz);
}
);
Where:
private function doRemove(XyzInterface $oldXyz): void
{
$this->em->remove($oldXyz);
$this->em->flush();
$this->dispatcher->dispatch(new XyzEvent($oldXyz), XyzEvents::onXyzDeleted);
}
This thing will complain 'app.ERROR: Pending transaction on master connection' because this due to event dispatching cannot be rollback.
I can not move out event dispatch from doRemove, because it's used somewhere else plus it's job of doRemove to dispatch this event here, since actual removing happens here only.
Any help would be appreciated, thank you all in advance.
Just return $oldXyz to the caller (by editing the closure as follows)
function () use ($xyz, $oldXyz) {
if ($oldXyz !== null) {
$this->doRemove($oldXyz);
return $oldXyz;
}
$this->em->persist($xyz);
return null;
}
Then check for returned value and dispatch
$oldXyz = $this->em->transactional(...);
if ($oldXyz instanceof <FQCNForOldXyz>) {
$this->dispatcher->dispatch(new XyzEvent($oldXyz), XyzEvents::onXyzDeleted);
}
Didn't tried directly, but I'm pretty confident it works.
Related
Referencing this NGRX ticket, I am seeing an oddity where a code-based subscription to a factory selector does not fire with state updates, however, the async pipe does work.
To clarify, if I set up a public observable property on a component and bind the HTML to it with an async pipe, the value will update as the state updates.
component.ts
public test$: Observable<ChatInteraction>;
[...]
this.test$ = this.store
.select(getNewMessageForChat(this.chat.chatId));
component.html
test: {{(test$ | async).message}}
Then I end up with output like
test: test1
test: test2
etc etc
However, if I try to subscribe to that same observable, it fires once when the subscription is created, but never again as the state updates.
component.ts
this.store
.select(getNewMessageForChat(this.chat.chatId))
.subscribe(() => {
if (this._calculateScrollDistanceFromBottom(this.chatWindowElem.nativeElement) <= 20) {
this._scrollToBottom();
}
});
If I put a breakpoint on that if statement, it does not get hit as I update state.
Any ideas why?
Figured it out. Turned out I had an undefined object reference in my subscription, which caused the subscription to break on its first run and resulted in it no longer firing. This was difficult to catch as it turns out the subscription code has a try/crush in it unless a setting is set.
__tryOrUnsub(fn, value) {
try {
fn.call(this._context, value);
}
catch (err) {
this.unsubscribe();
if (config.useDeprecatedSynchronousErrorHandling) {
throw err;
}
else {
hostReportError(err);
}
}
}
hostReportError didn't bubble anything up so to me it looked like it just wasn't firing anymore.
The documentation stresses that I should use a new EntityManager for each request and there's even a middleware for automatically generating it or alternatively I can use em.fork(). So far so good.
The EntityRepository is a great way to make the code readable. I could not find anything in the documentation about how they relate to EntityManager instances. The express-ts-example-app example uses single instances of repositories and the RequestContext middleware. This suggests that there is some under-the-hood magic that finds the correct EntityManager instances at least with the RequestContext. Is it really so?
Also, if I fork the EM manually can it still find the right one? Consider the following example:
(async () => {
DI.orm = await MikroORM.init();
DI.em = DI.orm.em;
DI.companyRepository = DI.orm.em.getRepository(Company);
DI.invoiceRepository = DI.orm.em.getRepository(Invoice);
...
fetchInvoices(em.fork());
}
async function fetchInvoices(em) {
for (const company of await DI.companyRepository.findAll()) {
fetchInvoicesOfACompany(company, em.fork())
}
}
async function fetchInvoicesOfACompany(company, em) {
let done = false;
while (!done) {
const invoice = await getNextInvoice(company.taxnumber, company.lastInvoice);
if ( invoice ) {
DI.invoiceRepository.persist(invoice);
company.lastInvoice = invoice.id;
em.flush();
} else {
done = true;
}
}
}
Does the DI.invoiceRepository.persist() in fetchInvoicesOfACompany() use the right EM instance? If not, what should I do?
Also, if I'm not mistaken, the em.flush() in fetchInvoicesOfACompany() does not update company, since that belongs to another EM - how should I handle situations like this?
First of all, repository is just a thin layer on top of EM (an extension point if you want), that bares the entity name so you don't have to pass it to the first parameter of EM method (e.g. em.find(Ent, ...) vs repo.find(...).
Then the contexts - you need a dedicated context for each request, so it has its own identity map. If you use RequestContext helper, the context is created and saved via domain API. Thanks to this, all the methods that are executed inside the domain handler will use the right instance automatically - this happens in the em.getContext() method, that first checks the RequestContext helper.
https://mikro-orm.io/docs/identity-map/#requestcontext-helper-for-di-containers
Check the tests for better understanding of how it works:
https://github.com/mikro-orm/mikro-orm/blob/master/tests/RequestContext.test.ts
So if you use repositories, with RequestContext helper it will work just fine as the singleton repository instance will use the singleton EM instance that will then use the right request based instance via em.getContext() where approapriate.
But if you use manual forking instead, you are responsible use the right repository instance - each EM fork will have its own one. So in this case you can't use a singleton, you need to do forkedEm.getRepository(Ent).
Btw alternatively you can also use AsyncLocalStorage which is faster (and not deprecated), if you are on node 12+. The RequestContext helper implementation will use ALS in v5, as node 12+ will be requried.
https://mikro-orm.io/docs/async-local-storage
Another thing you could do is to use the RequestContext helper manually instead of via middlewares - something like the following:
(async () => {
DI.orm = await MikroORM.init();
DI.em = DI.orm.em;
DI.companyRepository = DI.orm.em.getRepository(Company);
DI.invoiceRepository = DI.orm.em.getRepository(Invoice);
...
await RequestContext.createAsync(DI.em, async () => {
await fetchInvoices();
})
});
async function fetchInvoices() {
for (const company of await DI.companyRepository.findAll()) {
await fetchInvoicesOfACompany(company)
}
}
async function fetchInvoicesOfACompany(company) {
let done = false;
while (!done) {
const invoice = await getNextInvoice(company.taxnumber, company.lastInvoice);
if (invoice) {
company.lastInvoice = invoice; // passing entity instance, no need to persist as `company` is managed entity and this change will be cascaded
await DI.em.flush();
} else {
done = true;
}
}
}
I would like to cancel the operation if persist after-has test.and i dont know how i tried the redirection goal in vain.
/**
* #param mixed $object
*/
public function prePersist($object)
{
if (is_null($object->getFile())) {
}
}
any help ?
It's probably a bit late for you. Since I was looking for the same thing though, here's my solution.
It seems not to be possible to do any validation in Sonatas prePersist method.
For a simple NotNull validation like you are doing it I'd add a simple validation constraint to the entity or if the field is not mapped to the form element itself.
If you want to do some extra validation during Sonatas save process, override the validate method. This one is called before the prePersis/preUpdate methods and it allows you to add validation messages.
use Sonata\CoreBundle\Validator\ErrorElement;
public function validate(ErrorElement $errorElement, $object)
{
$errorElement->with('file')->addViolation('Hey, this is a validation message');
}
I'm a late too but if as me you're facing the same issue. Here is my work around with Doctrine
use ACME\Exceptions\CustomException;
public function prePersist($object)
{
// Do some stuff with your entity $object
if (//Something) {
$this->getRequest()->getSession()->getFlashBag()->add(
'error',
'Something wrong happens');
throw new CustomException('Something wrong happens');
}
}
/** Overwrite create methode to catch custom error */
public function create($object)
{
try {
$this->em->beginTransaction();
$res = parent::create($object);
$this->em->getConnection()->commit();
return $res;
} catch (CustomException $e) {
$this->em->getConnection()->rollBack();
return null;
}
}
This way you can validate or do anything you want in prePersist (and/or postPersist) and cancel creation throwing an exception.
Unfortunately it still display success flash bag but persist anything in your database.
How do I execute an asynchronous method from within a controller method and return an HttpStatusCodeResult(200), without the async delegate prematurely terminating its execution?
I am working on an asp.net application and one of my action my home controller takes long time to run(10-30 sec). I want to return an HttpStatusCodeResult(200) and keep running my function. The functoin need not to return anything, but still it's not really a case of fire and forget since I return a response to the server right away with HttpStatusCodeResult.
I tried using delegates, but it seems once I return the status code from the action, the delegate stops executing. Another option is to create a windows service but that would be like using a bazooka to kill a fly. I get very few requests so resources and performance is not an issue in my case. I heard ThreadPool.QueueUserWorkItem is an option, but which is most suitable for my case?
Create a static class that manages this long-running operation. This class will be responsible for creating threads to perform the task and also provides a way to check up on the status of any ongoing operations (i.e. if it's still in progress or has finished, and if so, what the result is).
Your MVC Controller method then uses this class to do the work and present status information or the completed processed data to the user.
Something like this:
public ActionResult GetSomething(String id) {
TaskResult result = TaskClass.GetStatus( id );
if( result == null ) { // the task has not been run, so start it up
TaskClass.StartNew( id );
return new View("PleaseWait");
} else if( !result.IsFinished ) {
return new View("PleaseWait");
} else {
return View("Results", result);
}
}
public static class TaskClass {
private static Dictionary<String,TaskResult> _tasks;
public static TaskResult GetStatus(String id) {
// TODO: Make this code thread-safe
if( _tasks.ContainsKey(id) ) return _tasks[id];
return null;
}
public static void Start(String id) {
_tasks.Add( id, new TaskResult("Working") );
Thread thread = new Thread( SomeExpensiveOperation );
thread.Start( id );
}
}
At the end of SomeExpensiveOperation there would be code that marks the TaskResult as finished.
I have a LINQ partial class:
public partial class resp
{
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (String.IsNullOrEmpty(respName))
yield return new RuleViolation("Responsibility name required", "respName");
yield break;
}
public bool IsValid
{
// Quick method for checking to see whether an object contains any RuleViolations
get { return (GetRuleViolations().Count() == 0); }
}
partial void OnValidate(ChangeAction action)
{
// Hook to LINQ to be notified before db is actually persisted .. and check to make sure resp is not used by respApprover or approvals
if (action == ChangeAction.Delete && ((respApprovers.Count() != 0 || approvals.Count() != 0)))
throw new ApplicationException("You cannot delete a responsibility that is in use");
if (!IsValid)
throw new ApplicationException("Rule violations prevent saving");
}
}
In my code I would like to be able to check whether a responsibility (dbo.resp) exists before it is deleted.
I am accomplishing this check with a hook to OnValidate as above ... if (action == ChangeAction.Delete) ... and returning an application exception if it fails.
Also, during normal validation, I would like to check to make sure rule violations aren't made (resp.respName in this case...)
So, normally I can just do a try { } catch { var errors = resps.GetRuleViolations() } and check whether I have validation errors. Of course, that doesn't know what action is being taken (Changeaction.Delete)
I guess my main question is, how can I have OnValidate return a reason rather than crash my whole app when the Changeaction is delete. Normally I would catch GetRuleViolations but if I put the 'if statement' in the GetRuleViolations ... it will always be true even during checks to see if the data was correct (in other words, respApprovers.Count() will matter even for checks on new insertions which I don't want to. I only want those count checks to be made on deletion)
Or possibly more simply stated: I want GetRuleViolations to tell me when someone attempts to delete resp that has respApprovers.Count() > 0 or approvals.Count() > 0 but only when the OnValidate Changeaction is delete.
Change GetRuleViolations to accept the ChangeAction parameter and add a parameterless version that calls it with ChangeAction.None (or Update or Insert, as appropriate). Then move your check in there as you want.
public IEnumerable<RuleViolation> GetRuleViolations( ChangeAction action )
{
if (action == ChangeAction.Delete) {
...
}
else {
...
}
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
return GetRuleViolations( ChangeAction.None );
}