WCF Streams and Timeouts

I’ve been working on a WCF service for distributing file updates. Recently I did some testing to see how well the system performs through slow connections and discovered some issues I’m glad I run into before the release.

My first discovery was that WCF (using a streaming net.tcp binding) does not perform very well with high ping round-trip-times. In this case my first test setup was quite extreme, as I connected to the service using VPN through a WLAN router connected to the internet by 3G. The ping was something around 750ms.

My second discovery was that the default SendTimeout for a service is 1 minute. This is not nearly enough for a service returning files! I believe the default timeout will cause problems even with fast connections as it only depends on the size of the largest file. If the operation is still returning data after 1 minute, the server closes the stream without warning and returning just some timeout or IO exception.

Fortunately fixing the issue was easy, I just needed to increase the SendTimeout of the service:

binding.SendTimeout = new TimeSpan(1, 0, 0); // 1 hour

If you change the SendTimeout, you should also consider increasing the default 10 minute ReceiveTimeout of the client binding.

There is of course a small security risk in incresing the timeouts, but in my case it’s ok as my service will only be used in a internal network. I wonder if there is a way to define an operation specific SendTimeout?

AutoCAD and .NET

Since AutoCAD 2008 Autodesk ObjectARX® has provided managed wrapper classes that enable you to use .NET to write commands and extensions for AutoCAD. These managed classes enable you to throw away the old COM type libraries and adapt a new way of interacting with AutoCAD in a way much similiar to working with a database. Unfortunately there are quite little examples available to help get started.

The assemblies you need are acdbmgd.dll and acmgd.dll located under the AutoCAD installation directory and also included with the Autodesk ObjectARX SDK. When you reference to these, make sure you change the value of the “Copy local” property of the reference to false as the libraries are already loaded when AutoCAD calls your classes.

To create a new command or extension for AutoCAD you need to setup a class library project and create the necessary classes:

using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[assembly: CommandClass(typeof(AutoCadDemo.MyCommands))]
[assembly: ExtensionApplication(typeof(AutoCadDemo.MyExtensionApplication))]
 
namespace AutoCadDemo
{
    public class MyCommands
    {
        [CommandMethod("MYCOMMAND")]
        public void MyCommand()
        {
            // Command implemenation
        }
    }
 
    public class MyExtensionApplication : IExtensionApplication
    {
        public void Initialize()
        {
            // Extension implemenation
        }
 
        public void Terminate()
        {
            // Extension disposer
        }
    }
}

As you can see, assembly attributes are used to tell AutoCAD what classes it should load. You also need to use attributes to define each command. To load your commands and extensions in AutoCAD you need to use the netload command. There is also a way to use the registry to define what assemblies should be loaded when AutoCAD starts.

The following example shows how to start a transaction, get input from the user and create a new object:

private void DrawCircle()
{
    // Uses editor to prompt user for center point and radius
    Editor editor = Application.DocumentManager.MdiActiveDocument.Editor;
 
    PromptPointResult pointResult = editor.GetPoint("Select center point:");
    if (pointResult.Status != PromptStatus.OK)
        return;
 
    PromptDistanceOptions radiusOptions = new PromptDistanceOptions("Radius:");
    radiusOptions.BasePoint = pointResult.Value;
    radiusOptions.UseBasePoint = true;
 
    PromptDoubleResult radiusResult = editor.GetDistance(radiusOptions);
    if (radiusResult.Status != PromptStatus.OK)
        return;
 
    // Starts transaction to enable editing of drawing
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
    Transaction transaction = database.TransactionManager.StartTransaction();
    {
        try
        {
            // Creates new object representing a circle
            Circle circle = new Circle(pointResult.Value, Vector3d.ZAxis, 
                radiusResult.Value);
            BlockTableRecord activeSpace = (BlockTableRecord)transaction.GetObject(
                database.CurrentSpaceId, OpenMode.ForWrite);
 
            // Adds object to active space
            activeSpace.AppendEntity(circle);
 
            // Adds newly created object to transaction
            transaction.AddNewlyCreatedDBObject(circle, true);
 
            // Commits changes
            transaction.Commit();
        }
        catch (System.Exception)
        {
            // Rollbacks changes on error
            transaction.Abort();
        }
    }
}

Things work a bit differently compared to the old COM interface used in the VBA macros and most Autodesk examples. Each object has a object id that is used to call the object. Database object has properties that provide the ids of most of the main collections e.g. blocks table.

As the drawing file can be seen as a database also access to it can be shared. This means you need to define the minimum access level (open mode) you need to complete your actions. The active transaction enables you to rollback any changes you have made in case something goes wrong.

The wrapper classes also enable you to store data into the drawing in such a way not accessable by the user through the AutoCAD user interface. For this you can either use the database specific Named Object Dictionary (NOD) or the Extenion dictionary of every object.

public void AddItemToNod(string key, string value)
{
    // Starts transaction
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
    Transaction transaction = database.TransactionManager.StartTransaction();
 
    try
    {
        // Retrieves the NOD table and inserts a new record
        DBDictionary nod = (DBDictionary)transaction.GetObject(
            database.NamedObjectsDictionaryId, OpenMode.ForWrite);
 
        ResultBuffer buffer = new ResultBuffer();
        buffer.Add(new TypedValue((int)DxfCode.Text, value));
 
        Xrecord record = new Xrecord();
        record.Data = buffer;
 
        nod.SetAt(key, record);
 
        // Adds newly created record to transaction
        transaction.AddNewlyCreatedDBObject(record, true);
 
        // Commits changes
        transaction.Commit();
    }
    catch (System.Exception)
    {
        transaction.Abort();
    }
}

By using XML serialization you can basically store any object data into the drawing.

Also the classic way of storing data under the file properties (summary info) is possible:

public void AddItemToSummaryInfo(string key, string value)
{
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
 
    // Uses builder to edit the summary info (file properties) of the drawing
    DatabaseSummaryInfoBuilder infoBuilder = 
        new DatabaseSummaryInfoBuilder(database.SummaryInfo);
 
    infoBuilder.CustomPropertyTable.Add(key, value);
 
    database.SummaryInfo = infoBuilder.ToDatabaseSummaryInfo();
}

You should check out Kean Walmsleys blog “Through the Interface” for good tips on developing extensions for AutoCAD using .NET.