Let's consider an example to understand the need for composite applications. Contoso Financial Investments provides an application for managing your stock investment portfolio. If you built this as a traditional WPF application with user controls, you would start with a top-level window and add user controls for each of the different aforementioned functions.
You would then use RoutedEvents, RoutedCommands, and data binding to wire everything up. The TrendLine and NewsReader are wired up in order to listen to the TickerSymbolSelected event and render their contents based on the selected ticker symbol.
In this case, the application is tightly coupled to each of the controls. There is a significant amount of logic in the UI for coordinating the different pieces.
There are also interdependencies among the controls. Due to these dependencies, there's no easy way to break up the application into a form where you can develop each of the different pieces separately. You could put all the user controls in a separate assembly to improve maintainability, but that only moves the problem from the main app to the control assembly. It's very difficult in this model to make significant changes or introduce new functionality.
Now let's complicate matters by adding two new business requirements. The first is to add a fund notes screen that displays personal notes about the selected fund when it is double-clicked.
The second is to add a new screen that displays a list of hyperlinks related to the selected fund. Due to time constraints, these features must be developed in parallel by different teams. Each team develops separate controls: FundNotes and FundLinks. To add both controls to the same control assembly, they must each be added to the control project. More important, they must be added to the main form, which means that changes to both code and XAML from each control must be merged with the main form.
This type of operation can be incredibly brittle, especially for existing applications. How do you check all of these changes back into the main application? By the time you're done, you will have potentially spent a significant amount of time doing merges and diffs in source control. If you make any mistakes applying the changes or you accidentally overwrite something, you'll be left with a broken application. The remedy lies in rethinking the application design.
A composite app consists of loosely coupled modules that are dynamically discovered and composed at run time. Modules contain visual and non-visual components representing the different vertical slices of the system see Figure 2. The visual components views are composed in a common shell that acts as the host for all of the app's content. Composites provide services that tie these module-level components together. Modules can offer additional services that relate to the specific functionality of the app.
At a high level, a composite application is an implementation of the Composite View design pattern, which describes a recursive UI structure of views containing children that are themselves views. The views are then composed by a mechanism—usually at run time, in contrast to being statically composed at design time. To illustrate the benefits of the pattern, think of an order entry system in which you have multiple instances of an order.
Each instance may involve significant complexity to display the header, details, shipping, and receipts. As the system evolves, it may need to display more information. Imagine also that parts of the order display differently depending on the type of order. If such a screen is built statically, then you may end up with a lot of conditional logic for displaying the different parts of the order. Also, adding new functionality increases the likelihood that the existing logic will break.
If we implement this as a composite view, however, then we compose the order screen dynamically of only the relevant pieces. This means that we can do away with conditional display logic and add new child screens without modifying the order view itself. Modules contribute the views from which the main composite view otherwise known as the shell is created. Modules never directly reference each other, nor do they directly reference the shell.
Instead, they utilize services to communicate with one another and with the shell in order to respond to user actions. Having a system composed of modules provides several benefits. Modules can aggregate data coming from different back-end systems within the same application.
Additionally, the system can more easily evolve over time. As the system requirements change, new modules can be added to the system with much less friction than in a non-modular system. Existing modules can then be evolved more independently, thus improving testability.
Finally, modules can be developed, tested, and maintained by different teams. The new guidance was designed to leverage the capabilities and programming model of WPF. At the same time, the team also improved upon the design of previous composite application guidance based on feedback from internal product teams, customers, and the. NET community. The CAL provides the services and plumbing for building composite apps.
It uses a composition model that allows each of its services to be used piecemeal or together as part of a CAL-designed app. Each service is also easily replaceable without recompilation of the CAL. For example, the CAL ships with an extension that uses the Unity Application Block for dependency injection but lets you replace this with your own dependency injection service. Quickstarts provide small, focused apps that demonstrate using each of the different CAL components. They are designed to help you ramp up on concepts without having to grasp everything at once.
In the remainder of the article, I will explore several technical concepts of composites that are illustrated in the Stock Trader reference implementation. When you build composite applications using the CAL, you must first initialize several core composition services. This is where the bootstrapper comes in. It performs all the necessary functions for composition to occur illustrated in Figure 3. In many ways it is the Main method of a CAL application.
First, the container is initialized. Containers play a key role in a CAL app. The container is the store of all the app services used in composition. It is responsible for injecting theses services wherever they are needed.
None of the classes in the CAL other than the Unity extensions depend on a specific container. As the container is being configured, several core services used for composition are automatically registered, including a logger and an event aggregator, and the base bootstrapper lets you override any of these.
For example, one service that is automatically registered is IModuleLoader. If you override the ConfigureContainer method in the bootstrapper, you can register your own module loader. If you don't want services to be registered by default, you can turn that off also. Simply call the Run method overload on the bootstrapper, passing a false value for the useDefaultConfiguration parameter.
Learn more. Is Microsoft. Composite earlier version of Microsoft. Ask Question. Asked 8 years, 2 months ago. Active 8 years, 2 months ago. Viewed 4k times. Demarsch Demarsch 1, 18 18 silver badges 36 36 bronze badges. Add a comment. Active Oldest Votes. The Composite and Composite. Presentation portions of the namespaces were removed and the Composite and Composite. Presentation assemblies collapsed into a single assembly named Microsoft.
Rate this:. Like this: Like Loading Leave a Reply Cancel reply Enter your comment here Fill in your details below or click an icon to log in:. Email required Address never made public. Name required. Follow Following. Sign me up. Already have a WordPress. Log in now. If the object is released externally, the container creates a new instance, otherwise it returns the live object contained in the weak reference.
I'll use the Unity container in the following examples, but there are also a number of open source alternatives to the Unity IoC container, such as Ninject, Spring. If you are familiar with and already using an IoC container other than Unity, you can supply your own container although it takes a little more effort. Ordinarily in a Silverlight application, the startup behavior is simply to create the main XAML page's class and assign it to the application's RootVisual property. In a composite application, this work is still required, but instead of creating the XAML page class, a composite application typically uses a bootstrapping class to handle startup behavior.
To start, you need a new class that derives from the UnityBootstrapper class. This class is in the Microsoft. UnityExtensions assembly. The bootstrapper contains overridable methods that handle different parts of startup behavior. Often, you will not override every startup method, only the ones necessary.
This is typically called the shell because it is the visual container for the application's components. My example includes a bootstrapper that creates a new instance of the Shell class and assigns it to RootVisual before returning this new Shell class, as shown here:.
The GetModuleCatalog method, which I'll explain in the next section, returns the list of modules to load. Now that you have a bootstrapper class, you can use it in your Silverlight application's startup method.
Usually, you create a new instance of the bootstrapper class and call its Run method, as shown in Figure 4. The bootstrapper is also involved in registering types with the container that different parts of the application require. To accomplish this, you override the ConfigureContainer method of the bootstrapper.
This gives you a chance to register any types that are going to be used by the rest of the application. Figure 5 shows the code. Here, the code registers an interface for a class that implements the IShellProvider interface, which is created in our example and is not part of the CAL framework. That way we can use it in our implementation of the CreateShell method. We can resolve the interface and then use it to create an instance of the shell so we can assign it to RootVisual and return it.
This methodology may seem like extra work, but as you delve into how the CAL helps you build your application, it becomes clear how this bootstrapper is helping you. In a typical.
NET environment, the assembly is the main unit of work. This designation allows developers to work on their code separately from each other. In the CAL, each of these units of work is a module, and for the CAL to use a module, it needs a class that can communicate the module's startup behavior. This class also needs to supports the IModule interface. The IModule interface requires a single method called Initialize that allows the module to set itself up to be used in the rest of the application.
The example includes a ServerLogger module that contains the logging capabilities for our application. The problem is that we don't know what we want to initialize in our module. Since it's a ServerLogging module, it seems logical that we want to register a type that does logging for us. We want to use the container to register the type so that whoever needs the logging facility can simply use our implementation without knowing the exact type of logging it performs.
We get the container by creating a constructor that takes the IUnityContainer interface. If you remember the discussion of dependency injection, the container uses constructor injection to add types that it knows about. IUnityContainer represents the container in our application, so if we add that constructor, we can then save it and use it in our initialization like so:.
Once initialized, this module is responsible for the logging implementation for the application. But how does this module get loaded? When using the CAL to compose an application, you need to create a ModuleCatalog that contains all the modules for the application. You create this catalog by overriding the bootstrapper's GetModuleCatalog call. With code, you create a new instance of the ModuleCatalog class and populate it with the modules.
For example, look at this:. In addition, you can specify dependencies between modules. Because some modules might depend on others, using dependencies helps the catalog know the order in which to bring in the dependencies. Using the ModuleInfo.
DependsOn property, you can specify which named modules are required to load another module. The XAML file contains the same type information you can create with code. The benefit of using XAML is that you can change it on the fly. Imagine retrieving the XAML file from a server or from another location based on which user logged on. An example of a catalog.
In this XAML catalog, the group includes two modules and the second module depends on the first. You could use a specific XAML catalog based on roles or permissions, as you could with code.
0コメント