MEF configuration method without features

Source: Internet
Author: User

Managed extensibility framework (MEF) is designed to provide Microsoft. NET Framework developers with an easy way to build loosely coupled applications. MEF version 1 focuses primarily on scalability, so that application developers can publish certain extensions to third-party developers and enable third-party developers to build add-ons or extensions for these components. The Visual Studio plug-in model used to expand Visual Studio itself is a good use case. You can read the "develop Visual Studio extension" (bit. ly/ikjqsz) to learn more. This open extension point and definition plug-in method uses the so-called characteristic programming model, developers can use features to modify attributes, classes, and even methods, to advertise whether a dependency must have a specific type or meet a specific type of dependency.

Although features are useful in scalable solutions with open-type systems, they are redundant for closed-type systems that are known at the time of generation. Some Basic Problems of the feature programming model include:

  1. Many configurations of similar components include unnecessary duplicate content, which violates the "do not repeat" (dry) principle and may cause human errors and make the source files more difficult to read.
  2. Writing extensions or components in. NET Framework 4 means relying on the MEF assembly, which binds developers to a specific dependency injection (DI) framework.
  3. The parts of MEF were not designed to be recognized correctly in applications by adding features. This may pose a huge obstacle to adoption.

. NET Framework 4.5 provides a centralized configuration method that allows you to write a set of rules to illustrate how to create and compile extensions and components. This is implemented using a new class named registrationbuilder (bit. ly/hsclrg). You can find this class in the system. componentmodel. Composition. Registration namespace. In this article, I will first introduce some reasons for using MEF and other systems. If you are an experienced MEF developer, skip this section. Then, I will assume a set of required developer roles and use the MEF feature programming model to create a simple console application. Next, I will convert the application to a convention-based model to illustrate how to use registrationbuilder to implement some typical solutions. Finally, I will discuss how to add the agreed-driven configuration to the application model, and how it turns the use of MEF and the ready-made di guidelines into a very simple thing.

Background

With the expansion of software projects, maintainability, scalability and testability will become a key issue. As software projects become increasingly mature, some components may need to be replaced or improved. As the scope of the project expands, requirements often change or need to be added. Adding a function to a large project in a simple way is very important for the development of this product. Furthermore, since changes are common in most software cycles, it is vital to be able to quickly test components included in software products independently of other components, especially in environments where concurrent development depends on components.

With these driving forces, the di concept is rapidly promoted in large-scale software development projects. The basic principle of DI is to develop components to advertise the dependencies they need (rather than actually instantiate them) and the dependencies they meet, in addition, the dependency injection framework will determine the correct dependency instance and "inject" it into the component. For more background information, the September 1, 2005 msdn Magazine "dependency injection" (msdn.microsoft.com/magazine/cc163739) is an excellent resource.

Application scenarios

Now, let's introduce the Application Scenario mentioned above: I am a developer who needs to follow the specifications provided. At a higher level, the goal of my solution is to provide users with weather forecast information based on the zip code. The steps are as follows:

  1. The application requires the user to provide the zip code.
  2. Enter a valid zip code.
  3. The application contacts the Internet meteorological service provider to obtain the forecast information.
  4. The application displays the prediction information in the specified format to the user.

From the perspective of requirements, there are still some unknown problems, or some aspects may change later in the cycle. For example, I do not know which meteorological service provider to use or how to obtain data from the provider. Therefore, to design the application, I divided the product into several discrete functional units: weatherserviceview, iweatherserviceprovider, and idatasource.Figure 1,Figure 2AndFigure 3The Code of each class is displayed.

Figure 1 weatherserviceview-Result Display class


  
  
  1. [Export]
  2. public class WeatherServiceView
  3. {
  4. private IWeatherServiceProvider _provider;
  5. [ImportingConstructor]
  6. public WeatherServiceView(IWeatherServiceProvider providers)
  7. {
  8. _providers = providers;
  9. }
  10. public void GetWeatherForecast(int zipCode)
  11. {
  12. var result=_provider.GetWeatherForecast(zipCode);
  13. // Some display logic
  14. }
  15. }

Figure 2 iweatherserviceprovider (weatherunderground) Data Analysis Service


  
  
  1. [Export(typeof(IWeatherServiceProvider))]
  2. class WeatherUndergroundServiceProvider:IWeatherServiceProvider
  3. { private IDataSource _source;
  4. [ImportingConstructor]
  5. public WeatherUndergroundServiceProvider(IDataSource source)
  6. {
  7. _source = source;
  8. }
  9. public string GetWeatherForecast(int zipCode)
  10. {
  11. string val = _source.GetData(GetResourcePath(zipCode));
  12. // Some parsing logic here
  13. return result;
  14. }
  15. private string GetResourcePath(int zipCode)
  16. {
  17. // Some logic to get the resource location
  18. }
  19. }

