My DI Failure

by Jacob 31. December 2007 22:10

Work Stress A couple of months ago, I mentioned a project I had coming up that might benefit from using Dependency Injection. The use-case on this is simple enough. We receive all of our EDI text files to a specific directory. Those files need to be processed into standard, internal temporary tables. Since each of our vendors uses the EDIFACT X12 fields differently, we need to customize parsing the files according to vendor.

This is actually one of the larger projects I undertake at our small in-house development shop so it needs more up-front architecture than most and it seemed like a good time to test my understanding of Dependency Injection and maybe develop some familiarity with a DI framework. For one, I know in advance that I’m going to want an interface for my EDI processors and I know for certain sure that I’m going to add file processors in the future.

Those of you familiar with DI might be able to see the ripples around the reefs I eventually cracked up on from here.

The Basic Solution

The solution isn’t that complex in theory.

  • I’ll want a service that monitors a given directory for new files with a given file mask. Both the directory and file mask are in a config file that reloads when altered (all handled by built-in .Net processes).
  • I’ll want a simple interface that the service can share with implementing classes.
  • I’ll want classes to process the files that are found.
EDI Solution

See, simple.

Failure

As much as this looked like a DI shoo-in at the time, it turns out to be a bad fit. I figured this out after digging through the Castle Windsor documentation trying to find a way to iterate through all the registered objects that implement a specific interface. I was frustrated at not being able to find one. It took me a while to realize that I was trying to bend the framework to do something it hadn’t been intended to do—invoke methods on arbitrary registered objects. A DI framework is supposed to handle injecting a class that implements a given interface into the class that depends on that interface.

Indeed, DI frameworks in general are meant to abstract and centralize the complexities of implementation object selection. As such, they are built around the assumption that the run-time already knows what class it wants to use for a given interface (either in a configuration file or determined programmatically at run-time).

It took me a while to figure out that my use of the pattern itself is at fault in this mismatch. I’m not injecting a dependency at all. My service isn’t dependent on classes that implement IEDIProcess, it just determines if there are any available that apply to a newly created file and invoke it correctly if there are. In other words, I’ve already architected around the conceptual dependency here and Dependency Injection is an additional complexity with no purpose.

If DI should be used anywhere in this solution, it’s in the processes themselves to separate out the data updates. And, as I’ve mentioned before, there’s no payoff for me in worrying about my data dependencies.

Busted

So it turns out that shoe-horning a design pattern just to play with it didn’t work out too well. Who’d have thought that could happen?

The broader take-away, at least for me, is that you look for a solution/pattern/tool based on the attributes of your problem. In other words the problem comes first and the solution flows from it. Having a pattern looking for a problem to solve is like having a hammer looking for something to fix—both methods are hit or miss.

Tags: , , , , ,

Programming

An Altova Mapforce Hack

by Jacob 17. October 2007 11:40

Mapforce Anyone who has been forced to deal with EDI X12 documents knows that they are a royal pain. Each document has tons of fields—enough for any reasonable use for any reasonable organization. Having so many defined fields is actually its biggest liability. It means that every organization that uses EDI X12 pretty much has to decide what fields are significant for them. That means, for example, that an 850 document is functionally different for Nordstroms than it is for, say, Wal-Mart.

As a result, there’s a significant market for people who can map EDI documents. Most of them are service vendors pushing both document translation and document delivery with the result (happy for them) of tying you tightly into their service—and revenue stream. Jerks.

Mapforce to the Rescue

Anyway, Altova has a different model because their business is still tied to producing software that makes it easy to manipulate XML documents. They’ve grown to the point where they do a lot more than simple XML and one of those areas of growth is the ability to understand EDI documents (both X12 and EDIFACT formats). This alone was worth it for us to spring for their professional "Mission Kit".

The cool thing about their Mapforce product is that once you’ve defined your mapping, it has an option to "Generate code" with one of the selections being C#.

Mapforce Mapping

