我正在使用OGR / GDAL C#管理的API写入GeoPackage数据库,并且在尝试将数据库同步到磁盘时,我一直遇到“数据库已锁定”错误。经过大量的实验,这似乎只有在我首先打开GeoPackage进行读取,关闭db(处置数据源),重新打开db并尝试写入的情况下才会发生。 Web搜索表明,如果尝试进行写入之前未处理读取的GDAL和SQLite对象,则会发生此错误。但是,我想我正在正确处理所有托管对象,所以我想知道是否丢失了一个呼叫。任何帮助将不胜感激。我提供了一个示例控制台应用程序,该应用程序显示了我得到的错误。它创建了一个新层,但是如果我尝试更新现有层,则会收到相同的错误。我正在Windows上使用GeoPackage v1.2,GDAL v2.3和SQLite v3.21.0。
示例控制台应用程序:
using System;
using System.Collections.Generic;
using OSGeo.OGR;
namespace GeoPackageWriteTest
{
class GeoPackageTest
{
public static void usage()
{
Console.WriteLine( "usage (to create a new layer):" );
Console.WriteLine( "gpkgWriteTest {gpkg filename} {layername}" );
System.Environment.Exit( 0 );
}
static void Main( string[] args )
{
if ( args.Length < 2 )
usage();
Ogr.RegisterAll();
// simulate an object opening a geopackage db to do a simple read
Console.WriteLine( "Opening database readonly: " + args[0] );
DataSource dataSource1 = Ogr.OpenShared( args[0], 0 ); // open readonly
if ( dataSource1 == null )
throw new ArgumentException( string.Format( "Error opening GeoPackage database: {0}", args[0] ) );
using ( dataSource1 )
{
Driver driver = dataSource1.GetDriver();
if ( driver == null )
throw new ArgumentException( "Error getting GDAL driver!" );
Console.WriteLine( "Using driver: " + driver.name );
string pfile = GetParameterTableName( dataSource1 );
Console.WriteLine( "Parameter table: " + pfile );
} // DataSource gets disposed and geopackage is closed
dataSource1 = null;
// make sure all Disposed objects are garbage collected
// (just trying to see if it makes a difference, it doesn't)
GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced, true, true );
GC.WaitForPendingFinalizers();
// simulate another object reopening a geopackage db to do a simple write
Console.WriteLine( "Opening database readwrite: " + args[0] );
DataSource dataSource2 = Ogr.OpenShared( args[0], 1 ); // reopen readwrite
if ( dataSource2 == null )
throw new ArgumentException( string.Format( "Error opening GeoPackage database: {0}", args[0] ) );
using ( dataSource2 )
{
List<string> options = new List<string>();
options.Add( "SPATIAL_INDEX=YES" );
Console.WriteLine( "Creating layer: " + args[1] );
Layer tableLayer = dataSource2.CreateLayer( args[1], null, wkbGeometryType.wkbNone, options.ToArray() );
if ( tableLayer == null )
throw new ArgumentException( string.Format( "Error creating layer: {0}", args[2] ) );
int rc = Ogr.OGRERR_NONE;
int approx_ok = 1;
int fieldCount = 3;
for ( int i = 0; i < fieldCount; i++ )
{
//FieldDefn fieldDefn = GetFieldDefn( args[n], args[n + 1] );
string fname = string.Format( "TestField{0}", i );
FieldDefn fieldDefn = GetFieldDefn( fname, "int32" );
rc = tableLayer.CreateField( fieldDefn, approx_ok );
if ( rc != Ogr.OGRERR_NONE )
throw new ArgumentException( string.Format( "Error creating field: {0}", fname ) );
}
// write changes to disk
Console.WriteLine( "Writing changes to disk..." );
dataSource2.FlushCache(); // this throws database locked error
}
}
static string GetParameterTableName( DataSource ogrDataSource )
{
try
{
string s = string.Empty;
string sql = "select * from sqlite_master where type='table' and name like '%Parameter%'";
OSGeo.OGR.Layer layer = ogrDataSource.ExecuteSQL( sql, null, "SQLITE" );
using ( layer )
{
layer.ResetReading();
OSGeo.OGR.Feature fea = layer.GetNextFeature();
if ( fea != null )
{
s = fea.GetFieldAsString( "name" );
}
}
return s;
}
catch ( Exception )
{
return string.Empty;
}
}
private static FieldDefn GetFieldDefn( string fldName, string fldType )
{
//OSGeo.OGR.FieldType fieldType = GetOGRFieldType( fldType );
OSGeo.OGR.FieldType fieldType = FieldType.OFTInteger;
OSGeo.OGR.FieldSubType subType = FieldSubType.OFSTNone;
FieldDefn fieldDefn = new FieldDefn( fldName, fieldType );
fieldDefn.SetSubType( subType );
return fieldDefn;
}
}
}
答案 0 :(得分:0)
以防万一有人好奇。这里的问题是我第一次读取结果集层的方式。我只读了1个功能(因为这就是我感兴趣的全部)。显然,结果集处于繁忙状态。我需要阅读整个图层,即
OSGeo.OGR.Feature fea = layer.GetNextFeature();
while ( fea != null )
{
s = fea.GetFieldAsString( "name" );
layer.GetNextFeature();
}
或者我需要在读取后重置该层,即
OSGeo.OGR.Feature fea = layer.GetNextFeature();
if ( fea != null )
{
s = fea.GetFieldAsString( "name" );
}
layer.ResetReading();