How to invoke async callbacks in IEnlistmentNotification implementation - async-await

I have scenario to pass async function as callback to my own resource manager(which implements IEnlistmentNotification interface), and need to invoke asynchronously in prepare method, but it works when invoke as synchronous way, is there any way to make it without wait or asynchronous, the wait producing the AggregatorException rather than my custom exception?
Resource Manager
public class ResourceManager : IEnlistmentNotification
{
private Func<Task>? _doWorkCallback;
public async Task EnlistAsync(Func<Task> doWorkCallback)
{
_doWorkCallback = doWorkCallback;
var transaction = Transaction.Current;
if (transaction != null)
{
await transaction.EnlistVolatileAsync(this, EnlistmentOptions.None).ConfigureAwait(false);
}
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
try
{
_doWorkCallback?.Invoke().Wait();
preparingEnlistment.Prepared();
}
catch
{
preparingEnlistment.ForceRollback();
}
}
public void Commit(Enlistment enlistment)
{
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
}
public static class TranscationExtensions
{
public static Task EnlistVolatileAsync(this Transaction transaction,
IEnlistmentNotification
enlistmentNotification,
EnlistmentOptions enlistmentOptions)
{
return Task.FromResult(transaction.EnlistVolatile
(enlistmentNotification,
enlistmentOptions));
}
}
Usage Code
public class Test
{
private async Task DoWork()
{
Thread.Sleep(1000);// considerer here my custom exception
await Task.CompletedTask;
}
public async Task TestMethod()
{
ResourceManager rm = new ResourceManager();
await rm.EnlistAsync(async () => await DoWork().ConfigureAwait(false)).ConfigureAwait(false);
}
}

Related

DomainService not registered in AppService

I am trying to incorporate DomainService into my application, and tried to do it like the code below shows.
Here is the sample code for the manager:
namespace FlexSped.DefaultColors
{
public class DefaultColorManager : FlexSpedDomainServiceBase, IDefaultColorsManager
{
private readonly IRepository<DefaultColor> _defaultColorRepository;
public DefaultColorManager(IRepository<DefaultColor> defColorRep)
{
_defaultColorRepository = defColorRep;
}
public async Task Create(DefaultColor input)
{
await _defaultColorRepository.InsertAsync(input);
}
public Task Update(int id)
{
throw new NotImplementedException();
}
}
}
And this is the application service:
namespace FlexSped.DefaultColors
{
[AbpAuthorize(AppPermissions.Pages_Administration_DefaultColors)]
public class DefaultColorsAppService : FlexSpedAppServiceBase, IDefaultColorsAppService
{
private readonly IDefaultColorsManager _defaultColorManager;
private readonly IRepository<DefaultColor> _defaultColorRepository;
//private readonly IIocResolver _iocResolver;
public DefaultColorsAppService(IRepository<DefaultColor> defaultColorRepository, IDefaultColorsManager defColManager)
{
_defaultColorRepository = defaultColorRepository;
_defaultColorManager = defColManager;
//_iocResolver = iocResolver;
}
public async Task CreateOrEdit(CreateOrEditDefaultColorDto input)
{
if (input.Id == null)
{
await Create(input);
}
else
{
await Update(input);
}
}
[AbpAuthorize(AppPermissions.Pages_Administration_DefaultColors_Create)]
private async Task Create(CreateOrEditDefaultColorDto input)
{
DefaultColor dt = ObjectMapper.Map<DefaultColor>(input);
await _defaultColorManager.Create(dt);
}
}
}
All this produces this error:
'FlexSped.DefaultColors.DefaultColorsAppService' is waiting for the following dependencies:
- Service 'FlexSped.DefaultColors.IDefaultColorsManager' which was not registered.
Not sure what the problem is.I was following convention here.
ABPboiler is registering the services dependencies by name so your should match the implementation with definition. In your case:
IDefaultColors.IDefaultColorsManager should be IDefaultColors.IDefaultColorManager
or vice versa DefaultColorManager should be DefaultColorsManager.

Is it possible to Fetch user location from a Worker class?

I have to schedule a work to fetch user current location and update to server in a given interval (Even the app is not running).
I am trying to WorkManagerAPI to implement the functionality.
Is it possible to fetch the current location of the user from the doWork() method ?
locationManager.requestLocationUpdates(
provider, timeInterval, travelDistance, locationListener
);
When I request Location updates from the doWork() it throws below error.
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
As per my understanding, when implementing LocationManager.requestLocationUpdates() on a Worker thread, the call is being made on a non-UI, background thread created by WorkManager. LocationManager.requestLocationUpdates() is an asynchronous call possibly on another background thread. To handle the callbacks defined by the LocationListener, the calling thread must stay alive. Thats why the exception says,
Can't create handler inside thread that has not called Looper.prepare()
Check the code snippet below. Please consider this as pseudocode, I haven't tested this piece of code.
public class LocationWorker extends Worker {
String LOG_TAG = "LocationWorker";
private Context mContext;
private MyHandlerThread mHandlerThread;
public LocationWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
mContext = context;
}
#NonNull
#Override
public Result doWork() {
Log.d(LOG_TAG, "doWork");
mHandlerThread = new MyHandlerThread("MY_THREAD");
mHandlerThread.start();
Runnable runnable = new Runnable() {
#Override
public void run() {
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
String bestProvider = locationManager.getBestProvider(new Criteria(), true);
boolean permission = false;
if (PermissionChecker.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
PermissionChecker.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Log.e(LOG_TAG, "This app requires ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions.");
permission = true;
}
Log.d(LOG_TAG, "permission: "+permission);
Log.d(LOG_TAG, "bestProvider: "+bestProvider);
if (permission && bestProvider != null) {
MyLocationListener locListener = new MyLocationListener();
locationManager.requestLocationUpdates(bestProvider, 500, 1, locListener, mHandlerThread.getLooper());
}
}
};
mHandlerThread.post(runnable);
return Result.success();
}
class MyHandlerThread extends HandlerThread {
Handler mHandler;
MyHandlerThread(String name) {
super(name);
}
#Override
protected void onLooperPrepared() {
Looper looper = getLooper();
if (looper != null)
mHandler = new Handler(looper);
}
void post(Runnable runnable) {
if (mHandler != null)
mHandler.post(runnable);
}
}
class MyLocationListener implements LocationListener
{
#Override
public void onLocationChanged(final Location loc)
{
Log.d(LOG_TAG, "Location changed: " + loc.getLatitude() +","+ loc.getLongitude());
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
Log.d(LOG_TAG, "onStatusChanged");
}
#Override
public void onProviderDisabled(String provider)
{
Log.d(LOG_TAG, "onProviderDisabled");
}
#Override
public void onProviderEnabled(String provider)
{
Log.d(LOG_TAG, "onProviderEnabled");
}
}
}

