Imagine there is a method in appservice which upgrade data and finally a report is made in Pdf form through Ssrs. All methods are unit of work, so data is not committed in server until calling method uow.CompleteAsync(). therefore GenerateSampleReport() can not be called. To solve the problem we have used [unit of work (Isdisabled = true) so the method is written so:
[UnitOfWork(IsDisabled =true)]
public async Task Method()
{
using (var uow = UnitOfWorkManager.Begin())
{
//some data manipulation
await uow.CompleteAsync();
}
var pdfFile GenerateSampleReport();
}
But, while providing the report, if there is any exception, report can not be generated and data manipulation must be roll backed(problem about data integrity). How to solve the problem?
you shouldn't begin your own Unit Of Work. And don't disable UnitOfWork. Normally if Method() is an application service method then this should work as you want.
public async Task Method()
{
//some data manipulation...
var pdfFile GenerateSampleReport();//if any exception occurs in this line, the db transaction will be rolled back.
}
have you checked this before?
Related
I have the following method in an #Service class which has #Transactional defined:
#Override
public Result add(#NonNull final UserSaveRequest request) {
final Result<Email> emailResult = Email.create(request.getEmail());
final Result<UserFirstName> userFirstNameResult = UserFirstName.create(request.getFirstName());
final Result<UserLastName> userLastNameResult = UserLastName.create(request.getLastName());
final Result combinedResult = Result.combine(emailResult, userFirstNameResult, userLastNameResult);
if (combinedResult.isFailure()) {
return Result.fail(combinedResult.getErrorMessage());
}
final Result<User> userResult = User.create(emailResult.getValue(), userFirstNameResult.getValue(), userLastNameResult.getValue());
if (userResult.isFailure()) {
return Result.fail(userResult.getErrorMessage());
}
this.userRepository.save(userResult.getValue());
return Result.ok();
}
Now as you can see I utilize a Result class which can contain a return value or an error message as I don't think using exceptions for flow control is very clean.
The problem I now have is; the complete method is bound in one transaction and if one database call should fail the whole transaction will be rolled back. In my model however, after the this.userRepository.save(userResult.getValue()); call, if something would happen that would force me to return a failed result, I can't undo that save(userResult.getVlaue()); call seeing as I don't use exceptions for flow control.
Is this a problem that has an elegant solution, or is this a place where I need to make a trade-off between using exceptions as flow control and having to mentally keep track of the ordering of my statements in these kind of situations?
Yes, you can trigger rollback manually. Try this:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
More information: https://docs.spring.io/spring/docs/5.0.7.RELEASE/spring-framework-reference/data-access.html#transaction-declarative-rolling-back
I have set up a signalR website .net core. My function in my hub is:
public async Task Notify(int id) {
await Clients.All.InvokeAsync("Notified", id);
}
I have also tested this with the following js:
let connection = new signalR.HubConnection(myURL);
connection.on('Notified', data => {
console.log(4, data);
});
connection.start();
The js code seems to work fine and I see the log when I try connection.Invoke('Notify').
Now I have a console app that can needs to make the invoke. I am trying this in two ways and don't mind either solution:
1. A mvc controller within the signalR website that can take the id and invoke 'Notified'.
2. Use the client library Microsoft.AspNetCore.SignalR.Client in the console app.
The way 1 I have only done in classic asp.net like this:
GlobalHost.ConnectionManager.GetHubContext(hubName)
But couldn't find a way to do this in .net core.
Way 2 I have used the library and tried this so far:
var con = new HubConnectionBuilder();
con.WithUrl(myURL);
var connection = con.Build();
connection.InvokeAsync("Notify",args[0]).Wait();
This is the closest I have come to create a connection in the same way as the js code. However this code throws a null pointer when calling connection.InvokeAsync. The connection object is not null. It seems to be an internal object that is null. According to the stack trace the exception is thrown when a MoveNext() function is internally called.
Well looks like both are not currently possible. As of now I just used a forced way which is hopefully temporary.
I have created and used the following base class for hubs:
public abstract class MyHub : Hub
{
private static Dictionary<string, IHubClients> _clients = new Dictionary<string, IHubClients>();
public override Task OnConnectedAsync()
{
var c = base.OnConnectedAsync();
_clients.Remove(Name);
_clients.Add(Name, Clients);
return c;
}
public static IHubClients GetClients(string Name) {
return _clients.GetValueOrDefault(Name);
}
}
GlobalHost is gone. You need to inject IHubContext<THub> like in this sample.
This can be a bug in SignalR alpha1. Can you file an issue on https://github.com/aspnet/signalr and include a simplified repro?
I'm trying to implement a wrapper for the Amazon IAP Plugin for Xamarin. It uses an event based system in the following way:
You can initiate method calls and listen for events. Method calls initiate requests, some of which return a response. Events are asynchronous system-generated messages that are sent in response to method calls to return the requested data to you.
See more here
My goal is to wrap this event based system into some API which allows me to use the plugin with tasks, so I can use the async-await syntax. To achieve that I'm using the TaskCompletionSource like in the following example:
public async Task<bool> GetProductInfoAsync(params string[] productIds)
{
var iapService = AmazonIapV2Impl.Instance;
var tcs = new TaskCompletionSource<bool>();
var skus = new SkusInput { Skus = productIds.ToList() };
var requestId = iapService.GetProductData(skus).RequestId;
GetProductDataResponseDelegator delegator = null;
delegator = new GetProductDataResponseDelegator(response =>
{
if(response.Id == requestId) {
var result = GetResultFromResponse(response);
tcs.SetResult(result);
//iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
}
});
iapService.AddGetProductDataResponseListener(delegator.responseDelegate);
return await tcs.Task;
}
This code seems to work fine if the method gets called once, but if it gets called two times in a row the app crashes immediately and the only thing printed to the console is the following message..
[mono] Unhandled Exception:
[mono] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
..which kinda makes no sense at all.
So is there something obvious I'm missing here? Or could it be a bug from the plugin?
I have created a repository with the code above so you can reproduce the problem. It's my playground, so please ignore the whole structure of the project and just focus on the classes AmazonIAPService and MainActivity.
Hint 1:
The commented line //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate); causes also a crash with the same message but already at the first call of the method.
Hint 2:
The AmazonIAPService contains a commented method which uses await Task.Delay(TimeSpan.FromMilliseconds(1)) and solves the problem from above in a very hacky way which I really don't like.
Problem seems to be that those functions have to run asynchronously. Also mentioned here in the doc. So once you run those functions synchronously somehow they throw exception, i dont what is happening in the library but your hacky solution is the actual solution for that. If you write the function as below. it also works.
PurchaseResponseDelegator delegator = null;
delegator = new PurchaseResponseDelegator(async response =>
{
await Task.Run(() =>
{
if (response.RequestId == requestId)
{
var result = GetPurchaseEventHandler(response);
var sucess = taskCompletionSource.TrySetResult(result);
context.RemovePurchaseResponseListener(delegator.responseDelegate);
}
} );
});
// Register for an event
context.AddPurchaseResponseListener(delegator.responseDelegate);
One other exception I had despite the async-await solution, somehow, it always throws exception for the line taskCompletionSource.SetResult(result); for PurchaseUpdates functions only. if i use instead this line var sucess = taskCompletionSource.TrySetResult(result); it works fine
I can't seem to find this on their site or here. I'm hoping the answer is YES. Specifically I want to write a Verifier to check for unique key violations (I know I will have to raise the isolation level to Serializable). This won't work unless the Verifier runs in the same transaction as the Save.
DevForce does not use the same transaction for validation and save processing. Within the context of an EntityServerSaveInterceptor, authorization and validation are performed and then a TransactionScope is opened when doing the actual save. If you do a query within a verifier it will use a separate TransactionScope.
You can work around this behavior with a little extra work in your custom EntityServerSaveInterceptor. Override the ValidateSave function to bypass validation, then override the ExecuteSave method to open a TransactionScope and then do your validation logic before calling the base save logic. The TransactionScope opened by DF during the save will enlist in your TransactionScope. Something like this:
public class EntityServerSaveManager : EntityServerSaveInterceptor {
protected override bool ValidateSave() {
// will do validation later
return true;
}
protected override bool ExecuteSave() {
using (var ts = new TransactionScope(TransactionScopeOption.Required, this.SaveOptions.TransactionSettings.ToTransactionOptions())) {
// Do validation logic now
...
// Now do save
base.ExecuteSave();
ts.Complete();
}
}
}
I have WebAPI implementation with method like this:
public IEnumerable<Device> GetAllDevices()
public Device GetDeviceById(int id)
Looks ok, it works when running in IIS or selfhosted. Returns JSON objects correctly.
However first method fails in my unit test where I attempt to use inmemory server.
System.InvalidOperationException : Cannot create and populate list type System.Linq.IQueryable`1[Test.Device].
This goes down to Newtonsoft.Json.Serialization assembly. An example of the test follow:
[Test]
public void GET_AskingForListOfDevices_GettingOk200WithListOfDevicesInJSON()
{
var client = new HttpClient(InMemoryServer);
HttpRequestMessage request = CreateRequest("api/devices", "application/json", HttpMethod.Get);
using (HttpResponseMessage response = client.SendAsync(request).Result)
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
Assert.NotNull(response.Content);
Assert.AreEqual("application/json", response.Content.Headers.ContentType.MediaType);
// next line throws exc
var content = response.Content.ReadAsAsync<IQueryable<Device>>().Result;
Assert.AreEqual(3, content.Count());
}
request.Dispose();
}
Any idea where to look?
UPDATE
The example below throws that error, however I found the solution to avoid it. Just by using IList<> instead of IQueryable<>. Still It does not answer me the question why it's working in Fiddler. Does it use the same trick?
I am running into same situation. IQueryable throws exception but if you use IEnumerable then it works.