Structuremap Quick Start Guide)

Source: Internet
Author: User
Tags mailmessage

I have read some articles about sm in China, but I am still a foreigner. This is the first article I translated. Please do not give me any advice on improper mistakes. I always think that a third-party framework must have a loud name for success. When I first came into contact with jquery four years ago, I thought it would become popular, because the name is obviously more impressive than other frameworks (prototype/Moo tool/EXT JS. This is also true for structuremap, short for SM. Before IOC, we were often overwhelmed by the complicated business logic SM. With structuremap, the bottom-layer coders saw the hope of turning over. We could use the SM business logic.

 

On my 30th birthday, I gave a speech about IOC container-structuremap (SM) at the Stockholm episerver (1) organization (2. The focus of this speech and this article is not on what IOC is and what it is trying to solve (it has been written countless times. If you are interested, you can read Wikipedia, Martin
Fowler, Joel Abrahamsson, and stackoverflow ). What I want to pay attention to here is what structuremap can do for you, and how do you do it.

Tldr (3): This article is quite long, so if you are more like the person who compiles and runs it, you can scroll through the page to download the sample solution.

 

A simple service class:

Suppose you have recently started to look at the solid (4) principle, especially Dependency inversion, which may generate the following code structure:

In this case, our service creates a constructor dependency for the verification and storage abstraction. The specific implementation of storage depends on some configurations and logger. The specific implementation of the logger depends on other abstractions. When we talk about specific implementation, for example, the storage depends on configuration and logging, which means the code is like this:

public class Repository : IRepository{    public Repository (IConfiguration configuration, ILogger logger)    {    }}

 

Create an instance of our service as follows:

var service = new Service(new Validator(),    new Repository(new Configuration(),           new Logger(new AmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency())));

 

This is manual dependency injection, which means that, as it sounds, we are manually injecting dependencies into our services. If you are interested in learning more about the advantages and disadvantages of using IOC containers, rather than how to use them, I would like to make a good summary in this article.

 

IOC container

Many IOC containers have emerged. The first thing I think of in my mind is structuremap,
Unity, ninject, and Castle Windsor. To be honest, I have not thoroughly compared different containers. I started with structuremap, and I have no reason to change it. In my understanding, the features of all these frameworks are similar, but the syntax is different.

 

Structuremap and syntax

Speaking of structuremap syntax, it will be nice to know two things: the official document (link on top) covers a wide range, but it is based on the old version. This means that if you use a newer version (I think more than 2.5, I am using 2.6.1 in this article), you will find that many methods cannot be found or are outdated. Another thing is that this framework uses a large number of lambda expressions, so if you are not familiar with those expressions, it may seem a little weird at first.

 

Basic configuration and automatic assembly

Aside from the syntax, the basic thing we need to do is to give sturcturemap some abstractions and some instructions so that it can decide what specific implementations we want to use in reality. Therefore, we still use the above service as an example. When we talk about iconfiguration abstraction, we actually want to use the specific configuration class.

After adding a reference to sturcturemap. dll, you can configure it as follows:

ObjectFactory.Configure(x => x.For<IConfiguration>().Use<Configuration>());

 

To create a specific implementation, send a request to the container:

var config = ObjectFactory.GetInstance<IConfiguration>();

 

If we fail to implement the configuration of how to create a type from the abstract, Sm (The author finally abbreviated it) will throw a very friendly exception:

 

For debugging purposes, you can call the whatdoihave (as the name suggests) method as follows to learn more about what happened inside the container:

var what = ObjectFactory.WhatDoIHave();

 

It is easy to think that to obtain our service (After configuring the required dependencies), we will write the following code:

var service = new Service(    ObjectFactory.GetInstance<IValidator>(),    ObjectFactory.GetInstance<IRepository>());

 

This method is not recommended for using (IOC) containers. The code we made here is somewhat like the code I first showed, and we have bound our service to our container. This is not good. We have begun to use Dependency inversion to get rid of the specific implementation dependencies of other parts in our code. We want to use the concept of automatic assembly to allow Sm to configure all Dependencies by itself.

Suppose that when we ask SM for our service (class instance), it will find the most greedy Constructor (which has the most parameters. In this constructor, an ivalidator and irespository (parameter) are required ). It will view the configuration. We can see that we have told it to use the specific validator and repository classes. when it tries to create a repository, it will see that another iconfiguration and ilogger are required, and it will view the related configuration again, so that until all dependencies are parsed. Therefore, to get a service instance, we only need to do this:

var service = ObjectFactory.GetInstance<Service>();

 

 

Automatic Assembly and Web Framework

Unfortunately, for our episerver developers, webforms is not well supported in IOC. using MVC makes it much easier to use IOC.

The reason is that we can control the creation of the Controller (5 ). since the logic is in the controller, this lays a great foundation to call your getinstance method and adjust automatic assembly without referencing sm in your code. Even if you cannot control the page creation of the webform program, you can overcome this problem by using setter injection (refer to my article), but it is not so elegant.

 

Assembly scanning and conventions

If you see our service configuration:

ObjectFactory.Configure(x =>{    x.For<IValidator>()        .Use<Validator>();    x.For<IAmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>()        .Use<AmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>();    x.For<IRepository>()        .Use<Repository>();    x.For<IConfiguration>()        .Use<Configuration>();    x.For<ILogger>()        .Use<Logger>();});

 

We can see an emerging model. We have a form of abstraction called isomething, and a specific implementation called something. This (Convention) is very popular. It forms a default SM convention around it. In this way, to make it easier to skip a lot of boring typing, we can let SM scan the Assembly and add all the abstractions/implementations that follow this convention.

ObjectFactory.Configure(x =>{    x.Scan(a =>    {        a.AssembliesFromApplicationBaseDirectory();        a.WithDefaultConventions();    });});

 

We can certainly control the assembly to be scanned. In the above example, we scan all the assemblies under the program root directory (which should be the bin directory in the ASP. NET program. We can also target an assembly, or call an assembly, or filter the Assembly starting with a string ...... Yes, it is omnipotent.

If you have some naming encoding rules, you can also create your own conventions.

 

 

Register DSL and guide

The sample we have seen so far has used Sm to register DSL (6). We recommend this method for configuration. It is usually implemented through a bootstrap and an inherited registration class.

It is about at your program entry point (for example, in ASP. net Program application_start), call your Bootstrap to view all the registrations in sequence and add anything configured in them.

Creating a registration is as easy as inheriting from the Registry class. In the preceding example, you can access the Registry API in the class constructor. This (method) facilitates the associated dependency group in a registration and assigning the corresponding name to the registration.

Suppose we want to create a registration for processing log records:

public class LogRegistry : Registry{    public LogRegistry()    {        For<ILogger>()            .Use<Logger>();        For<IAmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>().Use            <AmAnotherNeccessaryButAtThisPointAnnoyingToTypeDependency>();    }}

 

Our Bootstrap is like this:

public class StructureMapBootrstrapper{    public static void Bootstrap(IContainer container)    {        container.Configure(x =>        {            x.Scan(a =>                        {                            a.AssembliesFromApplicationBaseDirectory();                            a.LookForRegistries();                        });        });    }}

Note that we have passed an icontainer instance (to the bootstrap method ). We can use objectfactory directly to demonstrate a simple static facade of the main container, but we want to minimize the dependencies on specific classes.

At the entry point of the program, we only need to call our pilot and pass it to the container we use.

StructureMapBootrstrapper.Bootstrap(ObjectFactory.Container);

 

In this example, since those registrations (the same as we have seen before) can be registered by default, we may not need a logregistry. Generally, you will try your best to use the default conventions, and manually register (using the registration class) for non-conforming situations ).

 

Implement Conversion

For me, the best thing about IOC containers is that it is easy to change your program behavior without directly modifying the code. This is just a configuration issue. Let's take a look at the example below: Our program uses the following settings to send emails through SMTP.

public interface ISendMailMessage{    void SendMessage(MailMessage message);}

 

 

public class SendMailMessageViaSmtp : ISendMailMessage{    public void SendMessage(MailMessage message)    {        using (var client = new SmtpClient("mail.somecompany.com"))        {            client.Send(message);        }    }}

 

public class MailRegistry : Registry{    public MailRegistry()    {        For<IMailService>().Use<MailService>();        For<ISendMailMessage>().Use<SendMailMessageViaSmtp>();    }}

 

When developing on the local machine, we may not have access to the server, or do not want to send emails for some other reason. Of course we can use a variety of techniques (Hacks), such as commenting/uncommenting code in the sendmailmessageviasmtp class, but this rarely produces a solid solution.

Usage Section

There are several better ways to use profile in SM ). In this way, you can create different configurations for different sections. For our development machine, we have created another implementation (7), which records emails to a text file.

public class LogMailMessage : ISendMailMessage{    public void SendMessage(MailMessage message)    {        using(var writer = new StreamWriter("MailLog.txt", true))        {            writer.WriteLine(                "Sending mailmessage from {0} to {1} with subject {2}",                message.From.Address,                message.To,                message.Subject                );        }    }}

 

In our mailregistry, we create a debug section to guide Sm to use the above class under this section:

For<ISendMailMessage>().Use<SendMailMessageViaSmtp>();Profile("DEBUG", profile =>{    profile.For<ISendMailMessage>().Use<LogMailMessage>();});

 

Then we can use a known logic to switch the section when appropriate:

#if DEBUGObjectFactory.Profile = "DEBUG";#endif

 

 

Conditional Logic

Another way to implement the switchover is to use the conditions in the configuration. Therefore, we should not mention whether to build a program in debug mode. If we want to implement mail Switching Based on the machine name and use conditional statements, we can do this in the following Configuration:

ObjectFactory.Configure(x =>{    x.For<ISendMailMessage>()        .ConditionallyUse(c =>        {            c.If(context => Environment.MachineName.Equals("CNNOTE41"))                .ThenIt                .Is                .ConstructedBy(by => new SendMailMessageViaSmtp());            c.TheDefault                .Is                .ConstructedBy(by => new LogMailMessage());        });});

 

Native constructor Parameters

In some constructors, you may have dependencies (string, Int, etc.) on some native types ). One typical example is that a storage class will contain a connection string as the constructor parameter. If we ignore this for the moment, it may not be a good idea in terms of architecture, what should we do if we use SM?