How to pass the user context details from from bot Controller to FormDialog

Bot Info
SDK Platform: .NET
Active Channels: Direct Line
Deployment Environment: Azure Bot Service
Question
How to pass user context details from from bot Controller to FormDialog?
Code Example
public virtual async Task < HttpResponseMessage > Post([FromBody] Activity activity) {
if (activity != null && activity.GetActivityType() == ActivityTypes.Message) {
await Conversation.SendAsync(activity, () => {
return Chain.From(() => FormDialog.FromForm(RequestOrder.BuildEnquiryForm));
});
} else {
HandleSystemMessage(activity);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
}
public static IForm < RequestOrder > BuildEnquiryForm() {
return new FormBuilder < RequestOrder > ()
.Message("Hello {***Pass current user name?????****}Welcome to request bot!")
.Field(nameof(IsTermsAgreed))
.Field(nameof(ServiceRequired))
.AddRemainingFields()
.OnCompletion(ProcessParkingPermitRequest)
.Message("Thank you, I have submitted your request.")
.Build();
}
Fei Han answer is correct but using a static variable might lead to some unexpected error since all instances are sharing the same value. A better approach would be using the state of the form.
Request Order From
In your RequestOrder class you need to add a new member variable username.
public class RequestOrder
{
public string username;
/* Rest of your member variables */
}
The .Message method allows you to access the state of the form. You can get the username from the state of the form as below:
public static IForm < RequestOrder > BuildForm()
{
return new FormBuilder < RequestOrder > ()
.Message(async (state) => {
return new PromptAttribute($"Hi {state.username}, Welcome to request bot! ");
})
.Field(nameof(IsTermsAgreed))
.Field(nameof(ServiceRequired))
.AddRemainingFields()
.OnCompletion(ProcessParkingPermitRequest)
.Message("Thank you, I have submitted your request.")
.Build();
}
Root Dialog
In your root Dialog, before calling the BuildForm you need to create a new instance of your RequestOrder class and initialize username as the current user's name. Then pass your form to the BuildForm with option FormOptions.PromptInStart.
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var form = new RequestOrder()
{
username = context.Activity.From.Id
};
var requestOrderform = new FormDialog<RequestOrder>(form, RequestOrder.BuildForm, FormOptions.PromptInStart);
context.Call<RequestOrder>(requestOrderform, SampleFormSubmitted);
}
private async Task SampleFormSubmitted(IDialogContext context, IAwaitable<SampleForm> result)
{
try
{
var query = await result;
context.Done(true);
}
catch (FormCanceledException<SampleForm> e)
{
string reply;
if (e.InnerException == null)
{
reply = $"You quit. Maybe you can fill some other time.";
}
else
{
reply = $"Something went wrong. Please try again.";
}
context.Done(true);
await context.PostAsync(reply);
}
}
}
This is what you get:
In following sample code, I define a constructor of SandwichOrder class with a string type parameter, then I call FormDialog as a child dialog from root dialog (not from Messages Controller directly) and pass user name as parameter, which works for me, you can refer to it.
In RootDialog:
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var username = "Fei Han";
var myform = new Microsoft.Bot.Builder.FormFlow.FormDialog<SandwichOrder>(new SandwichOrder($"{username}"), SandwichOrder.BuildForm, Microsoft.Bot.Builder.FormFlow.FormOptions.PromptInStart, null);
context.Call<SandwichOrder>(myform, FormCompleteCallback);
}
private async Task FormCompleteCallback(IDialogContext context, IAwaitable<SandwichOrder> result)
{
await context.PostAsync($"The form is completed!");
context.Done(this);
}
}
In SandwichOrder class:
namespace BotFormFlowTest
{
public enum SandwichOptions
{
BLT, BlackForestHam, BuffaloChicken, ChickenAndBaconRanchMelt, ColdCutCombo, MeatballMarinara,
OvenRoastedChicken, RoastBeef, RotisserieStyleChicken, SpicyItalian, SteakAndCheese, SweetOnionTeriyaki, Tuna,
TurkeyBreast, Veggie
};
public enum LengthOptions { SixInch, FootLong };
public enum BreadOptions { NineGrainWheat, NineGrainHoneyOat, Italian, ItalianHerbsAndCheese, Flatbread };
public enum CheeseOptions { American, MontereyCheddar, Pepperjack };
public enum ToppingOptions
{
Avocado, BananaPeppers, Cucumbers, GreenBellPeppers, Jalapenos,
Lettuce, Olives, Pickles, RedOnion, Spinach, Tomatoes
};
public enum SauceOptions
{
ChipotleSouthwest, HoneyMustard, LightMayonnaise, RegularMayonnaise,
Mustard, Oil, Pepper, Ranch, SweetOnion, Vinegar
};
[Serializable]
public class SandwichOrder
{
public static string username = "User";
public SandwichOrder(string uname)
{
username = uname;
}
public SandwichOptions? Sandwich;
public LengthOptions? Length;
public BreadOptions? Bread;
public CheeseOptions? Cheese;
public List<ToppingOptions> Toppings;
public List<SauceOptions> Sauce;
public static IForm<SandwichOrder> BuildForm()
{
return new FormBuilder<SandwichOrder>()
.Message($"Hello {username}, Welcome to the simple sandwich order bot!")
.Build();
}
};
}
Test Result:

