I have written a set of classes to interact with AutoCAD from an out-of-process .NET assembly, but it seems like my class architecture and interactions are unusual. I am struggling to find a better way to design these.
I am reading data from several thousand AutoCAD drawings, collecting data from bills of materials, title blocks, and other entities. AutoCAD can be interfaced with from another process in several ways. One way is to use the AutoCAD COM type library which marshals every call and is relatively slow (about 6 seconds per drawing). A much faster way is to load a .NET dll into the AutoCAD process and execute the code there. This .NET component exposes a COM interface (lets call it IAutoCADDataReader) which you can call from the outside world. If you call a method on this interface, you can harvest the data and return an object that contains all your gathered data (call it IDomainObject). This works well and takes about 200 ms (huge improvement) since the interaction with the AutoCAD database was all in-process and you only marshaled one complicated object at the very end.
The above part of the design I am happy with. What bothers me is what I do with the IDomainObject when I get it back into my main .NET assembly. It is of course a _COMObject that I can cast to a IDomainObject, but it does not really exist in my managed assembly, it is a stub or proxy. When I read its data, it calls across the process boundary. If AutoCAD gets shut down, I will get a RPC error. I would really like this object to exist independently of its original source. The natural solution is to clone it and discard the COM object.
Now, usually a Clone method is on the object itself. This fits with SRP since who would know more about cloning than it would? That is not a solution in this case since a Clone method on this IDomainObject interface would actually execute in the AutoCAD process and just give me another COM object. So ignoring SRP, I wrote a private Clone function in the managed assembly that iterates over the various arrays and other data in the IDomainObject and returns a fully managed DomainObject (not an interface). This new object can now exist independently of AutoCAD, can do whatever I want in the managed world and will not be subject to RPC errors.
I like the end result of this convoluted process since I ended up with what I wanted in a fairly fast manner, but I cannot help but think that this is a odd way to architect such a thing. Do any of you have any suggestions on how this design can be refactored to make it less peculiar?
Another criticism that could be leveled at this type of cloning is that it fails the Open/Closed principle. If I extend any of the subtypes within the IDomainObject, the clone operation will no longer be correct. I would have to update this private clone function at that time. This really just shows how this solution works for now, but it violates several principles that will probably bite me as this project continues to evolve. As a developer of in-company software, this stuff will be used and evolved for years and it would not be good to have these bad practices at the beginning.