In fact, it is still very simple, just like answering the sm QUESTION and saying what parameters we want to use. To provide this storage class:

public class Repository : IRepository{    private string connectionString;    public Repository(string connectionString)    {        this.connectionString = connectionString;    }    public string GetConnectionString()    {        return connectionString;    }}

 

We can inject the selection of connection strings through the ctor method.

ObjectFactory.Configure(x =>{    x.For<IRepository>()        .Use<Repository>()        .Ctor<string>()        .Is("Stefans connectionstring");});

 

Because our class only has one constructor that contains the string type parameter, Sm can calculate the matched one. If there are two parameters, we have to specify which value belongs to which parameter. To do this, you only need to specify a name in the ctor method. The code below will generate the same constructor parameters as above, but explicitly specifies which constructor parameter to set.

ObjectFactory.Configure(x =>{    x.For<IRepository>()        .Use<Repository>()        .Ctor<string>("connectionString")        .Is("Stefans connectionstring");});

 

 

List and add all types

Imagine that we are building a plug-in part of our framework, where third-party assembly wants us to Process Code related to a specific interface. This example is somewhat unnatural. It is a helloservice that retrieves the list of isayhello (implementations) and simply loops to execute each of the hello methods.

public class SayHelloService{    private List<ISayHello> helloList;    public SayHelloService(List<ISayHello> helloList)    {        this.helloList = helloList;    }    public void SayHello()    {        helloList.ForEach(x => Console.Out.WriteLine(x.Hello()));    }}

 