MassTransit's ISendObserver is not observing

I have a consumer that is also publishing a response back to the bus. I can get an IReceiveObserver wired up and working on the bus, but I haven't been able to get either an ISendObserver or IPublishObserver running. I have confirmed with RabbitMQ management console that the messages are being published correctly.
class Program
{
static BusHandle _BusHandle;
static void Main(string[] args)
{
InitLogging();
InitStructureMap();
InitBus();
System.Console.WriteLine("Starting processing, ENTER to stop...");
System.Console.ReadLine();
System.Console.WriteLine("See you later, alligator!");
StopBus();
}
static void InitBus()
{
var busCtrl = ObjectFactory.Container.GetInstance<IBusControl>();
var recObserver = ObjectFactory.Container.GetInstance<IReceiveObserver>();
var sendObserver = ObjectFactory.Container.GetInstance<ISendObserver>();
busCtrl.ConnectReceiveObserver(recObserver);
busCtrl.ConnectSendObserver(sendObserver);
_BusHandle = busCtrl.Start();
}
static void StopBus()
{
_BusHandle.Stop();
}
static void InitLogging()
{
XmlConfigurator.Configure();
Log4NetLogger.Use();
}
static void InitStructureMap()
{
ObjectFactory.Initialize(x => {
x.AddRegistry<MyTestConsoleRegistry>();
x.AddRegistry<MyTestRegistry>();
});
}
}
public class MyTestConsoleRegistry : Registry
{
public MyTestConsoleRegistry()
{
var rabbitURI = ConfigurationManager.AppSettings["rabbitMQHostUri"];
var queueName = ConfigurationManager.AppSettings["massTransitQueue"];
For<IBusControl>(new SingletonLifecycle())
.Use("Configure IBusControl for MassTransit consumers with RabbitMQ transport",
ctx => Bus.Factory.CreateUsingRabbitMq(cfg => {
cfg.UseJsonSerializer();
cfg.PublisherConfirmation = true;
var host = cfg.Host(new Uri(rabbitURI), rabbitCfg => { });
cfg.ReceiveEndpoint(host, queueName, endpointCfg => {
endpointCfg.LoadFrom(ctx);
});
})
);
For<IReceiveObserver>().Use<MassTransitObserver>();
For<ISendObserver>().Use<MassTransitObserver>();
// ...snip...
}
}
public class MyTestRegistry : Registry
{
public MyTestRegistry()
{
ForConcreteType<MyTestConsumer>();
// ...snip...
}
}
public class MassTransitObserver : IReceiveObserver, ISendObserver
{
// Does nothing for now, just trying to wire it up...
public Task ConsumeFault<T>(ConsumeContext<T> context, TimeSpan duration, string consumerType, Exception exception) where T : class
{
return Task.CompletedTask;
}
public Task PostConsume<T>(ConsumeContext<T> context, TimeSpan duration, string consumerType) where T : class
{
return Task.CompletedTask;
}
public Task PostReceive(ReceiveContext context)
{
return Task.CompletedTask;
}
public Task PreReceive(ReceiveContext context)
{
return Task.CompletedTask;
}
public Task ReceiveFault(ReceiveContext context, Exception exception)
{
return Task.CompletedTask;
}
public Task PreSend<T>(SendContext<T> context) where T : class
{
return Task.CompletedTask;
}
public Task PostSend<T>(SendContext<T> context) where T : class
{
return Task.CompletedTask;
}
public Task SendFault<T>(SendContext<T> context, Exception exception) where T : class
{
return Task.CompletedTask;
}
}
public class MyTestConsumer : IConsumer<MyTestMessage>,
// for testing only:
IConsumer<MyTestResponse>
{
readonly IDoSomething _DoSomething;
public TestConsumer(IDoSomething doSomething)
{
_DoSomething = doSomething;
}
public Task Consume(ConsumeContext<MyTestResponse> context)
{
// For testing only...
return Task.CompletedTask;
}
public async Task Consume(ConsumeContext<MyTestMessage> context)
{
var result = await _DoSomething(context.Message.Id);
var resp = new MyTestResponseMessage(result);
await context
.Publish<MyTestResponse>(resp);
}
}
Given this code, the IReceiveObserver methods are getting called, but the ISendObserver methods are not.
I'm new to MassTransit, I expect this is probably a straightforward issue.
EDIT: A unit test using NUnit and Moq, doesn't use StructureMap. I believe this properly illustrates what I'm seeing.
[Test]
public void TestSendObserver()
{
var bus = CreateBus();
var busHandle = bus.Start();
var sendObs = new Mock<ISendObserver>();
sendObs.Setup(x => x.PreSend<TestMessage>(It.IsAny<SendContext<TestMessage>>()))
.Returns(Task.FromResult(0))
.Verifiable();
sendObs.Setup(x => x.PostSend<TestMessage>(It.IsAny<SendContext<TestMessage>>()))
.Returns(Task.FromResult(0))
.Verifiable();
using (bus.ConnectSendObserver(sendObs.Object)) {
var pubTask = bus.Publish(new TestMessage { Message = "Some test message" });
pubTask.Wait();
}
busHandle.Stop();
// Fails, neither PreSend nor PostSend have been called
sendObs.Verify(x => x.PreSend<TestMessage>(It.IsAny<SendContext<TestMessage>>()), Times.Once());
sendObs.Verify(x => x.PostSend<TestMessage>(It.IsAny<SendContext<TestMessage>>()), Times.Once());
}
IBusControl CreateBus()
{
return MassTransit.Bus.Factory.CreateUsingRabbitMq(x => {
var host = x.Host(new Uri("rabbitmq://localhost/"), h => {
h.Username("guest");
h.Password("guest");
});
});
}
public class TestMessage
{
public String Message { get; set; }
}

