How one can be notified that a variable goes out of scope in Workflow Foundation 4? - workflow-foundation-4

We are using Workflow Foundation 4 to implement custom logic in our application. One particular thing is that we are using variables of a custom type that are associated with a ressource in an external system.
When such a variable is no longer in use in a workflow, I would like to dispose of the corresponding resource in the external system.
How can my custom host be notified at runtime that my variable goes out of scope and/or is disposed. Do I need my variable objects to derive from a particular class or interface ? Do I need to inject a particular extension in the workflow instance ?

One way could be to implement a custom TrackingParticipant. This can be used to watch for when an activity's state changes to a closed state. When it is closed, you can inspect the arguments to see if any are of a resource that you'd like to clean up.
It could look something like this:
public interface IResource
{
}
public class MyTrackingParticipant : TrackingParticipant
{
private readonly MyResourceManager resourceManager;
public MyTrackingParticipant(MyResourceManager resourceManager)
{
this.resourceManager = resourceManager;
}
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
var activityStateRecord = record as ActivityStateRecord;
if (activityStateRecord != null && activityStateRecord.State == ActivityStates.Closed)
{
// Scan arguments to see if resources should be deallocated from resource manager.
foreach (var keyValuePair in activityStateRecord.Arguments)
{
// If the argument is of a resource type...
var resource = keyValuePair.Value as IResource;
if (resource != null)
this.resourceManager.DeallocateResource(resource);
}
}
}
}
And using the custom tracking participant is just like any other WF extension:
var resourceManager = new MyResourceManager();
var wfResourceTrackingParticipant = new MyTrackingParticipant(resourceManager);
var workflow1 = new Workflow1();
var workflowApp = new WorkflowApplication(workflow1);
workflowApp.Extensions.Add(wfResourceTrackingParticipant);

Related

Unity to DryIoC conversion ParameterOverride

We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).
Example of the scenario:
//Called from service into custom resolver method
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", IObjectWithData)
};
//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();
foreach(var parameterOverride in parameterOverrides)
{
resolverOverrides.Add(parameterOverride);
}
return _container.Resolve<T>(resolverOverrides.ToArray());
You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:
internal class SomeFactory : IProductViewFactory
{
public SomeFactory( IService dependency )
{
_dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
}
#region IProductViewFactory
public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
#endregion
#region private
private readonly IService _dependency;
#endregion
}
See this, too:
For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.
For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.
For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.
Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.
First, I would agree with the #haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.
Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.
using System;
using DryIoc;
public class Program
{
record ParameterOverride(string Name, object Value);
record Product(int productID);
public static void Main()
{
// get container somehow,
// if you don't have an access to it directly then you may resolve it from your service provider
IContainer c = new Container();
c.Register<Product>();
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", "blah"),
};
var parameterRules = Parameters.Of;
foreach (var po in parameterOverrides)
{
parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
}
c = c.With(rules => rules.With(parameters: parameterRules));
var s = c.Resolve<Product>();
Console.WriteLine(s.productID);
}
}

Inject multiple implementations in abp.io .NET5/6/Core