public interface ISayHello{    string Hello();}

 

We can guide the SM scanning assembly by adding all types of interfaces It finds through some logic.

public class SayHelloRegistry : Registry{    public SayHelloRegistry()    {        Scan(a =>        {            a.AssembliesFromApplicationBaseDirectory();            a.AddAllTypesOf<ISayHello>();        });    }}

 

Therefore, if we have two implementations of isayhello (8 ):

Public class sayhelloinenglish: isayhello {Public String Hello () {return "hello" ;}} public class sayhelloinchinese: isayhello {Public String Hello () {return "";}}

 

SM is smart enough to work out. Since we have dependencies on the isayhello list (9), we can pass all the implementations that can be found in the configuration. Therefore, as long as a third party wants to use your plug-in system to implement a negotiated interface, you can scan their assembly, which will be automatically sent to you.

 

That's more?

SM certainly has more content than I write, but these parts are the most commonly used in my own projects.

 

Sample solution

I made a vs2010 solution, including examples of various SM features listed above. You can download it here.

 

By Stefan Forsberg

Original article: http://world.episerver.com/Blogs/Stefan-Forsberg/Dates/2010/8/An-introduction-to-StructureMap/

Note:

1) episerver: A company that provides robust, flexible, and highly customizable solutions, support and manage the company's online presentation in four key areas-content management, community and social media, business and Communication (official website ).

2) This is translated into an organizational unit. The original term is Meetup, which refers to an online social network portal that helps offline group meetings around the world (reference ).

3) the abbreviation of too long and didn't read (too long to read ).

4) for the abbreviation of the Five Principles of object-oriented design, see http://en.wikipedia.org/wiki/solid.

5)
Reference link of the original article. There are also several articles in the garden: extensions you must know in Qilin's Asp.net MVC2 (1): controller factory, du Zong when ASP. NET
MVC falls in love with IOC, and Lao Zhao supports area controllerfactory.

6) DSL is the abbreviation of domain specific language.

7) It also inherits the isendmailmessage. It seems that the message in the interface name is a little cumbersome, just call it isendmail.

8) the second implementation of the original article is sayhelloinswedish (Swedish ).

9) Here, it is more appropriate to use a set instead. Because the ienumerable <t> parameter is more universal than the list <t> parameter.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.