Figure 3 idatasource (weatherfilesource)


  
  
  1. [Export(typeof(IDataSource))]
  2. class WeatherFileSource :IDataSource
  3. {
  4. public string GetData(string resourceLocation)
  5. {
  6. Console.WriteLine("Opened ----> File Weather Source ");
  7. StringBuilder builder = new StringBuilder();
  8. using (var reader = new StreamReader(resourceLocation))
  9. {
  10. string line;
  11. while((line=reader.ReadLine())!=null)
  12. {
  13. builder.Append(line);
  14. }
  15. }
  16. return builder.ToString();
  17. }
  18. }

Finally, to create this component hierarchy, I need to use a catalog, which can be used to find all the components in the application, and then use compositioncontainer to obtain the weatherserviceview instance, which can then be processed, as follows:


  
  
  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. AssemblyCatalog cat =
  6. new AssemblyCatalog(typeof(Program).Assembly);
  7. CompositionContainer container =
  8. new CompositionContainer(cat);
  9. WeatherServiceView forecaster =
  10. container.GetExportedValue<WeatherServiceView>();
  11. // Accept a ZIP code and call the viewer
  12. forecaster.GetWeatherForecast(zipCode);
  13. }
  14. }

All the code I introduced earlier is a very basic MEF syntax. If you are not clear about how any code works, refer to the "managed extensibility framework overview" (bit. ly/jljl8y), which details the MEF characteristic programming model.

Agreed-driven Configuration

Now that I have a feature version for working code, I want to explain how to use registrationbuilder to convert these code segments into a convention-driven model. Let's delete all classes that have added the MEF feature first. For example, let's take a lookFigure 4The code segment in, which is fromFigure 2The weatherunderground data analysis service shown in is modified.

Figure 4 convert to weatherunderground data analysis class of simple C # class


  
  
  1. class WeatherUndergroundServiceProvider:IWeatherServiceProvider
  2. {
  3. private IDataSource _source;
  4. public WeatherUndergroundServiceProvider(IDataSource source)
  5. {
  6. _source = source;
  7. }
  8. public string GetWeatherForecast(int zipCode)
  9. {
  10. string val = _source.GetData(GetResourcePath(zipCode));
  11. // Some parsing logic here
  12. return result;
  13. }
  14. ...
  15. }

Figure 1AndFigure 3InFigure 4Make changes in the same way.

Next, I use registrationbuilder to define some conventions to indicate the content we specified using the feature.Figure 5The code to perform this operation is displayed.

Figure 5 settings


  
  
  1. RegistrationBuilder builder = new RegistrationBuilder();
  2. builder.ForType<WeatherServiceView>()
  3. .Export()
  4. .SelectConstructor(cinfos => cinfos[0]);
  5. builder.ForTypesDerivedFrom<IWeatherServiceProvider>()
  6. .Export<IWeatherServiceProvider>()
  7. .SelectConstructor(cinfo => cinfo[0]);
  8. builder.ForTypesDerivedFrom<IDataSource>()
  9. .Export<IDataSource>();

Each rule Declaration has two different parts. One part specifies a class or a group of classes to be processed; the other part specifies the features, metadata, and sharing policies of the classes to be applied to the selected classes, their attributes, or the constructors of these classes. Therefore, you can see rows 2nd, 5th, and 8th start the three rules I defined. The first part of each rule specifies the type to which the rest of the rule is applied. For example, in row 5th, I want to apply a convention for all types derived from iweatherserviceprovider.

Now, let's take a look at these rules and remap themFigure 1,Figure 2AndFigure 3Original feature code in. Weatherfilesource (Figure 3Is exported directly as idatasource. InFigure 5, Rules in rows 8th and 9th specify all types derived from idatasource and export them as idatasource conventions. InFigure 2, You will see the code export type iweatherserviceprovider, And the idatasource needs to be imported in its constructor (this is modified using the importingconstructor feature ). Rows 5th, 6th, and 7th specifyFigure 5Rules of this type. The code snippet added here is the selectconstructor that accepts the func <constructorinfo [], constructorinfo> method. This provides me with a method to specify constructors. You can specify a convention. For example, the constructor with the minimum or maximum number of parameters is always importingconstructor. In my example, since there is only one constructor, I can use the first and only simple case of a constructor. ForFigure 1Code in,Figure 5The rules in are defined in rows 2nd, 3rd, and 4th, and are similar to the rules just discussed.

When rules are developed, I need to apply them to the types that exist in the application. Therefore, all directories now have an overload that accepts registrationbuilder as the parameter. Therefore, you should modify the previous compositioncontainer code, suchFigure 6.

