Registering cascading dependencies (Take 2) - unity-container

I attempted to ask this question yesterday and failed completely to make sense. So I've built a reproduction to demonstrate my problem.
In the following program I am resolving Bar witch should have a Foo and a Fool injected into it. Fool also has a Foo injected into it. The catch is I want Bar and Fool to both use the same Foo. In the following code that is not happening.
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
Console.WriteLine("Resolving Bar");
container.Resolve<Bar>();
Console.ReadLine();
}
}
public class Foo
{
public Foo()
{
Console.WriteLine("Foo created " + this.GetHashCode());
}
}
public class Fool
{
public Foo Foo { get; set; }
public Fool(Foo foo)
{
Foo = foo;
Console.WriteLine("Fool created with injected Foo " + foo.GetHashCode());
}
}
public class Bar
{
public Bar(Foo foo, Fool fool)
{
Console.WriteLine("Bar created with injected Foo " + foo.GetHashCode());
Console.WriteLine("Bar created with injected Fool.Foo " + fool.Foo.GetHashCode());
// I want foo == fool.Foo
}
}
public class Blat
{
public Blat(Foo foo, Fool fool)
{
Console.WriteLine("Blat created with injected Foo " + foo.GetHashCode());
Console.WriteLine("Blat created with injected Fool.Foo " + fool.Foo.GetHashCode());
}
}
I could Resolve the types and define the a specific injection constructor in the Main method, but I have a complication. In this case I also create a Blat as well as a Bar. The catch is I want the Blat and the Bar to use different Foo's.
Consider:
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
var foo = container.Resolve<Foo>();
container.RegisterType<Fool>(new InjectionConstructor(foo));
var fool = container.Resolve<Fool>();
container.RegisterType<Bar>(new InjectionConstructor(foo, fool));
container.RegisterType<Blat>(new InjectionConstructor(foo, fool));
Console.WriteLine("Resolving Bar");
container.Resolve<Bar>();
container.Resolve<Blat>();
Console.ReadLine();
}
}
In this case Bar.Foo == Bar.Fool.Foo but Blat.Foo == Bar.Foo. In other words I want
Bar.Foo == Bar.Fool.Foo && Blat.Foo != Bar.Foo
Is there a way to do this? I will be resolving many types of Bar's and Blat's so I would like to avoid creating child containers for each Bar, Blat, etc...
EDITTED TO FURTHER CONFUSE THE ISSUE
The problem is in the real world I have something like the following:
BaseViewModel(Context context, ServiceA a, ServiceB b)
Each control will need a ViewModel created that uses a different context. In each viewModel the services have to use the same context. So I want Unity to create context, a, and b all using the same context.
I could do something similar to what you suggest. I could set a Context property on the services in the constructor of BaseViewModel, but that seems odd to me. Right now I'm just newing up the services in the constructor, but I'd rather do it with Unity so they aren't coupled so tightly. I was hoping there was a more elegant way.

With your IUnityContainer, register Foo with a PerResolveLifetimeManager, i.e.:
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<Foo>(new PerResolveLifetimeManager());
Console.WriteLine("Resolving Bar");
container.Resolve<Bar>();
container.Resolve<Blat>();
Console.ReadLine();
}
When you resolve Bar, the same instance of Foo will be injected into Bar and injected into Fool.
Then when you resolve Blat, a new instance of Foo will be injected into Blat and into Fool.
So Bar.Foo == Bar.Fool.Foo and Blat.Foo == Blat.Fool.Foo but Bar.Foo != Blat.Foo and Bar.Fool.Foo != Blat.Fool.Foo.
Every time you call container.Resolve, only one instance of Foo is ever resolved for that graph, that is what PerResolveLifetimeManager does.

Have you ever considered something like this:
public class Bar
{
public Bar(Fool fool)
{
this.Fool = fool;
}
public Foo Foo
{
get { return this.Fool.Foo; }
set { this.Fool.Foo = value; }
}
public Fool Fool { get; set; }
}
where Bar.Foo is just a convenience setter/getter for Bar.Fool.Foo? Why inject the same instance twice?

Related

Mono.Cecil AddInterfaceImplementation equivalent?

