For azure actor services, Actor Method Start Stop logs in Diagnostics window, which looks like below.
How can i add some additional detail such as a Correlation Id every time a method is called ?
{
"Timestamp": "2016-09-14T19:46:40.9955448+05:30",
"ProviderName": "Microsoft-ServiceFabric-Actors",
"Id": 7,
"Message": "Actor method is being invoked. Method name: IStore.GetStoreById, actor type: Backend.Actor.Store.Store, actor ID: STORE_6.",
"ProcessId": 30736,
"Level": "Verbose",
"Keywords": "0x0000F00000000002",
"EventName": "ActorMethod/Start",
"Payload": {
"methodName": "IStore.GetStoreById",
"methodSignature": "System.Threading.Tasks.Task`1[Backend.Models.Store.StoreView] GetStoreById(System.String)",
"actorType": "Backend.Actor.Store.Store",
"actorId": "STORE_6",
"actorIdKind": 2,
"replicaOrInstanceId": 131183360004211655,
"partitionId": "8af1c125-3666-40d0-b630-e3570c41833b",
"serviceName": "fabric:/MultiBannerBackend/StoreActorService",
"applicationName": "fabric:/MultiBannerBackend",
"serviceTypeName": "StoreActorServiceType",
"applicationTypeName": "MultiBannerBackendType",
"nodeName": "_Node_4"
}
}
In order to log custom data for every Actor operation, you can use these methods:
protected override Task OnPreActorMethodAsync(ActorMethodContext c)
protected override Task OnPostActorMethodAsync(ActorMethodContext c)
In order to get the call context, I've found that CallContext.LogicalGetData doesn't work in this situation. The Actor itself fortunately does know about its context. You can get it using some reflection.
For example:
protected override Task OnPreActorMethodAsync(ActorMethodContext c)
{
var correlationID = this.GetActorContext() ?? Guid.Empty.ToString("N");
string message = $"Actor method is being invoked. Method name: {c.MethodName}, actor type: {GetType().FullName}, actor ID: {Id}, CorrelationID:{correlationID}";
ActorEventSource.Current.ActorMessage(this, message);
return Task.FromResult(true);
}
protected override Task OnPostActorMethodAsync(ActorMethodContext c)
{
var correlationID = this.GetActorContext() ?? Guid.Empty.ToString("N");
string message = $"Actor method has completed. Method name: {c.MethodName}, actor type: {GetType().FullName}, actor ID: {Id}, CorrelationID:{correlationID}";
ActorEventSource.Current.ActorMessage(this, message);
return Task.FromResult(true);
}
Combined with:
public static class ActorContextExtensions
{
public static string GetActorContext(this Actor actor)
{
var concurrencyLockProperty = GetPropertyInfo("ConcurrencyLock", typeof(ActorBase));
var concurrencyLockPropertyValue = concurrencyLockProperty.GetValue(actor);
var currentContextProperty = GetPropertyInfo("Test_CurrentContext", concurrencyLockPropertyValue.GetType());
string currentContextPropertyValue = (string)currentContextProperty.GetValue(concurrencyLockPropertyValue);
return currentContextPropertyValue;
}
private static PropertyInfo GetPropertyInfo(string propertyName, IReflect owner)
{
var property = owner.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
if (property == null) throw new InvalidOperationException($"Failed to find property '{propertyName}' on '{owner}'.");
return property;
}
}
Obvious downside of this, is whenever the internals of ActorBase change, you'll need to change your reflection code accordingly.
Related
Summary: I am hoping to use Maui's builder.Services to resolve services in view models. But I don't understand how to do so.
I could create my own IServiceProvider, but I am hoping to avoid the needed boilerplate code, so seek a "standard Maui" solution.
I added the following line to MauiProgram.CreateMauiApp:
builder.Services.AddSingleton<IAlertService, AlertService>();
And the corresponding declarations (in other files):
public interface IAlertService
{
// ----- async calls (use with "await") -----
Task ShowAlertAsync(string title, string message, string cancel = "OK");
Task<bool> ShowConfirmationAsync(string title, string message, string accept = "Yes", string cancel = "No");
}
internal class AlertService : IAlertService
{
// ----- async calls (use with "await") -----
public Task ShowAlertAsync(string title, string message, string cancel = "OK")
{
return Application.Current.MainPage.DisplayAlert(title, message, cancel);
}
public Task<bool> ShowConfirmationAsync(string title, string message, string accept = "Yes", string cancel = "No")
{
return Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
}
}
Then in my BaseViewModel class:
internal class BaseViewModel
{
protected static IAlertService AlertSvc = ??? GetService (aka Resolve) ???
public static void Test1()
{
Task.Run(async () =>
await AlertSvc.ShowAlertAsync("Some Title", "Some Message"));
}
}
Question: How fill AlertSvc with the service registered in MauiProgram?
CREDIT: Based on a suggested "DialogService" in some SO discussion or Maui issue. Sorry, I've lost track of the original.
All the dependencies will be provided through the IServiceProvider implementation that is part of .NET MAUI. Luckily, the IServiceProvider itself is added to the dependency injection container as well, so you can do the following.
Add what you need to your dependency injection container in the MauiProgram.cs:
builder.Services.AddSingleton<IStringService, StringService>();
builder.Services.AddTransient<FooPage>();
For completeness, my StringService looks like this:
public interface IStringService
{
string GetString();
}
public class StringService : IStringService
{
public string GetString()
{
return "string";
}
}
Then in your FooPage, which can also be your view model or anything of course, add a constructor like this:
public FooPage(IServiceProvider provider)
{
InitializeComponent();
var str = provider.GetService<IStringService>().GetString();
}
Here you can see that you resolve the service manually through the IServiceProvider and you can call upon that. Note that you'll want to add some error handling here to see if the service actually can be resolved.
Is it acceptable to re-use a reference to a IReliableCollection or should I request from IReliableStateManager every time I want to use it?
For example, if I have a dictionary that is widely used in my application, is it acceptable to retrieve it once in the RunAsync method and then pass this reference to any method that requires it, e.g:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
_someCollection = StateManager.GetOrAddAsync<IReliableDictionary<int, string>>(
"SomeName");
}
public async Task DoSomething(int id, string message)
{
_someClass.DoSomething(_someCollection, id, message);
}
And then use in a class like so:
public class SomeClass
{
public void DoSomething(IReliableDictionary<int, string> dict, int id, string msg)
{
using (ITransaction tx = StateManager.CreateTransaction())
{
await dict.AddAsync(tx, id, msg);
await tx.CommitAsync();
}
}
}
Or should I request from IReliableStateManager on each call, e.g,
public class SomeClass
{
public void DoSomething(int id, string msg)
{
var dict = StateManager.GetOrAddAsync<IReliableDictionary<int, string>>("SomeName");
using (ITransaction tx = StateManager.CreateTransaction())
{
await dict.AddAsync(tx, id, msg);
await tx.CommitAsync();
}
}
}
Passing a reference seems to work fine from what I can tell but I'm not sure whether this would be considered bad practice and I can't find a definitive answer in the guidelines or documentation
Yes. You can.
You can check this to see how you can even receive notifications when new instance of IReliableState is added to ReliableStateManager.
You can also subscribe for events (if this is a dictionary).
Given a Document class, a mono-valued Property of the Entry Template is associated with a ChoiceList. This works well if the ChoiceList has no "sublevels" (Choice).
When a Group Choice is added and the user tries to fill the property, the dialog becomes ugly, as well as displayed below:
Is there a way to automatically unfold the tree view for the root Choices, and moreover to remove the "none" label ("Aucun" in french) as well as the symbolic name of the ChoiceList (blurred here)?
Do I have to write a Plugin to fix the issue?
Update. The purpose of "Aucun" here is to empty the field.
I contacted the support team, and in a few words, it's not possible "out of the box". But I found a workaround.
I wrote a ResponseFilter which catches the response of the request /p8/openContentClass. Turns out its response contains the ChoiceList values:
{
"classes": [{
"parentClassId": "<PARENTCLASSID>",
"template_name": "<ENTRYTEMPLATE>",
/* [...] */
}
],
/* [...] */
"criterias": [/* [...] */, {
"settability": "readWrite",
"defaultOperator": "EQUAL",
"minValue": null,
"uniqueValues": true,
"orderable": false,
"choiceList": {
"choices": /* <----- here */,
"displayName": "CL_ToFilter"
},
/* [...] */
"name": "<propertyName>"
}
]
}
Reformatting "choices" entry to get a one-level Choice List ensure a display on one level. Below the relevant code of the ResponseFilter:
public class ChoiceListValuesResponseFilter extends PluginResponseFilter {
public String[] getFilteredServices() {
return new String[] { "/p8/openContentClass"/* "/p8/openItem"*/ };
}
public void filter(String serverType, PluginServiceCallbacks callbacks,
HttpServletRequest request, JSONObject jsonResponse) throws Exception {
// [...]
JSONArray jsonProperties =
(JSONArray) jsonResponse.get("criterias");
Iterator it = jsonProperties.iterator();
while (it.hasNext()) {
JSONObject jo = (JSONObject) it.next();
if ("<PROPERTYWITHFILTEREDCL>".equals(jo.get("name"))) {
JSONObject choiceListJo = (JSONObject) jo.get("choiceList");
// do the processing here
break;
}
}
}
// [...]
}
I need to recollect some data calling to a method is connecting to a webservice.
problem: Imagine I need to update the content text of a label control according to this remote gathered information. Until all this data is recollected I'm not going to be able to show the label.
desired: I'd like to first show the label with a default text, and as I'm receiving this information I want to update the label content (please, don't take this description as a sucked code, I'm trying to brief my real situation).
I'd like to create an observable sequence of these methods. Nevertheless, these method have not the same signature. For example:
int GetInt() {
return service.GetInt();
}
string GetString() {
return service.GetString();
}
string GetString2 {
return service.GetString2();
}
These methods are not async.
Is it possible to create an observable sequence of these methods?
How could I create it?
Nevertheless, which's the best alternative to achieve my goal?
Creating custom observable sequences can be achieved with the Observable.Create. An example using your requirements is shown below:
private int GetInt()
{
Thread.Sleep(1000);
return 1;
}
private string GetString()
{
Thread.Sleep(1000);
return "Hello";
}
private string GetString2()
{
Thread.Sleep(2000);
return "World!";
}
private IObservable<string> RetrieveContent()
{
return Observable.Create<string>(
observer =>
{
observer.OnNext("Default Text");
int value = GetInt();
observer.OnNext($"Got value {value}. Getting string...");
string string1 = GetString();
observer.OnNext($"Got string {string1}. Getting second string...");
string string2 = GetString2();
observer.OnNext(string2);
observer.OnCompleted();
return Disposable.Empty;
}
);
}
Note how I have emulated network delay by introducing a Thread.Sleep call into each of the GetXXX methods. In order to ensure your UI doesn't hang when subscribing to this observable, you should subscribe as follows:
IDisposable subscription = RetrieveContent()
.SubscribeOn(TaskPoolScheduler.Default)
.ObserveOn(DispatcherScheduler.Current)
.Subscribe(text => Label = text);
This code uses the .SubscribeOn(TaskPoolScheduler.Default) extension method to use a TaskPool thread to start the observable sequence and will be blocked by the calls the Thread.Sleep but, as this is not the UI thread, your UI will remain responsive. Then, to ensure we update the UI on the UI thread, we use the ".ObserveOn(DispatcherScheduler.Current)" to invoke the updates onto the UI thread before setting the (data bound) Label property.
Hope this is what you were looking for, but leave a comment if not and I'll try to help further.
I would look at creating a wrapper class for your service to expose the values as separate observables.
So, start with a service interface:
public interface IService
{
int GetInt();
string GetString();
string GetString2();
}
...and then you write ServiceWrapper:
public class ServiceWrapper : IService
{
private IService service;
private Subject<int> subjectGetInt = new Subject<int>();
private Subject<string> subjectGetString = new Subject<string>();
private Subject<string> subjectGetString2 = new Subject<string>();
public ServiceWrapper(IService service)
{
this.service = service;
}
public int GetInt()
{
var value = service.GetInt();
this.subjectGetInt.OnNext(value);
return value;
}
public IObservable<int> GetInts()
{
return this.subjectGetInt.AsObservable();
}
public string GetString()
{
var value = service.GetString();
this.subjectGetString.OnNext(value);
return value;
}
public IObservable<string> GetStrings()
{
return this.subjectGetString.AsObservable();
}
public string GetString2()
{
var value = service.GetString2();
this.subjectGetString2.OnNext(value);
return value;
}
public IObservable<string> GetString2s()
{
return this.subjectGetString2.AsObservable();
}
}
Now, assuming that you current service is called Service, you would write this code to set things up:
IService service = new Service();
ServiceWrapper wrapped = new ServiceWrapper(service); // Still an `IService`
var subscription =
Observable
.Merge(
wrapped.GetInts().Select(x => x.ToString()),
wrapped.GetStrings(),
wrapped.GetString2s())
.Subscribe(x => label.Text = x);
IService wrappedService = wrapped;
Now pass wrappedService instead of service to your code. It's still calling the underlying service code so no need for a re-write, yet you still are getting the observables that you want.
This is effectively a gang of four decorator pattern.
is there a way to log username or user id (or some additional data) together with parameters on OnEntry/OnSuccess/OnException.
I need my log record to look like:
"... Method MethodName invoked with params [param1: value1, param2: value2 ...] by User : [username]"
Thanks.
The following code is taken from the Postsharp documentation site at Trace Sample with some minor modification
using System;
using System.Diagnostics;
using System.Reflection;
using PostSharp.Aspects;
namespace Samples
{
[Serializable]
public sealed class TraceAttribute : OnMethodBoundaryAspect
{
// This field is initialized and serialized at build time, then deserialized at runtime.
private readonly string category;
// These fields are initialized at runtime. They do not need to be serialized.
[NonSerialized] private string enteringMessage;
[NonSerialized] private string exitingMessage;
// Default constructor, invoked at build time.
public TraceAttribute()
{
}
// Constructor specifying the tracing category, invoked at build time.
public TraceAttribute(string category)
{
this.category = category;
}
// Invoked only once at runtime from the static constructor of type declaring the target method.
public override void RuntimeInitialize(MethodBase method)
{
string methodName = method.DeclaringType.FullName + method.Name;
this.enteringMessage = "Entering " + methodName;
this.exitingMessage = "Exiting " + methodName;
}
// Invoked at runtime before that target method is invoked.
public override void OnEntry(MethodExecutionArgs args)
{
Trace.WriteLine(this.enteringMessage, this.category);
DisplayArgs(args);
}
// Invoked at runtime after the target method is invoked (in a finally block).
public override void OnExit(MethodExecutionArgs args)
{
Trace.WriteLine(this.exitingMessage, this.category);
DisplayArgs(args);
}
}
}
private void DisplayArgs(MethodExecutionArgs args)
{
var parameters = args.Method.GetParameters();
var arguments = args.Arguments;
var zipped = parameters.Zip(arguments, (f,s) => f.Name + ":" + s == null ? "null" : s.ToString());
string traceLine = string.Format("invoked with params [{0}] by User:[{1}]", string.Join(",", zipped),
System.Security.Principal.WindowsIdentity.GetCurrent().Name);
System.Diagnostics.Trace.TraceInformation(traceLine);
}