Figure 6 usage conventions


  
  
  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. // Put the code to build the RegistrationBuilder here
  6. AssemblyCatalog cat =
  7. new AssemblyCatalog(typeof(Program).Assembly,builder);
  8. CompositionContainer container = new CompositionContainer(cat);
  9. WeatherServiceView forecaster =
  10. container.GetExportedValue<WeatherServiceView>();
  11. // Accept a ZIP code and call the viewer
  12. forecaster.GetWeatherForecast(zipCode);
  13. }
  14. }
Set

Now, I have completed the corresponding work, and my simple MEF application has started and run without features. It would be nice if life was so simple! Someone tells me that I need to be able to support multiple meteorological services in the application, and that it needs to display the forecast information provided by all meteorological services. Fortunately, since I used MEF, I am not confused. This is just a solution that contains multiple implementations of interfaces, and I need to access these implementations iteratively. Now, my example contains multiple iweatherserviceprovider implementations and I want to display all the results provided by these meteorological engines. Let's take a look at the changes I need, as shown inFigure 7.

Figure 7 enable multiple iweatherserviceprovider


  
  
  1. public class WeatherServiceView
  2. {
  3. private IEnumerable<IWeatherServiceProvider> _providers;
  4. public WeatherServiceView(IEnumerable<IWeatherServiceProvider> providers)
  5. {
  6. _providers = providers;
  7. }
  8. public void GetWeatherForecast(int zipCode)
  9. {
  10. foreach (var _provider in _providers)
  11. {
  12. Console.WriteLine("Weather Forecast");
  13. Console.WriteLine(_provider.GetWeatherForecast(zipCode));
  14. }
  15. }
  16. }

That's it! I changed the weatherserviceview class to accept one or more iweatherserviceprovider implementations and iteratively access the set in the logic section. The previous conventions now capture and export all iweatherserviceprovider implementations. However, there seems to be something missing in my Conventions: at any time, When configuring weatherserviceview, I do not need to add importmany many features or equivalent conventions. This is the magic of registrationbuilder. It specifies that if your parameter has ienumerable <t>, it must be importmany many without specifying it explicitly. Therefore, using MEF can simplify the operation of the extended application. By using registrationbuilder, as long as the new version implements iweaterserviceprovider, I don't need to perform any operations for them to be used in my application. Great!

"Date"

Another very useful feature in MEF is the ability to add metadata to parts. For the sake of discussion, assume that in the example we discuss, the value returned by the getresourcepath method (for exampleFigure 2As shown in) is subject to the specific types of idatasource and iweatherserviceprovider used. Therefore, I have defined a naming convention to specify a combination of meteorological service providers and data sources separated by underscores. According to this Convention, the Weather Underground service provider with a web data source will have the name weatherunderground_web_resourcestring.Figure 8The code for this Convention is displayed.

