Transcript
00:01We're going to talk about building ArcObjects-based modular extensible WPF applications.
00:07Oh, with a bunch of frameworks. So, just for that, and I won't spend much time on this...
00:19...but this really describes the problem, the reason we have to sort of focus on...
00:25...architecture and design and those kinds of things.
00:28Oh, couple of spatial analyses, lot of legacy code.
00:34Many, many developers across multiple companies declined.
00:41What to do?
00:42So, it is a problem, as opposed to greenfield development, where you can focus on one thing.
00:50If you need more details, there are more details, and Alex Bostick is there.
00:56Micro League is in the back and he is the technical manager for the World Project.
01:03He can fill you in on a lot of those details, as well as why we ended up with this kind of an architecture.
01:13Start off, but really, the focus is on the architecture and design.
01:18It's not a software demo. That's not the intention of the presentation.
01:22Really we'll talk about architecture, MEVM, MEF, Prism, and at the end, do some code walkthroughs.
01:30So that's a lot to cover in half an hour.
01:33We'll go as fast as we can. In order for me to just do a reasonable job of how much time I spend on some of the slides...
01:46...if I get a sense from folks, if you have experience in MEVM or Prism, you do, okay. Anybody else?
01:59I do.
02:00Yeah.
02:02Okay, well great, great.
02:04So we will go faster through MEVM and a little bit slower through Prism, how about that?
02:10Then we may be able to go...get to the code walkthroughs a little bit [unintelligible].
02:15Again, stop me wherever you want to, you know, we're here 'til 5:45 and then I guess we have the big party, right?
02:25So, we can stop, we can slow down, we can speed up. I will go fast.
02:31So you just stop me right in the middle when you feel like it.
02:36I won't spend much time on this, but by the end of this...
02:38...you will see that we have kind of gone through most of these things.
02:44P and P, paradigms and practices. So this is sort of the heart of the way we implement things.
02:51Very conservative, very traditional because of those millions of lines of code we're talking about that we have to deal with.
02:57So, all of these things will eventually show up as we go through it.
03:02And we can come back to this and see how good we did. It's kind of our audit, in a sense.
03:08But again, it is about building the line of business applications...
03:11...and this is sort of the pattern to use, at the highest level.
03:20At a very high level, the application consists of a number of modules.
03:24There's an application host. We'll talk about that.
03:27Of course, all kinds of data at the back.
03:29There're some common cross-cutting things. That's the interesting part.
03:32And there's some external system links. Pretty typical.
03:37We will delve into this a bit more.
03:43When we begin to look at it, you know, you saw those modules in the back...
03:48...so this is a bit more detail on the modules.
03:51Each module consists of views, view models and viewer manager...
03:56...which is really splitting out the view module functionality, modular controller concept there.
04:03The business layer, DTOs, that's kind of [unintelligible] of the entities...
04:08...really they are data transfer objects. Then there are services, and there's an overall manager.
04:13That's your typical module. There's a bunch of cross-cutting things. The data is the cross-cutting thing...
04:19...the services that access data, they're sort of split out.
04:23That's different from the DTOs. The DTOs are more transient.
04:28And then there are Prism [unintelligible], globally [unintelligible].
04:31We'll talk about those. Those are critical. And then there's some generic types and this and that.
04:37This is crucial. We'll spend some time on that. This is really where all of this stuff gets hosted on.
04:46And again, it's...it runs on ArcMap. So ArcMap is there, multiple interactions with ArcMap.
04:56And then, of course, the application itself has a shell and has infrastructure stuff there.
05:02So, infrastructure built on top of modules and then a bunch of cross-cutting things here.
05:12Delving into it in a little different fashion before I get into the MEVM.
05:21We have the Mom and apple pie stuff here, right? High-level modules should not depend on low-level modules, abstractions, the interfaces, yeah.
05:33Detail should depend on abstractions, not the vice versa. Sure.
05:40I will not spend a whole bunch of time at that end.
05:44Data access to you...we are still in the process of refactoring it.
05:53Eventually you get to a repository unit of work kind of stuff, at that end.
05:57But really, the only MEVM and Prism is more focused at this tier, and a little bit at this tier.
06:03So, you know, this is sort of the focus of the presentation here.
06:09But this is all pretty basic. You have the UI, and then you have the logic, and you have binding here, obviously...
06:16...the view and the view model. And then you have binding going on, a bunch of services to do. Stuff.
06:25And, just a different view. But really, this is more than MEVM, because this is multiple layers.
06:31This is layers beyond MEVM. This is layers beyond what Prism provides, right?
06:36This is the full bit of it. So, MEVM. Won't spend much time.
06:44There was a really good presentation this afternoon on MEVM, if you guys attended it.
06:50If you didn't, then catch up on the video that was really going into the guts of it.
06:55So I don't...I won't...but, basically, we have the view depends on the view model...
07:05...the view model depends on the model, view contains the controls, it's the XAML file.
07:15Basic conversation with the view model is through commands and data bindings, minimal code behind.
07:22That's the rule. So the XAML RTF file, minimally used. We will see some of that in the code behind, in the walkthrough.
07:32The view model, yeah it exposes data methods and commands for binding here.
07:39Does not know about the view, that's the goal.
07:43Knows about services and other view models down over there.
07:48Now, what we've done is, we've taken out services in other view models from the view model.
07:54That's why we have a UI manager. So, an individual view model will not know about any other view model.
08:00Really, that's our hope.
08:04And really, the UI manager is a central way to handle the individual view models talking to each other.
08:13It will need to know the data transfer objects, which is what this is.
08:21And, clearly the goal is more or less separate from hard [unintelligible] got it from services or whatever.
08:28Yeah, Greg.
08:29[Inaudible audience comment]
08:37With the view model, sure. Yep. Yep. And, that's a good question.
08:42But we would want to, if we are binding for certain things.
08:47If you're binding directly to the model, then we'd want the model to propagate property changes up, and that's one reason.
08:56In addition to...I did error info, which does the same thing. It does propagate up.
09:02And I'll show the structure of the DTO, so both of these are in the DTO.
09:10But the idea is that we will...we don't want to duplicate the model, really, in the view model.
09:20So, you know, some things, sure, if they change slightly.
09:23But we don't necessarily want to duplicate what's in the model into the view model.
09:26That was the goal there. Sir, that's a good question.
09:31So, it really ends up being in both places. For properties that are in here, there will be a notify property changed.
09:45Yeah, so, very quick, and then we're done with MEO, I think.
09:49Views and XAML view hold the reference to the view model.
09:53View binds to the view model. Commands are the key here.
10:00And then the view model was the reference to the model.
10:04So we want to kind of minimize those circular dependencies. That's why we have this flow going in one direction.
10:13Okay, so, commands are crucial. And so I thought I'd just throw commands out there.
10:20Separate then walk around the logic. And really expose these two things.
10:27And, this particular event. So the goal is, you're binding to a command that is exposed by the view model...
10:37...and whether the command can be executed or not, enabled or not, depends on these methods...
10:44...which you have written in the view model.
10:48And we make, we've again, being conservative and traditional in this, so made use of the relay command a whole lot...
10:56...which is what, four, five years old, I think. But it's just a wraparound ICommand with a decent wrapper.
11:08Alright, data validation. That's the other big one. There's others, but I tried to focus on a few of these things.
11:16Data validation was crucial, so that's the example. You have notify and rendition errors equal True...
11:25...data errors equals True, right? So, we don't want to throw exceptions from property centers.
11:33That was the...that's the goal, 'cause it's not recommended. You can talk about why or not.
11:41Anyway, we implement our data error interface and we have an example of that.
11:45We walked through the code on that. It exposes...you have to implement two properties, error and item.
11:54And, really, it does filter up to the view.
12:01So, if you implement this and you implement your data validation right on your model...
12:07...then you will see your errors get propagated up through your view model into your view...
12:16...without putting stuff in your code behind or without putting stuff in your view model.
12:22So you take all of that out, and if you decorate your properties in the DTO with your range validation...
12:33...whatever kind of validation you're doing, then it's really code free.
12:37And that's the big goal with data validation, because data validation can be messy and really mess up your model code.
12:43So we've...just minimized the amount of code we are writing with...
12:46...that interface, and again have an example of that.
12:50So we have a whole bunch of these. I won't go through each one.
12:54But if you want me to go through each one, I can go through each one.
12:58But that's guidelines and I thought, as a part of this kind of presentation, you'll want to see something here.
13:04Now, things like awarding [unintelligible] using commands and [unintelligible].
13:13View model to avoid duplication of model unless warranted.
13:20View first approach. That's...we will talk about that. That comes into play now. What does that mean?
13:29And now we begin to talk a little bit about...we will begin to shift to Prism here a little bit.
13:37So how are views and view models created? Okay.
13:40So you have the view, which depends on the view model, which then depends on the model.
13:47As it happens, you know, creation of things is a crucial piece of all of this.
13:54Because that's where a lot of dependencies end up happening.
13:57When you say, class equals new. Whatever.
14:03Adding that new to any particular thing adds a whole bunch of dependencies...
14:11...which we will later regret in our lots of lines of code.
14:16So how do these things get instantiated so you don't have to instantiate them yourself, right?
14:24One of the approaches is that the view gets created first, and then it has the view model injected.
14:35I will talk about what injection means.
14:38And then from there we'll go into Prism and all that.
14:42The other option is view model creates the view, then we have these two, which I prefer.
14:51So IOC in version of control container can create the view in the View Model together...
14:57...and presenter, or in our case, you know, controller, an initializer class creates view, connects View Model...
15:05...both of those are the options that I...that I tend to prefer and we've used here significantly.
15:13So we will talk about those.
15:14So what is this thing? Where did that come from? And why?
15:21So, a moment of our dependency injection. So this is pretty basic here.
15:33You have a class that you need to use and in your default constructor, here you create it, right?
15:45Okay. Well, if this changes then you have a problem, you know, this and that.
15:50So you can say, alright, I'm going to inject it into the...into my constructor...
15:58...and I'm going to use an interface, right, as opposed to my implementation...
16:07...which improves things significantly already, right?
16:10When I generate the class, I know that thing would exist when I create it.
16:15And I'm using an interface, so the concrete implementation would change. That's cool.
16:22So, so far this is all, you know, [unintelligible] standard oops stuff.
16:29But if you wanted to move beyond that, right? So, we don't want to create either of these classes.
16:33We want to make sure that these dependencies at application start-up are resolved by something.
16:40And then, some of these things need to be singletons, which is a big deal.
16:44Some of these things can be multiple instance things, you know, things like your connections to your database.
16:52You know, the fine-grained service code that you have, which inserts, deletes, updates, right?
17:01You want singletons there. So that's real-life time management is.
17:04So this is a crucial part of it. So all three of these things need to be done, and that can be done with the containers.
17:14So the, you know, just real quick, the problem with hard references, easy but rigid, right?
17:21So here I have a reference to this other class. I'm calling the method, but then I have put an interface up there...
17:28...but I still have a hard reference from that and that, up over there, to that interface.
17:35So there's still a hard reference, although it's better, because my method can change, right?
17:42So we end up with loose coupling by using our DI container, right?
17:46So we have another thing up there, which manages this.
17:50So if this thing is actually instantiating them, then things get a little better.
17:56Because if only this existed, then somewhere in this or that I would have to instantiate...
18:02...or I would have to have another class that instantiates these things...
18:07...so that they're not each dependent on the other, because they're instantiating each other, right?
18:14So, in order for these things to get instantiated, we need something. So that's what the container's doing.
18:23Okay, so how does it do that, alright? So here's MEF, right? So MEF is a extensibility framework...
18:32...it does many things, but the thing we use it for currently...
18:36...is really as our DI, dependency injection, IOC, inversion of control container.
18:48And pretty straightforward. You designate some things for export.
18:55So they're available so the thing knows that these things can now be imported into other classes.
19:03So you decorate them. So really just, you know, an attribute on the class or particular, you know, class.
19:11And then other classes would import them, import these references.
19:17And the dependency injection container then goes through and reads the attributes through reflection...
19:26...of all the classes and knows, builds a list and knows which classes I ought to instantiate first...
19:35...and it creates those classes and then loads them and then they're available in memory.
19:40So that's what you're dependency injector is doing. The example is simpler with MEF, so that's why.
19:50So compose means it creates them, right? So where does it get the list of things that it needs to worry about?
19:59Those can be hard coded, but they can also be in a catalog...
20:04...so they could be in a place, in a location, you know, in a folder, right?
20:13So they could be in many different things. They could be in a configuration file.
20:17So the catalog can be configured to be coming from somewhere...
20:21...and that catalog really holds the list of...essentially all your DLLs, right?
20:25These are your modules that you're going to plug in.
20:28And so you...so with that kind of an architecture, you could dynamically load modules. That's where we want to get to.
20:35So there's the catalog, there's the composition, and the export versus the import, right? So this is the MEF way.
20:44So when we begin to bring MEF and MEVM together, we do this, right?
20:51So we take the container, it will compose the view, and the view will say, I need to import the view model.
21:01It'll compose the view model, which will say, I'm available for export, but I need to import the model.
21:09And then the model will say, I'm available for export.
21:14So this is how we account for MEF in MEVM.
21:22Now, this is not the only container or even my preferable one.
21:32So we have another container, right? This is unity. This is the older one, right? I think, the more mature one.
21:42And it was available with Prism when Prism started being available, which is a few years now, right?
21:48So, MEF is very much a .NET 4 kind of a thing. Maybe 3.5 a little bit, but really 4.
22:00I would like to separate the two and I won't spend any time on examples of unity.
22:07We haven't used unity, but we will eventually go through refactoring of our product to use unity.
22:14The reason we want you to focus on MEF was because MEF is new and eventually we want to do extensibility.
22:20So MEF does a lot more than things like the dependency injection, the kinds of things we just talked about.
22:29MEF does a lot more, but MEF can be kind of a stand-in for dependency injection...
22:34...but unity's really very good at dependency injection and as an IOC.
22:40So, the goal would be to use both, and that's the way we'd want to go, but currently we're just using MEF.
22:51One of the limits of this is that really you can only use attributes, you know...
22:59...that export and import decoration on your classes is the only way you can tell it to do stuff.
23:07With Unity you have many, many other options.
23:11Okay, so when you put all this together, you've got MEVM, you've got MEF...
23:16...and we are doing a lot of things, sort of, to create a kind of a scalable modularized application.
23:30So if we do that, then maybe a good idea is to use an actual framework that helps us do this and has a lot more things to it.
23:39So, Prism adds things like regions. I'll talk about that.
23:43And it adds things like loosely coupled events, which are crucial elements to our work, and we'll talk about that.
23:50So it's got a lot of things, won't go into all these, but it does help you with events and commands...
23:57...multitargeting, Silverlight and WPF. So the latest version of Prism will do all that.
24:04Basic Prism app consists of a shell. This is the shell that we originally saw in our application.
24:12There's a thing called the boot strapper and then you have individual modules.
24:17So this is only one module, and there'll be many modules.
24:20Each module has your view, view model, controller, which we call a manager, Esri services.
24:26So these are modules in the Prism world, and then you have the shell.
24:30So that's the basics. And the shell has the boot strapper, and we'll talk about that.
24:36Building a Prism app. Alright. So we create the shell project; we define the shell window, it's a window.
24:43We add various regions to that window, and those regions are where your views will get plugged in.
24:51So the...and dynamically you can push views into these various regions that are defined in your shell.
24:59The region manager is a powerful tool there.
25:02So your modules have your views, and the views then can get plugged into the regions.
25:08So we'll talk about how that can be done cleanly.
25:11The boot strapper really fires everything up, gets everything going.
25:16And shared events are important. One of the big issues, and I've touched on this a couple of times, we'll touch on this more.
25:24We don't want one module talking to the other module, right?
25:26'Cause we don't even know if the other module will exist when we create this big application.
25:30We'll be adding modules later on.
25:33So dependencies within modules is completely a no-no, right?
25:37So if you need to get information from a module...
25:41...or you need to send information to a module, how would you do it?
25:44So you define some global events where you publish and then you can allow your modules to subscribe to events.
25:53And events can have a generic interface and all that...so shared events.
26:00So we move forward a little bit and we begin to look at the full architecture of Prism, right?
26:06So we have the host, we have the shell, we have multiple regions, and then we have modules here.
26:12Over there we have some core services, and we get things like the region manager...
26:17...which manages the behavior of these things. We have the module catalog.
26:20Remember that that thing can sit out there in a file where you can be loading DLLs into a folder and pulling them up.
26:30So the module loader will load them up from the catalog.
26:34So, logging, [unintelligible]. We talked about the events, and there's others.
26:40Now, the modules will themselves create their own services, and we make heavy use of those.
26:45So, that's Prism. I don't know, how am I doing on time?
26:53About five minutes. Five minutes. Alright.
26:55So Prism, shell, and boot strapper. Now, this is the application root object.
27:03The shell is a window object, top level structure.
27:08In your typical WPF application, we don't...we will not here use the startup URI.
27:15The boot strapper will do all our initialization. It'll get everything running and up. That's what those two things are for.
27:27The region manager. So each region can be in items control or a content control.
27:37So content control will hold one region, and items control could hold a whole list of views.
27:43So each region will end up having a view, so that you define your view, right there.
27:49And this is hard coded, but it could be loaded through a configuration.
27:54And once you define your regions, where your modules are going, where the views are going...
27:59...then the region manager can get them fired up.
28:05The Prism events. When degradator is a singleton class provided by the system, it enables communication between [unintelligible] components.
28:18Publisher subscriber. You will publish and you will publish event arcs, which are strongly typed.
28:24You define those, which is powerful.
28:27Under the hoods is just to delegate. But again, is using the publish and subscribe pattern.
28:33And we make heavy use of that. Specially for things like if you have a geoprocessing service running...
28:38...and it's bubbling up errors, it's just going to publish errors. It's not going to bring your application to a stop.
28:46So where do you capture them? Because you may be adding many new geoprocessing services over time.
28:53So they will publish an event, and the event has a standard signature, and you will decide where you want to capture them.
28:59The shell decides where it wants to capture those events.
29:05So the [unintelligible] looks like this, has the status areas and these will expand...
29:12...task panes, where you can put in parameters and things like that.
29:16Individual workflows get run here. Use your menu at the top.
29:22Typical WPF app. And this is where commanding happens. Hit the Process button and that's a command.
29:33When you...each of these has data validation turned on by using ID error info.
29:44And, I know I moved fast through it, and I'll keep moving faster.
29:48All of this has an impact on your solution structure.
29:53So you need to worry about things like name spaces.
29:56So in here we have the common application area. This is sort of the cross-cutting concerns.
30:01And it's interesting to look at this a little bit, but to try to divide up our dependencies.
30:08So you have common entities, but then you have common entities that are services that are dependent on ArcGIS...
30:15...but then, once they are dependent on ArcGIS ArcMap.
30:19So imagine you're isolating your dependencies here, and if in the future you were to separate things with something...
30:24...like ArcGIS Runtime as opposed to ArcMap, you would be removing only a few things out of this and changing things, hopefully.
30:32Now we need to do a bit more restructuring, reconfiguration, in order to achieve that...
30:38...but we're on that path. We may be 70 percent there.
30:41With that kind of a replacement, which tends to be, you know, pretty massive.
30:46But you look under the...you can see...in a typical project like this, you'll...solution like this...
30:52...you end up with a lot of projects, and you'll see that there are 69 up there.
30:57We'll go down there we begin to see individual modules.
31:00And individual modules, typical module, has this structure.
31:04There's the UI, which has the views and the view models.
31:07Each of them are their own projects. They'll be their own DLL.
31:11And then you have the DTOs, manager services for the business logic tier, and each of these then is a module.
31:24Anyway, I won't spend time on this, but there's a video that's why I thought I would go through all this.
31:33This is an example of where the data binding happens and the rear validation happens...
31:38...so we talked about that a little bit, but if you look at this, you can see that we are referring to the DTO...
31:47...and we're binding to it, and we're saying validate on data errors is equal to true, right?
31:53So if we go down into the code behind, we'll see there's nothing much here except it's referencing the view model...
32:01...that's it's data context. That's all the code behind is doing.
32:06If you look at the model, the model is defining these properties.
32:13It's putting property changed in there, the view model is for...is fairly limited, but it is getting the DTO.
32:29Not spend time on that.
32:32Here we have a data validation happening, and here we have raster projection defining validator...
32:37...which is a custom validator. There's also a mark null validator.
32:41So all we are doing is decorating these, and once we decorate these into our DTO, our model...
32:50...we add...ID error info, which has these two functions, we'll end up bubbling everything up.
32:59So this is all we need to do for validation.
33:05And then we'll have a standard service. Won't spend time on that.
33:08But that service bubbles to a base geoprocessing service, which actually runs things.
33:15I think I made it just...but I can spend time on these things, you know, if we have time.
33:25These are the code walkthroughs that I don't think I went through. That's up to Amy.
33:34And of course, we can do this later on too, but I'm glad you guys are still here.
Building ArcObjects Based Modular and Extensible WPF Applications
Naveed Sami of URS Corporation describes architectural patterns, software engineering practices, and lessons learned from the development of an enterprise application.
- Recorded: Mar 28th, 2012
- Runtime: 33:48
- Views: 1131
- Published: Apr 30th, 2012
- Night Mode (Off)Automatically dim the web site while the video is playing. A few seconds after you start watching the video and stop moving your mouse, your screen will dim. You can auto save this option if you login.
- HTML5 Video (Off) Play videos using HTML5 Video instead of flash. A modern web browser is required to view videos using HTML5.
Right-click on these links to download and save this video.
- 480x270:WebM (70.5 MB)MP4 (50.1 MB)
- 960x540:WebM (190.7 MB)MP4 (153.5 MB)
If you don't have an Esri Global Login ID, please register here.