📜 ⬆️ ⬇️

StructureMap - quick reference for work (3/3)

Now it's time for:

Interceptors


Since version 2.5+, it has been possible to post-process a newly created object, or completely replace it. In this case, it is not intended to create another AOP framework, since there are already enough of them in the world, it can just make life easier.

For post-processing there are two + methods:


Honestly, in simple examples it’s not very good to convey the difference and the advantages of using OnCreation over EnrichWith and vice versa.

Oncreation


To demonstrate, you can use the following simple class:
public class ClassS : IClassS { public int Counter { get; private set; } public void Init() { Counter = -100; } public void Init(IClass1 class1) { Counter = -50; } public void Increase() { Counter++; } } 

')
Now, you can demonstrate the use of the OnCreation method.
 public class InterceptionsSimple { public IContainer Container; public InterceptionsSimple() { Container = new Container(x => x.For<IClassS>() .Use<ClassS>() .OnCreation(c => c.Init())); } } 

When we get a class from the IoC framework, we can see that the Counter value is -100. As you can see, in the application everything is very simple and again intuitively clear what the code will do. An untrained person can read the class definition in StructureMap and immediately understand what is happening here.

It is also possible that the object constructed by the StructureMap itself should be passed to the initialization method. For these purposes we use the same overloaded method.
 public class InterceptionWithContext { public IContainer Container; public InterceptionWithContext() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassS>().Use<ClassS>() .OnCreation((context, cls) => { var class1 = context.GetInstance<IClass1>(); cls.Init(class1); }); }); } } 

In this case, in the lambda expression there is access to the container itself and you can get the objects that are assigned to StructureMap.

Enrichwith


This method allows you to wrap the constructed class in another, if possible casting. For example, let it be a class
 public class ClassSs : ClassS { private readonly ClassS s; public int Abs { get { return Math.Abs(s.Counter); } } public ClassSs(ClassS s) { this.s = s; } } 

Then you can get it from StructureMap using the following code:
 public class InterceptionWithContext2 { public IContainer Container; public InterceptionWithContext2() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassS>().Use<ClassS>() .EnrichWith(cls => { cls.Init(); return new ClassSs(cls); }); }); } } 

As a result, when we try to get an instance of the class that implements the IClassS interface, we will get the ClassSs class.

Generic types


Without template types, it is probably difficult to imagine a more or less large program, therefore, although this topic will be small in the context of StructureMap, it is important.

So, let's say we have a template adapter.
 public interface IAdapter { string SomeMethod(); } public interface IAdapter<T> : IAdapter {} public class Adapter<T> : IAdapter<T> { public string SomeMethod() { return typeof (T).ToString(); } } 

There are also several of his heirs with specific types.
 public class StringAdapter : Adapter<string> {} public class IntAdapter : Adapter<int> {} 

There are no restrictions on types T in the adapter.

There are several ways to work with template types. The easiest way is to register and call specific types StringAdapter, IntAdapter.
 public class GenericTypes { public IContainer Container; public GenericTypes() { Container = new Container(x => { x.For(typeof (IAdapter<>)).Use(typeof (StringAdapter)); x.For<IAdapter<int>>().Use<IntAdapter>(); }); } } 

The example shows two identical ways to register a template class.

You can call them in several ways.

Way of times: You can ask the container to give us an adapter for a specific type.
 private static string GenericTypesExample() { var container = new GenericTypes().Container; var stringAdapter = container.GetInstance<StringAdapter>(); var intAdapter = container.GetInstance<IntAdapter>(); return stringAdapter.SomeMethod() + " " + intAdapter.SomeMethod(); } 

The second method is more general and, in my opinion, more applicable in practice. Using the second method, you can pass the key type (T), according to which StrutureMap can return the required class.

The drafting of the container remains the same, the production method changes.
 private static string GenericTypesExample() { var container = new GenericTypes().Container; var instance = container .ForGenericType(typeof (IAdapter<>)) .WithParameters(typeof(string)) .GetInstanceAs<IAdapter>(); return  instance.SomeMethod(); } 