Figure 8 Definition of Resource Description


  
  
  1. public class ResourceInformation
  2. {
  3. public string Google_Web_ResourceString
  4. {
  5. get { return "http://www.google.com/ig/api?weather="; }
  6. }
  7. public string Google_File_ResourceString
  8. {
  9. get { return @".
  10. \GoogleWeather.txt"; }
  11. }
  12. public string WeatherUnderground_Web_ResourceString
  13. {
  14. get { return
  15. "http://api.wunderground.com/api/96863c0d67baa805/conditions/q/"; }
  16. }
  17. }

By using this naming convention, I can now create an attribute in weatherunderground and the Google meteorological service provider to import all these resource strings and select the corresponding strings based on the current configuration. Let's take a look at how to write registrationbuilder rules to configure resourceinformation as export (seeFigure 9).

Figure 9 rules for exporting attributes and Adding metadata


  
  
  1. builder.ForType<ResourceInformation>()
  2. .ExportProperties(pinfo =>
  3. pinfo.Name.Contains("ResourceString"),
  4. (pinfo, eb) =>
  5. {
  6. eb.AsContractName("ResourceInfo");
  7. string[] arr = pinfo.Name.Split(new char[] { '_' },
  8. StringSplitOptions.RemoveEmptyEntries);
  9. eb.AddMetadata("ResourceAffiliation", arr[0]);
  10. eb.AddMetadata("ResourceLocation", arr[1]);
  11. });

Only the class is specified for row 1st. Row 2nd defines a predicate to select all the attributes in this class that contain resourcestring. This is exactly what I have stipulated. The last parameter of exportproperties is action <propertyinfo, exportbuilder>, in which I specify the naming convention that I want to export all attributes that match the predicates specified in row 2nd to resourceinfo, you also want to use the keys resourceaffiliation and resourcelocation to add metadata based on the analysis results of the attribute name. At the user end, I need to add an attribute to all iweatherserviceprovider implementations, as shown below:


  
  
  1. public IEnumerable<Lazy<string, IServiceDescription>> WeatherDataSources { get; set; }

Then, add the following interface to use strong metadata:


  
  
  1. public interface IServiceDescription
  2. {
  3. string ResourceAffiliation { get; }
  4. string ResourceLocation { get; }
  5. }

To learn more about metadata and strong metadata, read the help tutorial (bit. ly/haowww ).

Now, let's add a rule in registrationbuilder to import all parts with the agreed name resourceinfo. For this reasonFigure 5Extract existing rules from (rows 5th-7) and add the following clause:


  
  
  1. builder.ForTypesDerivedFrom<IWeatherServiceProvider>()
  2. .Export<IWeatherServiceProvider>()
  3. .SelectConstructor(cinfo => cinfo[0]);
  4. .ImportProperties<string>(pinfo => true,
  5. (pinfo, ib) =>
  6. ib.AsContractName("ResourceInfo"))

Rows 8th and 9th now specify that all types derived from iweatherserviceprovider should apply the import to all properties of the type string, and the import should be performed on the agreed name resourceinfo. When running this rule, the previously added attributes will become the import of all the conventions with the name resourceinfo. Then, I can query the enumeration to filter out the correct resource String Based on the metadata.

Is the era of features over?

If you consider the examples I will discuss, you will see that we do not need to use features anymore. Now, you can use a contract-based model to perform any operations that you perform using the feature programming model. I mentioned some common use cases where registrationbuilder can help. The best article about registrationbuilder (bit. ly/tvqa1j) published by Nicholas Blumhardt can provide you with detailed information. However, features still play an important role in the Convention-driven MEF field. An important issue with related conventions is that these conventions are crucial only when they are followed. Once a rule has exceptions, the overhead of the conventions may be very large, but the features can help overwrite the conventions. Let's assume that a new resource is added to the resourceinformation class, but its name does not conform to the conventions, as shown inFigure 10.

Figure 10 use feature coverage conventions


  
  
  1. public class ResourceInformation
  2. {
  3. public string Google_Web_ResourceString
  4. {
  5. get { return "http://www.google.com/ig/api?weather="; }
  6. }
  7. public string Google_File_ResourceString
  8. {
  9. get { return @".
  10. \GoogleWeather.txt"; }
  11. }
  12. public string WeatherUnderground_Web_ResourceString
  13. {
  14. get { return "http://api.wunderground.com/api/96863c0d67baa805/conditions/q/"; }
  15. }
  16. [Export("ResourceInfo")]
  17. [ExportMetadata("ResourceAffiliation", "WeatherUnderground")]
  18. [ExportMetadata("ResourceLocation", "File")]
  19. public string WunderGround_File_ResourceString
  20. {
  21. get { return @".
  22. \Wunder.txt"; }
  23. }
  24. }

InFigure 10According to the naming conventions, the first part of the conventions is incorrect. However, by entering and explicitly adding the correct agreed name and metadata, You can overwrite or add it to the parts found in registrationbuilder, this makes the MEF feature an effective tool that specifies the agreed exceptions defined by registrationbuilder.

Seamless Development

In this article, I introduced the configuration of the Convention-driven, a new function of MEF, which is published in the registrationbuilder class and can greatly simplify the development work related to MEF. You can find the test versions of these libraries in mef.codeplex.com. If you have not installed. NET Framework 4.5, you can visit the codeplex website and download the content.

It is ironic that registrationbuilder can make your daily development activities no longer so centered on MEF, and you can use MEF in projects highly seamless. A good example is the integration package built into MEF's Model-View-controller (MVC). You can read the Bcl team blog (bit. ly/yswbdl) to learn more. You can download a package to the MVC application, which sets your project to use MEF. Experience shows that no matter what code is "working properly", you can get the benefit of using MEF in an application at the beginning of compliance with the specified conventions, without having to write a line of MEF code in person. You can learn more from the Bcl team blog (bit. ly/ukksfe.

Alok ShriramIs the project manager of the Microsoft. NET Framework Team, responsible for the work of the base class library. Previously, he was a developer of the office live team, and the team was later changed to the Office 365 team. He is currently working in Seattle after graduating from the University of North Carolina graduate school in Chapell Hill. In his spare time, he prefers to visit the famous mountains and rivers in the northwestern region with his wife Mangal. He may be hiding on the MEF codeplex website, hanging on Twitter (twitter.com/alokshriram), and occasionally posting on the. NET blog.

We sincerely thank the following technical experts for reviewing this article:Glenn Block,Nicolas BlumhardtAndImmo landwerth

Address: http://msdn.microsoft.com/zh-cn/magazine/jj133818.aspx

Http://blogs.ejb.cc/archives/3/mef-depth-framework-a-basic-concepts

Http://blogs.ejb.cc/archives/90/mef-depth-framework-contract-import-export

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.