This is extremely cool and actually works

A Minor Inconvenience

Now, this is pure magic as far as I’m concerned, so I’m certainly not going to gripe about the code generated by Altova. Much. There are a few things that niggle, though.

  1. The resulting project is created as a "Console" app, so it generates an .exe file.
  2. The worker class’s Run method accepts both source and destination parameters so you’ll hit the file system if you use it "as is". Since want to use this as a library .dll, I’d prefer not to hit the file system for the destination.
  3. If you customize the Run method, you’ll overwrite it if you have to make changes and regenerate the code.

As I said, minor. Still, it means that I have to manually edit code every time I (re)generate a project and every developer knows that if it isn’t automated, it’s a potential source for errors.

Making it Better

I don’t know about you, but little things like that sometimes bug me enough that I’ll try to do something about it. In this case, I found a work-around that is useful enough that I thought I’d share it (thus this post). Two things make this work-around possible.

  1. Mapforce doesn’t clear directories when it generates code.
  2. Mapforce uses templates to generate code.

So a little creative editing and this becomes a lot less error-prone. Here’s what I ended up doing.

  • The templates are located in \{mapforce install dir}\spl\cs\MapForce\
  • Edit the "Mapforce2005.csproj" file so that it’s a Library instead of Exe:
	<OutputType>Library</OutputType>
  • If you want to get fancy, add a compile reference for a partial class you’ll be adding (the highlighted Compile line):
    <ItemGroup>
        <Compile Include="AssemblyInfo.cs"/>
        <Compile Include="[=$application.Name]Console.cs"/>
[foreach $Mapping in $application.Mappings
    foreach $AlgorithmGroup in $Mapping.AlgorithmGroups
]        <Compile Include="[=$application.Name][=$AlgorithmGroup.Name].cs"/>
         <Compile Include="[=$application.Name][=$AlgorithmGroup.Name]Partial.cs"/>
[    next
    foreach $AlgorithmGroup in $Mapping.AllLocalFunctions
]        <Compile Include="[=$AlgorithmGroup.LocalFunctionNamespace]_[=$AlgorithmGroup.Name].cs"/>
[    next
next
]    </ItemGroup>
  • Edit the "AlgorithmGroup.cs" file so that the main generated class is partial:
public partial class [=$classname] 
  • After code is generated, you’ll need to open the solution and add the partial class file it is expecting. Once you’ve done this once, however, you shouldn’t need to redo this step if/when you make changes.

Since I only really want to override the "Run" method, I’ll copy that from the main class, change the return type to XmlDocument and replace the save logic. Since saving is in a try...catch bit at the end, this is easy to replace.

At that point, you can reference the generated .dll file in whatever project is having to read EDI files. Having Mapforce target a project-specific DataSet schema makes it easy to use the returned document, though you have to work the XmlDocument into the DataSet.

public EDI850Data ReadNordstrom850File(string FileName)
{
    NordstromX850MapToEDI850Data nordstromEDIProcessor = new NordstromX850MapToEDI850Data();
    XmlDocument targetDoc = nordstromEDIProcessor.Run(FileName);

    StringReader reader = new StringReader(targetDoc.OwnerDocument.OuterXml);
    EDI850Data ds = new EDI850Data();
    ds.ReadXml(reader);

    return ds;
}
 
Trying This at Home

This is my first pass at it, so bear that in mind if you plan on doing something similar. And I’d welcome any refinement or suggestions you might have. That said, initial indications for this solution are pretty good so by all means use this as a basis for your own experimentation.

Tags: , , , , ,

Programming | Software

scruffylookingcatherder.com

Information

    Recent Posts

    Calendar

    <<  September 2010  >>
    MoTuWeThFrSaSu
    303112345
    6789101112
    13141516171819
    20212223242526
    27282930123
    45678910

    View posts in large calendar
    Disclaimer
    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    © Copyright 2010 Scruffy-looking Cat Herder