How to use SqlAzureExecutionStrategy and "Nolock"

To deal with SQL timeouts I'm trying to use SqlAzureExecutionStrategy (https://msdn.microsoft.com/en-us/data/dn456835.aspx)
The problem I am running into is it prevents "user initiated transactions" which seem to be the recommended way to implement "with (nolock)" in EF (http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspx, NOLOCK with Linq to SQL).
example code
public AspnetUser GetAspnetUserByUserName(string userName)
{
using (var tx = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }))
{
return context.AspnetUsers.Where(x => x.UserName == userName).FirstOrDefault();
}
}
throws error
The configured execution strategy 'SqlAzureExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.
I've seen the answers that say to turn off the SqlAzureExecutionStrategy on a per call basis, but that would defeat the purpose of using it, if all my reads ignored the strategy. It is possible to have both "NoLock" and SqlAzureExecutionStrategy
SqlAzureExecutionStrategy doesn't support transactions initiated outside the action to be retried. To work around this restriction you would need to suspend the strategy, create the transaction scope and do the work as an action that you manually pass to the execution strategy to be retried:
public AspnetUser GetAspnetUserByUserName(string userName)
{
new SuspendableSqlAzureExecutionStrategy().Execute(() =>
{
using (var tx = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }))
{
return context.AspnetUsers.Where(x => x.UserName == userName).FirstOrDefault();
}
});
}
Here I am using an alternative to the suspendable strategy from https://msdn.microsoft.com/en-us/data/dn307226 that will suspend any nested invocations automatically:
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;
using System.Data.Entity.Utilities;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Threading.Tasks;
public class SuspendableSqlAzureExecutionStrategy : IDbExecutionStrategy
{
private readonly IDbExecutionStrategy _azureExecutionStrategy;
public SuspendableSqlAzureExecutionStrategy()
{
_azureExecutionStrategy = new SqlAzureExecutionStrategy();
}
private static bool Suspend
{
get { return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false; }
set { CallContext.LogicalSetData("SuspendExecutionStrategy", value); }
}
public bool RetriesOnFailure
{
get { return !Suspend; }
}
public virtual void Execute(Action operation)
{
if (!RetriesOnFailure)
{
operation();
return;
}
try
{
Suspend = true;
_azureExecutionStrategy.Execute(operation);
}
finally
{
Suspend = false;
}
}
public virtual TResult Execute<TResult>(Func<TResult> operation)
{
if (!RetriesOnFailure)
{
return operation();
}
try
{
Suspend = true;
return _azureExecutionStrategy.Execute(operation);
}
finally
{
Suspend = false;
}
}
public virtual async Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken)
{
if (!RetriesOnFailure)
{
await operation();
return;
}
try
{
Suspend = true;
await _azureExecutionStrategy.ExecuteAsync(operation, cancellationToken);
}
finally
{
Suspend = false;
}
}
public virtual async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken)
{
if (!RetriesOnFailure)
{
return await operation();
}
try
{
Suspend = true;
return await _azureExecutionStrategy.ExecuteAsync(operation, cancellationToken);
}
finally
{
Suspend = false;
}
}
}
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SuspendableSqlAzureExecutionStrategy());
}
}

Resources