UPDATE 2: FIXED THE CODE at the end
I have the abp.io service below with 2 parameters in the constructor instantiated via DI.
One of them, IOutcomeWriter, has 2 implementations.
I'd like to define at runtime which of the implementations of IOutcomeWriter to use.
This is the main service:
public class UCManagerService
: DomainService, IUCManagerService, ITransientDependency {
private readonly IUCInputReader _inputReader;
// This field can have 2 or 3 implementations.
private readonly IOutcomeWriter _outcomeWriter;
public UCManagerService(
IUCInputReader inputReader, IOutcomeWriter outcomeWriter) {
_inputReader = inputReader;
_outcomeWriter = outcomeWriter;
}
public async Task ExecuteAsync() {
// start processing the input and generate the output
var input = _inputReader.GetInput());
// do something
// ...
_outcomeWriter.Write(something);
}
}
The main service is registered in the AbpModule together with with IUCInputReader and the 2 implementations of IOutcomeWriter:
[DependsOn(
typeof(SwiftConverterDomainModule),
typeof(AbpAutofacModule) // <= use Autofac in some way (I don't know how)
)]
public class ProgramAppModule : AbpModule {
public override void ConfigureServices(ServiceConfigurationContext context) {
context.Services.AddTransient<IUCManagerService, UCManagerService>();
context.Services.AddTransient<IUCInputReader, UCInputReader>();
// 2 implementations of IOutcomeWriter
context.Services.AddTransient<IOutcomeWriter, OutcomeWriter1>();
context.Services.AddTransient<IOutcomeWriter, OutcomeWriter2>();
}
}
What I would like is to instantiate UCManagerService sometimes with OutcomeWriter1 and sometimes with OutcomeWriter2, according to some values in appsettings.json:
IList<JobSetting> jobsToSet = _configuration.GetSection("Jobs")
.Get<List<JobSetting>>();
foreach (JobSetting jobToSet in jobsToSet) {
// If jobsToSet.SomeValue == 'MyValue1' following line should have to
// require a IUCManagerService using OutcomeWriter1. If it is
// 'MyValue2' it'd use OutcomeWriter2, and so on:
var service = abpApplication.ServiceProvider.GetRequiredService<IUCManagerService>(); // ???
// do something else with service
// ...
}
Finally, if a tomorrow I add an OutcomeWriter3 I would just like to register it in ProgramAppModule.ConfigureServices(...) and of course use a different key in appsettings.json.
If I understand correctly, you need the IOutcomeWriter to differ based on the currently executed job. In other words, that means that you need to dynamically switch the writer based on its context.
The fact that it you need to change it dynamically, it means that is not a problem that can be solved solely using your DI configuration, because DI configurations are best kept static.
Instead, you need to mix and match a few concepts. First of all, you need a way to set the used job in the context. For instance:
// DI configuration
services.AddScoped<JobContext>();
// Execution of a job
using (var scope = abpApplication.ServiceProvider.CreateScope())
{
var context = scope.GetRequiredService<JobContext>();
context.CurrentJob = typeof(MyFirstJob);
var job = scope.GetRequiredService<MyFirstJob>();
var job.Execute();
}
In this example, JobContext is a class that holds the data that is used during the execution of a certain job. It is registered as Scoped to allow this data to be available for multiple classes within the same scope.
Now using this new JobContext, you can build an adapter for IOutcomeWriter that can forward the incoming call to the right implementation based on its injected JobContext. This might look as follows:
public class JobSpecificOutcomeWriter : IOutcomeWriter
{
private readonly JobContext context;
private readonly IList<JobSetting> settings;
private readonly IEnumerable<IOutcomeWriter> writers;
public JobSpecificOutcomeWriter(
JobContext context,
IList<JobSetting> settings,
IEnumerable<IOutcomeWriter> writers)
{
this.context = context;
this.settings = settings;
this.writers = writers;
}
// Implement all IOutcomeWriter methods by forwarding them to the
// CurrentWriter.
object IOutcomeWriter.SomeMethod(object a) =>
this.CurrentWriter.SomeMethod(a);
private IOutcomeWriter CurrentWriter
{
get
{
// TODO: Based on the current context and the settings,
// select the proper outcome writer from the writers list.
}
}
}
When JobSpecificOutcomeWriter is injected into UCManagerService (or any component for that matter), it transparently allows the proper writer to be used, without the consuming class from knowing about this.
The tricky part, actually, is to now configure your DI container correctly using JobSpecificOutcomeWriter. Depending on which DI Container you use, your mileage might vary and with the MS.DI Container, this is actually quite complicated.
services.AddTransient<IOutcomeWriter>(c =>
new JobSpecificOutcomeWriter(
context: c.GetRequiredService<JobContext>(),
settings: jobsToSet,
writers: new IOutcomeWriter[]
{
c.GetRequiredService<MyFirstJob>(),
c.GetRequiredService<MySecondJob>(),
c.GetRequiredService<MyThirdJob>(),
});
services.AddTransient<MyFirstJob>();
services.AddTransient<MySecondJob>();
services.AddTransient<MyThirdJob>();

.NET Core default dependency injection with Castle DynamicProxy

I have many AOP libraries that use Castle DynamicProxy with Autofac DI container for logging, auditing, transaction control, etc.
I wonder if there is a way to declare interceptors using the default .NET Core DI container. It will be good to have this flexibility since many .NET Core projects don't use Autofac.
Yes, you can use DynamicProxy using Core DI. I've written up a blog post explaining it at http://codethug.com/2021/03/17/Caching-with-Attributes-in-DotNet-Core5/, but here is the code for it:
Create an attribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CacheAttribute : Attribute
{
public int Seconds { get; set; } = 30;
}
Create an interceptor (requires Castle.Core nuget package)
public class CacheInterceptor : IInterceptor
{
private IMemoryCache _memoryCache;
public CacheInterceptor(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
// Create a cache key using the name of the method and the values
// of its arguments so that if the same method is called with the
// same arguments in the future, we can find out if the results
// are cached or not
private static string GenerateCacheKey(string name,
object[] arguments)
{
if (arguments == null || arguments.Length == 0)
return name;
return name + "--" +
string.Join("--", arguments.Select(a =>
a == null ? "**NULL**" : a.ToString()).ToArray());
}
public void Intercept(IInvocation invocation)
{
var cacheAttribute = invocation.MethodInvocationTarget
.GetCustomAttributes(typeof(CacheAttribute), false)
.FirstOrDefault() as CacheAttribute;
// If the cache attribute is added ot this method, we
// need to intercept this call
if (cacheAttribute != null)
{
var cacheKey = GenerateCacheKey(invocation.Method.Name,
invocation.Arguments);
if (_memoryCache.TryGetValue(cacheKey, out object value))
{
// The results were already in the cache so return
// them from the cache instead of calling the
// underlying method
invocation.ReturnValue = value;
}
else
{
// Get the result the hard way by calling
// the underlying method
invocation.Proceed();
// Save the result in the cache
var options = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
new System.TimeSpan(hours: 0, minutes: 0,
seconds: cacheAttribute.Seconds)
};
_memoryCache.Set(cacheKey, invocation.ReturnValue,
options);
}
}
else
{
// We don't need to cache the results,
// nothing to see here
invocation.Proceed();
}
}
}
Add an extension method to help register classes in DI:
public static void AddProxiedScoped<TInterface, TImplementation>
(this IServiceCollection services)
where TInterface : class
where TImplementation : class, TInterface
{
// This registers the underlying class
services.AddScoped<TImplementation>();
services.AddScoped(typeof(TInterface), serviceProvider =>
{
// Get an instance of the Castle Proxy Generator
var proxyGenerator = serviceProvider
.GetRequiredService<ProxyGenerator>();
// Have DI build out an instance of the class that has methods
// you want to cache (this is a normal instance of that class
// without caching added)
var actual = serviceProvider
.GetRequiredService<TImplementation>();
// Find all of the interceptors that have been registered,
// including our caching interceptor. (you might later add a
// logging interceptor, etc.)
var interceptors = serviceProvider
.GetServices<IInterceptor>().ToArray();
// Have Castle Proxy build out a proxy object that implements
// your interface, but adds a caching layer on top of the
// actual implementation of the class. This proxy object is
// what will then get injected into the class that has a
// dependency on TInterface
return proxyGenerator.CreateInterfaceProxyWithTarget(
typeof(TInterface), actual, interceptors);
});
}
Add these lines to ConfigureServices in Startup.cs
// Setup Interception
services.AddSingleton(new ProxyGenerator());
services.AddScoped<IInterceptor, CacheInterceptor>(
After that, if you want to use the cache interceptor, you need to do two things:
First, add the attribute to your method
[Cache(Seconds = 30)]
public async Task<IEnumerable<Person>> GetPeopleByLastName(string lastName)
{
return SomeLongRunningProcess(lastName);
}
Second, register the class in DI using the Proxy/Interception:
services.AddProxiedScoped<IPersonRepository, PersonRepository>();
Instead of the normal way without the Proxy/Interception:
services.AddScoped<IPersonRepository, PersonRepository>();
The base .NET Core container does not have any extra features like interceptors. The whole reason the DI container in .NET Core can be swapped out for something like Autofac is so you can move to a different container once you outgrow the default one.

.NET and Castle DynamicProxy - How to check if one interception is "inside" another interception

I have an AOP auditing library that intercepts methods using Castle DynamicProxy with Autofac and checks if they have this specific attribute:
[Audit(AuditOperation.Create), MethodImpl(MethodImplOptions.NoInlining)]
public virtual TEntity Add(TEntity entity, [MethodIsAuditableParam]bool methodIsAuditable = true)
{
entity = entity.DeleteNestedProperties();
using (var entityContext = ContextCreator())
{
TEntity addedEntity = entityContext.Set<TEntity>().Add(entity);
entityContext.SaveChanges();
return addedEntity;
}
}
This is a generic repository method that adds a generic entity to the database. But I also have specific methods in my Domain layer that do additional stuff and then call these generic repository methods:
[Audit(AuditOperation.Create), MethodImpl(MethodImplOptions.NoInlining)]
public virtual User AddUser(int currentUserId, User newUser, [MethodIsAuditableParam]bool methodIsAuditable = true)
{
try
{
UserDBValidations(currentUserId, newUser);
if (newUser.UserProfileId == (int)UserProfileType.Admin ||
newUser.UserProfileId == (int)UserProfileType.ProcessSpecialist)
newUser.UGBs = _ugbRepository.FindAll().ToList();
_userRepository.Add(newUser);
return newUser;
}
catch (Exception ex)
{
throw new Exception("Erro no cadastro de usuário: " + ex.Message);
}
}
My intention is that if an auditable Domain method (that can call many generic repository methods) exists, my auditing interceptor will audit only this Domain method and will ignore the generic repository audition. But if I call the generic repository methods directly, they will be audited normally.
I tried to achieve this behavior using the stack trace in my interceptor. This following method checks if the stack frames array already contains an IInterceptor.Intercept method. :
private bool CheckIfMethodCallersAreBeingIntercepted()
{
StackTrace stackTrace = new StackTrace();
StackFrame[] stackFrames = stackTrace.GetFrames();
//the int number should match the exact number of method calls in this interception until this current method
StackFrame[] realStackFrames = stackFrames.SubArray(5, stackFrames.Count() - 5);
foreach (StackFrame realStackFrame in realStackFrames)
{
MethodBase method = realStackFrame.GetMethod();
if (method == null)
continue;
Type declaringType = method.DeclaringType;
if (declaringType == null)
continue;
string declaringTypeFullName = declaringType.FullName;
if (declaringTypeFullName == null)
continue;
if(declaringTypeFullName
.Contains("AwesomeAuditing.AwesomeAuditingInterceptor"))
return true;
}
return false;
}
But this doesn't work because I can intercept a method and don't audit it:
public void Intercept(IInvocation invocation)
{
if (!CheckIfMethodShouldBeIntercepted(invocation.Method, invocation.Arguments))
{
invocation.Proceed();
return;
}
if (!CheckIfMethodReturnTypeInheritAuditEntity(invocation.Method))
throw new AwesomeAuditingException(#"The audited method's return type most inherit the IAuditableEntity interface.");
invocation.Proceed();
if (CheckIfMethodIsAsync(invocation.Method))
invocation.ReturnValue = AsyncProceedExecution((dynamic)invocation.ReturnValue, invocation);
else
AfterProceedExecution(invocation.ReturnValue, invocation);
}
I don't want to manage a static collection with all current intercepted methods because I want this AOP library to work on many types of projects (Web API, Windows Services, Console Applications, WPF, etc.) and each type has its own way to manage static instances. For example, in a Web API context I don't want that two HTTP request share this static collection. In a Windows Service I don't want to share this static collection between two execution threads.
Do you guys know a better way to manage this multi level interception? Do you know any good practice that will work good in many project types?

What is the easiest way to add custom dimensions to default Request Telemetry for App service?

I just leverage default Application Insights logging to log the request telemetry without ANY custom code.
The request telemetry looks like this:
timestamp [UTC] 2019-12-19T00:22:10.2563938Z
id |a758472d124b6e4688a33b2ad9755f33.b3979544_
name GET MyMethod [type]
url https://xxxx
success True
resultCode 200
duration 153.2676
performanceBucket <250ms
itemType request
customDimensions
AppId xxxx-xxxx-xxxx-xxxx-xxxxxx
AspNetCoreEnvironment: west us
_MS.ProcessedByMetricExtractors (Name:'Requests', Ver:'1.1')
Now I want to add a new property to customDimensions in Request telemetry, say, correlationId. What is the easiest way to to it? I just want to expend the existing request telemetry, don't want to create new event.
If you're interested in massaging data (i.e. modify based on what's available in telemetry item itself) then Ivan's answer is the right one.
If you'd like to add something to existing request then you need to do a few things:
1) Use Activity.Tags property bag while in a request
Activity.Current?.AddTag("TagName", "TagValue");
2) Have Telemetry initializer which puts tags as custom dimensions (in next versions we might add it as default initializer and this step will no longer be required)
/// <summary>
/// Helper class to workaround AI SDK not collecting activity tags by default.
/// This initializer allows the business logic to have no AI references.
/// </summary>
public class ActivityTagsTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var activity = Activity.Current;
var requestTelemetry = telemetry as ISupportProperties;
if (requestTelemetry == null || activity == null) return;
foreach (var tag in activity.Tags)
{
requestTelemetry.Properties[tag.Key] = tag.Value;
}
}
}
3) Register in Startup
services.AddSingleton<ITelemetryInitializer, ActivityTagsTelemetryInitializer>();
For adding custom dimensions, you can take use of ITelemetryInitializer.
Here is an example for a .NET core web project:
1.Add a class named MyTelemetryInitializer to the project, and the code like below:
public class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
//if it's not a request, just return.
if (requestTelemetry == null) return;
if (!requestTelemetry.Properties.ContainsKey("correlationId"))
{
requestTelemetry.Properties.Add("correlationId", "id_123456");
}
}
}
2.In Startup.cs -> ConfigureServices method, use the following code:
public void ConfigureServices(IServiceCollection services)
{
//your other code
//register the class MyTelemetryInitializer.
services.AddSingleton<ITelemetryInitializer, MyTelemetryInitializer>();
}
The test result:
If you're using other programming language, please follow the official doc and use the proper method for ITelemetryInitializer.
You don't need to create your own TelemetryInitializer but can just do this from anywhere you can reference the httpContext:
var requestTelemetry = httpContext.Features.Get<RequestTelemetry>();
if (requestTelemetry != null)
{
requestTelemetry.Properties["YourCustomDimension"] = someValue;
}
Properties added in this way will be added to the requests table in Application Insights.
To add for the dependencies and traces tables you can use
System.Diagnostics.Activity.Current.AddBaggage("YourCustomDimension" someValue);
To add to traces when you write a log entry just pass in objects to the LogXXX method with a placeholder in the log message, e.g.
_logger.LogWarning("hello {YourCustomDimension}", someValue);
someValue will be serialized to json so can be a complex object if you like.

Resources