I am working on Mono.Cecil codegen util, and I want to preform following operation:
Loop through types
If type contains X attribute:
- Add ITestInterface implementation (where ITestInterface has defined some methods)
// For reference
public interface ITestInterface
{
void Something();
int IntSomething();
}
// Expected result, if type contains X attribute:
// Before codegen:
[X]
public class CodeGenExample
{
}
// After codegen
[X]
public class CodeGenExample : ITestInterface
{
public void Something()
{
// some stuff
}
public int IntSomething()
{
// do some stuff
return 0;
}
}
I have seen that .NET Reflection has a AddInterfaceImplementation method (https://learn.microsoft.com/pl-pl/dotnet/api/system.reflection.emit.typebuilder.addinterfaceimplementation?view=net-5.0).
Is there a Mono.Cecil equivalent or a workaround for this & how to use it?
That can be achieved by:
Iterating over all types defined in the assembly
Checking which types have the attribute applied to
Injecting the methods.
As an example you can do something like:
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace inject
{
interface IMyInterface
{
int Something();
}
class MarkerAttribute : Attribute {}
[Marker]
class Foo
{
}
class Program
{
static void Main(string[] args)
{
if (args.Length == 1)
{
using var a = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
var interfaceToImplement = a.MainModule.GetType("inject.IMyInterface");
foreach(var t in a.MainModule.Types)
{
if (t.HasCustomAttributes && t.CustomAttributes.Any(c => c.Constructor.DeclaringType.Name == "MarkerAttribute"))
{
System.Console.WriteLine($"Adding methods to : {t}");
var something = new MethodDefinition("Something", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, a.MainModule.TypeSystem.Int32);
something.Body = new Mono.Cecil.Cil.MethodBody(something);
var il = something.Body.GetILProcessor();
il.Emit(OpCodes.Ldc_I4, 42);
il.Emit(OpCodes.Ret);
t.Methods.Add(something);
// Add the interface.
t.Interfaces.Add(new InterfaceImplementation(interfaceToImplement));
var path = typeof(Program).Assembly.Location + ".new";
a.Write(path);
System.Console.WriteLine($"Modified version written to {path}");
}
}
}
else
{
object f = new Foo();
IMyInterface itf = (IMyInterface) f;
System.Console.WriteLine($"Something() == {itf.Something()}");
}
}
}
}
Another potential solution is to have the methods implemented in an internal class and copying over their method bodies.
As a side note, these are 2 online tools you can use to explore/learn more about CIL, Mono.Cecil, C#:
Sharplab.io
Cecilifier (disclaimer: I'm the author of this one)
That being said if you can use C# 9.0 you may be able to leverage the new Source Generators feature.

How to trust all packages when deserialising in Spring Cloud Stream?

This consumer didn't need trusted packages:
#Bean
fun berichtStateStoreBuilder() = Consumer<GlobalKTable<String, BerichtEvent>> {}
This suddenly does:
#Bean
fun berichtStateStoreBuilder() = Consumer<KStream<ByteArray, ByteArray>> {
it
.transform({ EventTypeAwareTransformer(EVENT_TYPE_MAPPING, objectMapper) })
.mapValues { v -> v.payload as BerichtEvent }
.groupByKey(Grouped.with(Serdes.StringSerde(), JsonSerde()))
.aggregate(
{ BerichtAggregator() },
{ _, event, aggregator -> aggregator.add(event) },
Named.`as`("aggregate"),
Materialized.`as`<String, BerichtAggregator, KeyValueStore<Bytes, ByteArray>>(BerichtStore.NAME)
.withKeySerde(Serdes.String())
.withValueSerde(JsonSerde(BerichtAggregator::class.java))
)
I've tried the following approaches, but they didn't work as I only get the following error:
Caused by: java.lang.IllegalArgumentException: The class 'at.wrwks.smp.controlling.event.BerichtEvent' is not in the trusted packages: [java.util, java.lang]. If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*).
at org.springframework.kafka.support.converter.DefaultJackson2JavaTypeMapper.getClassIdType(DefaultJackson2JavaTypeMapper.java:126)
at org.springframework.kafka.support.converter.DefaultJackson2JavaTypeMapper.toJavaType(DefaultJackson2JavaTypeMapper.java:100)
at org.springframework.kafka.support.serializer.JsonDeserializer.deserialize(JsonDeserializer.java:504)
at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:55)
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
... 8 more
#Bean
fun defaultKafkaHeaderMapper(objectMapper: ObjectMapper): DefaultKafkaHeaderMapper {
val mapper = DefaultKafkaHeaderMapper(objectMapper, "event_type")
val rawMappedHeaders = HashMap<String, Boolean>()
rawMappedHeaders[BaseEvent.EVENT_TYPE_HEADER] = true
mapper.setRawMappedHeaders(rawMappedHeaders)
mapper.addTrustedPackages("*")
return mapper
}
spring.cloud.stream.kafka.streams.binder.header-mapper-bean-name: defaultKafkaHeaderMapper
spring.cloud.stream.kafka.streams.binder.configuration.spring.json.use.type.headers: false
spring.cloud.stream.kafka.streams.binder.configuration.spring.json.trusted.packages: '*'
Spring Cloud Stream version: 3.1.2 with Kafka Streams binder.
Workaround by using a custom JSON serde:
.groupByKey(Grouped.with(Serdes.StringSerde(), Serdes.serdeFrom(
SimpleJsonSerializer(objectMapper), SimpleJsonDeserializer(objectMapper, BerichtEvent::class.java)
)))
I just tested it and it works fine for me...
#SpringBootApplication
public class So67059860Application {
public static void main(String[] args) {
SpringApplication.run(So67059860Application.class, args);
}
#Bean
public Consumer<KStream<String, Foo>> input() {
return str -> str.foreach((k, v) -> System.out.println(v));
}
}
public class Foo {
private String bar;
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde=org.springframework.kafka.support.serializer.JsonSerde
spring.cloud.stream.kafka.streams.binder.configuration.spring.json.trusted.packages=*
spring.cloud.stream.kafka.streams.binder.configuration.spring.json.value.default.type=com.example.demo.Foo
spring.application.name=so67059860
spring.cloud.function.definition=input
#logging.level.root=debug
Foo [bar=baz]
Boot 2.4.4, Cloud 2020.0.2 (SCSt 3,1.2).
Set a breakpoint in JsonSerde.configure() to see the properties being used.
Although I've done this dozens of times this time I forgot to pass the target class to the constructor JsonSerde(). This is correct:
.groupByKey(Grouped.with(Serdes.StringSerde(), JsonSerde(BerichtEvent::class.java)))
Apparently when no class will be passed, then no package can be added to the trusted packages. With a class passed the Serde will be configured with the package the target pass belongs to.

Route PropertyChanged Event from static ObservableCollection to non-static wrapper class

I am trying to bind an ObservableCollection, which is filled in a Background thread, to a charting Control in the UI.
Therefore i have a static class "Core", which have a member "DataState". This DataState class owns the ObservableCollection "SensorData", which is filled in the aforementioned Background Task.
As there is no possibility to do UWP XAML Binding for static properties i wrote a wrapper class "DataWrapper" which is nonstatic and refers to the static Core.DataState.SensorData-object.
Here's some of my Code:
Core.cs:
public static partial class Core
{
private static DataState m_DataState;
public static DataState DataState
{
get
{
return m_DataState;
}
set
{
if (value != null)
{
m_DataState = value;
}
}
}
}
DataState.cs:
public class DataState
{
private ObservableCollection<SensorData> m_SensorData = new ObservableCollection<SensorData>();
public ObservableCollection<SensorData> SensorData
{
get
{
return m_SensorData;
}
set
{
if (value != null)
m_SensorData = value;
}
}
DataWrapper.cs:
public class DataWrapper
{
public ObservableCollection<SensorData> SensorData
{
get
{
return Core.DataState.SensorData;
}
}
}
XAML:
<Charting:LineSeries Name="MySeries" Title="Title" IndependentValuePath="X" DependentValuePath="Y" ItemsSource="{x:Bind DataWrapper.SensorData}"></Charting:LineSeries>
where X and Y are the variables contained in the SensorData-Object.
So, if i wait to Show the Charting-Control until there's some data in the ObservableCollection this data is nicely plotted into my Control. But after that recently added data is not plotted anymore.
Therefore i am looking for a way to route the PropertyChanged-Event from Core.DataState.SensorData somehow to DataWrapper.SensorData. Is there any possibility to do this?
Is the structure of this Problem clear to you? I think my descriptions sounds a bit confusing...
Thank you in advance for any help :-)

NativeActivity in XAML loaded COMPILED workflow throws Expression Activity type 'CSharpValue1' requires compilation in order to run

This is a know error when using C# expressions in windows workflow. The article at https://learn.microsoft.com/en-us/dotnet/framework/windows-workflow-foundation/csharp-expressions#CodeWorkflows explains the reason and how to fix it. It all works fine for me in standard workflows, but as soon as I add a custom NativeActivity to the WF, I get that same error again !
Below the code of how I load the XAML workflow and the simple NativeActivity (which is the ONLY activity in the test workflow and inside that activity is a simple assign expression).
Loading and invoking WF via XAML:
`XamlXmlReaderSettings settings = new XamlXmlReaderSettings()
{
LocalAssembly = GetContextAssembly()
};
XamlReader reader = reader = ActivityXamlServices.CreateReader(new XamlXmlReader(fileURL, settings));
ActivityXamlServicesSettings serviceSettings = new ActivityXamlServicesSettings
{
CompileExpressions = true
};
var activity = ActivityXamlServices.Load(reader, serviceSettings);
WorkflowInvoker.Invoke(activity);`
Doing it in code throws same Exception:
Variable<string> foo = new Variable<string>
{
Name = "Foo"
};
Activity activity = new Sequence
{
Variables = { foo },
Activities =
{
new TimeExecuteUntilAborted
{
Activities =
{
new Assign<string>
{
To = new CSharpReference<string>("Foo"),
Value = new CSharpValue<string>("new Random().Next(1, 101).ToString()")
}
}
}
}
};
CompileExpressions(activity);//the method from the article mentioned above
WorkflowInvoker.Invoke(activity);
The Native Activity:
[Designer("System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation")]
public sealed class TimeExecuteUntilAborted : NativeActivity
{
private Sequence innerSequence = new Sequence();
[Browsable(false)]
public Collection<Activity> Activities
{
get
{
return innerSequence.Activities;
}
}
[Browsable(false)]
public Collection<Variable> Variables
{
get
{
return innerSequence.Variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddImplementationChild(innerSequence);
}
protected override void Execute(NativeActivityContext context)
{
context.ScheduleActivity(innerSequence);
}
}
Your TimeExecutedUntilAborted class seems to be the culprit. I was able to swap in one of my own template NativeActivities instead and your workflow executed fine with the expressions. I'm guessing that your class is causing an issue in the compiler method when it parses your code. I used this doc as an example for my NativeActivity: https://msdn.microsoft.com/en-us/library/system.activities.nativeactivity(v=vs.110).aspx.
Sizzle Finger's answer is no solution but pointed me into the right direction to simply check what is different. It came out that the simple call to the base class method was missing:
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
base.CacheMetadata(metadata); // !! This needs to be added
metadata.AddImplementationChild(innerSequence);
}

structuremap enrichwith not overriden

I am trying to find a way to override a structuremap registry statement containing EnrichWith like so (here is the Registry class):
public class MyRegistry : Registry
{
public MyRegistry()
{
For(typeof(IMyList<int>)).EnrichWith(x => DecorateMyList(x)).Use(typeof(MyListA<int>));
For(typeof(IMyList<int>)).Use(typeof(MyListB<int>));
For<IMyList<string>>().Use<MyListA<string>>();
For<IMyList<string>>().Use<MyListB<string>>();
}
private object DecorateMyList(object o)
{
var genericParameters = o.GetType().GetGenericArguments();
var myListDecoratorType = typeof(MyListDecorator<>).MakeGenericType(genericParameters);
var decorated = Activator.CreateInstance(myListDecoratorType, new []{o});
return decorated;
}
}
public class MyRegistryUser
{
ObjectFactory.GetInstance<IMyList<string>>(); // Good: Returns an instance of MyListB<string> as expected
ObjectFactory.GetInstance<IMyList<int>>(); // Bad: Returns an instance of the decorator containing MyListB<int> - my second rule should have overridden the EnrichWith as well.
}
Am I right to think that there is a glitch in structure map or is there something I'm not seeing?
Thanks in advance

Resources