Closing shape file

Topics: SharpMap v0.9 / v1.x
Sep 22, 2014 at 12:33 PM
Hi,

I’m having trouble closing a shape file after using it. For some reason it doesn’t appear to get closed. Here’s the code that I’m using at the moment:
public async Task<IHttpActionResult> Patch(int id)
{
    // Add the boundary file map data
    if (!Request.Content.IsMimeMultipartContent())
    {
        return BadRequest();
    }

    // Temp file name
    Guid filename = Guid.NewGuid();

    // Set the temp folder
    string root = HttpContext.Current.Server.MapPath("~/App_Data/temp");
    var provider = new CustomMultipartFormDataStreamProvider(root, filename);
    await Request.Content.ReadAsMultipartAsync(provider);

    // If there are no files to process
    if (provider.FileData.Count == 0)
    {
        return BadRequest();
    }

    string path = root + @"\" + filename.ToString() + ".shp";
    ShapeFile shp = new ShapeFile(path);

    ShapeFileDetails result = new ShapeFileDetails();
    result.ID = filename;
    result.Columns = new List<string>();

    try
    {
        // Open the SHP file
        shp.Open();

        // Get the columns
        FeatureDataRow row = shp.GetFeature(0);

        for (int i = 0; i < row.Table.Columns.Count; i++)
        {
            result.Columns.Add(row.Table.Columns[i].ColumnName);
        }
    }
    finally
    {
        // Close the SHP file
        shp.Close();

        // Delete old files
        string[] files = Directory.GetFiles(root);
        foreach (string file in files)
        {
            FileInfo fi = new FileInfo(file);
            if (fi.LastAccessTime < DateTime.Now.AddDays(-1))
            {
                fi.Delete();
            }
        }
    }

    return Ok(result);
}
It’s part of an ASP.NET Web API that I’ve set up that allows you to upload a SHP file and then returns the names of the columns in JSON format.

The problem I’m having is when it comes to the "finally" in the "try" statement. It appears to run the shp.Close() without throwing any exceptions, and then gets to the bit where it deletes old files (older than a day) where it then throws an exception if it tries to delete any files as they are apparently still open despite the previous shp.Close()!

Am I missing something obvious here, or is shp.Close() not working as it should?

Thanks
Sep 22, 2014 at 12:45 PM
Edited Sep 22, 2014 at 1:08 PM
As a test, I removed most of the code and got it down to this:
public async Task<IHttpActionResult> Patch(int id)
{
    string path = HttpContext.Current.Server.MapPath("~/App_Data/temp/test.shp");
    ShapeFile shp = new ShapeFile(path);

    // Open the SHP file
    shp.Open();

    // Close the SHP file
    shp.Close();

    return Ok();
}
So it basically just opens the file "test.shp" in the temp folder, then closes it. I then attempted to delete the "temp.shp" file manually in explorer, and got an error that the file is open in another process. What am I missing here?

** Edit **
Bizarrely I can delete the DBF file, but not the SHP or SHX files. Seems to be the same for all files uploaded.
Coordinator
Sep 22, 2014 at 2:17 PM
Which version of SharpMap are you using?
Sep 22, 2014 at 2:47 PM
Edited Sep 22, 2014 at 2:48 PM
FObermaier wrote:
Which version of SharpMap are you using?
I’m using version 1.1
Sep 23, 2014 at 12:31 PM
I had a look at the source code for the ShapeFile class and it looks like all the code in the Close() method has been commented out. I guess that’s why it doesn’t appear to close the SHP file, and explains why I cannot delete it after reading from it.

For anyone that’s interested, I’ve rewritten my code to use NetTopologySuite instead of SharpMap:
public async Task<IHttpActionResult> Patch(int id)
{
    // Add the boundary file map data
    if (!Request.Content.IsMimeMultipartContent())
    {
        return BadRequest();
    }

    // Temp file name
    Guid filename = Guid.NewGuid();

    // Set the temp folder
    string root = HttpContext.Current.Server.MapPath("~/App_Data/temp");
    var provider = new CustomMultipartFormDataStreamProvider(root, filename);
    await Request.Content.ReadAsMultipartAsync(provider);

    // If there are no files to process
    if (provider.FileData.Count == 0)
    {
        return BadRequest();
    }

    string path = root + @"\" + filename.ToString() + ".shp";

    // Open the shapefile
    GeometryFactory factory = new GeometryFactory();
    ShapefileDataReader shapefileDataReader = new ShapefileDataReader(path, factory);

    ShapeFileDetails result = new ShapeFileDetails();
    result.ID = filename;
    result.Columns = new List<string>();

    try
    {
        DbaseFileHeader header = shapefileDataReader.DbaseHeader;

        for (int i = 0; i < header.NumFields; i++)
        {
            DbaseFieldDescriptor field = header.Fields[i];
            result.Columns.Add(field.Name);
        }
    }
    catch(Exception e)
    {
        return InternalServerError(e);
    }
    finally
    {
        // Close the shapefile
        shapefileDataReader.Close();
        shapefileDataReader.Dispose();

        // Delete old files
        string[] files = Directory.GetFiles(root);

        foreach (string file in files)
        {
            FileInfo fi = new FileInfo(file);
            if (fi.LastAccessTime < DateTime.Now.AddDays(-1))
                fi.Delete();
        }
    }

    return Ok(result);
}
Coordinator
Sep 23, 2014 at 1:34 PM
I'm not sure if the code in Branches/1,0 does match the behavior in SharpMap 1.1.
petlof has refactored the shapefile provider to open a stream to the shapefile whenever needed to fix concurrency issues. Thus the shapefile provider should not hold references to the shapefile anymore. I'm not sure if that fix is in SharpMap 1.1.

If it is, i suspect that the temporary streams, that are created when parsing the header and reading the shx index are not disposed/closed at the time you want to delete the shapefile.
Sep 23, 2014 at 1:37 PM
I see. I suspect that the temporary streams might not be getting closed properly at all as even hours later I am unable to delete the files manually! I can delete them if I restart IIS of course, as the files are released then, but otherwise they don’t seem to get released at any point within my code.