Those. we inform the container that we will require a generic type for such and such a type, and return the instance as an IAdapter type.

For real use, it would look like:
 private static string GenericTypesExample<T>() { var container = new GenericTypes().Container; var instance = container .ForGenericType(typeof (IAdapter<>)) .WithParameters(typeof(T)) .GetInstanceAs<IAdapter>(); return instance.SomeMethod(); } 

The specific adapter will be determined by the type of the transmitted parameter T.

Attributes


StructureMap can be configured to a certain extent using attributes. The DefaultCounstructor attribute has already been covered, which points to the constructor that should be used by default. There are attributes for specifying classes and interfaces for registration, which properties of the class are automatically populated, and also set methods for validation.

The author himself recommends not to get involved in attributes, since they are highly specialized, they allow to carry out only the basic configuration and are scattered throughout the project, which makes support difficult. It is better to declare everything in one place.

The most useful attribute is already specified, the following is useful for ValidationMethod , the rest is not recommended, but if you really want, then here is a brief description.

ValidationMethod


Used for self test classes. Those. You have written some class and you can write a method that will determine the correctness of the class creation. StructureMap can hook it up and perform it for self-checking. This seems to be the unique ability of the framework in question.

Suppose we have a class for which we need to set a specific property. This can be a database connection string, interaction settings, complex links. In our example, we restrict ourselves to the fact that the field should not be empty.
 public class SelfValidation { public string Name { get; set; } [ValidationMethod] public void IsClassBuildCorrectly() { if(string.IsNullOrWhiteSpace(Name)) throw new ArgumentException("Name can't be null or empty"); } } 

You can see that the method is marked as ValidationMethod. In general, there may be more than one such method, StructureMap scans them all and tries to execute alternately when the AssertConfigurationIsValid method is called . Be careful with this method, because with a large configuration, the framework will try to build all the dependencies, fill in all fields, run all the methods marked with the attribute in question.

Class registration will be conducted as follows:
 public class ValidationShowcase { public IContainer Container; public ValidationShowcase() { Container = new Container(x => x.ForConcreteType<SelfValidation>()); } } 

Now you can try to get it from the container.
 private static string ValidationShowcaseExample() { var container = new ValidationShowcase().Container; container.AssertConfigurationIsValid(); return ""; } 

When calling the method above, you will receive a StructureMapConfigurationException where internal exceptions caused by the StructureMap internal device checks will be shown.

The screenshot shows that the message about the exceptional situation is our message.

Pluginfamily
Indicates StructureMap that the labeled type will be used as a plugin type. Equivalent to the way we wrote . For < plugin type > .

You can also specify the type that will be returned by default, and you can specifically indicate that it will be a singleton.
 [PluginFamily("Default", IsSingleton = true)] 


Pluggable
The labeled type will be included in the plugin collection, indicating a specific implementation. Equivalent to use . Use < pluggable type > . You must always use a name for the type.
 [Pluggable("Default")] 


SetterProperty
Indicates that the property marked with an attribute needs to be initialized by means of the framework. They will be mandatory and if StructureMap fails to initialize them, there will be an execution error.

Tests


In addition to the fact that StructureMap can check for itself the definition of all classes, parameters and other components, it also allows point-to-point work with checking registered classes. The framework has built-in tools for testing, so that you don’t have to reinvent the bikes, but write your tests nicely and succinctly.

RhinoMock and Moq frameworks are built into StructureMap. In order to use them, you will need to deliver the NuGet structuremap.automocking package, after which you can use mocking of objects.

A review of how Moq and RhinoMock work is outside the scope of this article

Conclusion


I hope that you have a desire to study StructureMap in more detail and in practice. I also strongly advise you to look at the source code of the project, you can learn interesting and useful ideas.

Overboard articles remained on the creation of a container based on the configuration file. Work with nested containers and why they are needed. I hope that in the near future this gap will be filled.

The first part covered the following topics:

In the second part we will talk about:

Source: https://habr.com/ru/post/125785/


All Articles