<pre class="brush:c#;toolbar:false"> public partial class SQLiteDataStore : SQLStoreBase<SqlEntityInfo>, IDisposable
{
private string m_connectionString;
private string Password { get; set; }
public string FileName { get; protected set; }
protected SQLiteDataStore()
: base()
{
}
public SQLiteDataStore(string fileName)
: this(fileName, null)
{
}
public SQLiteDataStore(string fileName, string password)
: this()
{
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException();
}
FileName = fileName;
Password = password;
}
public override string Name
{
get { return FileName; }
}
protected override string DefaultDateGenerator
{
get { return "CURRENT_TIMESTAMP"; }
}
public override string ConnectionString
{
get
{
if (m_connectionString == null)
{
m_connectionString = string.Format("Data Source={0};", FileName);
if (!string.IsNullOrEmpty(Password))
{
m_connectionString += string.Format("Password={0};", Password);
}
}
return m_connectionString;
}
}
protected override IDbCommand GetNewCommandObject()
{
return new SQLiteCommand();
}
protected override IDbConnection GetNewConnectionObject()
{
return new SQLiteConnection(ConnectionString);
}
protected override IDataParameter CreateParameterObject(string parameterName, object parameterValue)
{
return new SQLiteParameter(parameterName, parameterValue);
}
protected override string AutoIncrementFieldIdentifier
{
get { return "AUTOINCREMENT"; }
}
public override void CreateStore()
{
if (StoreExists)
{
throw new StoreAlreadyExistsException();
}
#if(!WINDOWS_PHONE)
SQLiteConnection.CreateFile(FileName);
#endif
var connection = GetConnection(true);
try
{
foreach (var entity in this.Entities)
{
CreateTable(connection, entity);
}
}
finally
{
DoneWithConnection(connection, true);
}
}
public override void DeleteStore()
{
if (StoreExists)
{
File.Delete(FileName);
}
}
public override bool StoreExists
{
get { return File.Exists(FileName); }
}
protected override string ParameterPrefix
{
get { return "@"; }
}
private SQLiteCommand GetInsertCommand(string entityName)
{
// TODO: support command caching to improve bulk insert speeds
// simply use a dictionary keyed by entityname
var keyScheme = Entities[entityName].EntityAttribute.KeyScheme;
var insertCommand = new SQLiteCommand();
var sbFields = new StringBuilder(string.Format("INSERT INTO {0} (", entityName));
var sbParams = new StringBuilder( " VALUES (");
foreach (var field in Entities[entityName].Fields)
{
// skip auto-increments
if ((field.IsPrimaryKey) && (keyScheme == KeyScheme.Identity))
{
continue;
}
sbFields.Append(field.FieldName + ",");
sbParams.Append(ParameterPrefix + field.FieldName + ",");
insertCommand.Parameters.Add(new SQLiteParameter(ParameterPrefix + field.FieldName, field.DataType));
}
// replace trailing commas
sbFields[sbFields.Length - 1] = ')';
sbParams[sbParams.Length - 1] = ')';
insertCommand.CommandText = sbFields.ToString() + sbParams.ToString();
return insertCommand;
}
/// <summary>
/// Inserts the provided entity instance into the underlying data store.
/// </summary>
/// <param name="item"></param>
/// <remarks>
/// If the entity has an identity field, calling Insert will populate that field with the identity vale vefore returning
/// </remarks>
public override void OnInsert(object item, bool insertReferences)
{
if (item is DynamicEntity)
{
OnInsertDynamicEntity(item as DynamicEntity, insertReferences);
return;
}
var itemType = item.GetType();
string entityName = m_entities.GetNameForType(itemType);
var keyScheme = Entities[entityName].EntityAttribute.KeyScheme;
if (entityName == null)
{
throw new EntityNotFoundException(item.GetType());
}
// ---------- Handle N:1 References -------------
if (insertReferences)
{
DoInsertReferences(item, entityName, keyScheme, true);
}
var connection = GetConnection(false);
try
{
FieldAttribute identity = null;
var command = GetInsertCommand(entityName);
command.Connection = connection as SQLiteConnection;
command.Transaction = CurrentTransaction as SQLiteTransaction;
// TODO: fill the parameters
foreach (var field in Entities[entityName].Fields)
{
if ((field.IsPrimaryKey) && (keyScheme == KeyScheme.Identity))
{
identity = field;
continue;
}
else if (field.DataType == DbType.Object)
{
// get serializer
var serializer = GetSerializer(itemType);
if (serializer == null)
{
throw new MissingMethodException(
string.Format("The field '{0}' requires a custom serializer/deserializer method pair in the '{1}' Entity",
field.FieldName, entityName));
}
var value = serializer.Invoke(item, new object[] { field.FieldName });
if (value == null)
{
command.Parameters[ParameterPrefix + field.FieldName].Value = DBNull.Value;
}
else
{
command.Parameters[ParameterPrefix + field.FieldName].Value = value;
}
}
else if (field.DataType == DbType.DateTime)
{
var dtValue = GetInstanceValue(field, item);
if (dtValue.Equals(DateTime.MinValue) &&
((field.AllowsNulls) || (field.DefaultType == DefaultType.CurrentDateTime)))
{
// testing of just letting the null fall through is setting the field to null, not using the default
// so we'll set it manually
dtValue = DateTime.Now;
}
command.Parameters[ParameterPrefix + field.FieldName].Value = dtValue;
}
else if (field.IsRowVersion)
{
// read-only, so do nothing
}
else if (field.PropertyInfo.PropertyType.UnderlyingTypeIs<TimeSpan>())
{
// SQLite doesn't support "timespan" - and date/time must be stored as text, real or 64-bit integer (see the SQLite docs for more details)
// here we'll store TimeSpans (since they can be negative) as an offset from a base date
var value = field.PropertyInfo.GetValue(item, null);
if (value == null)
{
command.Parameters[ParameterPrefix + field.FieldName].Value = DBNull.Value;
}
else
{
var storeTime = new DateTime(1980, 1, 1) + (TimeSpan)value;
command.Parameters[ParameterPrefix + field.FieldName].Value = storeTime;
}
}
else
{
var value = field.PropertyInfo.GetValue(item, null);
if ((value == null) && (field.DefaultValue != null))
{
command.Parameters[ParameterPrefix + field.FieldName].Value = field.DefaultValue;
}
else
{
command.Parameters[ParameterPrefix + field.FieldName].Value = value;
}
}
}
command.ExecuteNonQuery();
// did we have an identity field? If so, we need to update that value in the item
if (identity != null)
{
var id = GetIdentity(connection);
identity.PropertyInfo.SetValue(item, id, null);
}
if (insertReferences)
{
// ---------- Handle 1:N References -------------
DoInsertReferences(item, entityName, keyScheme, false);
}
}
finally
{
DoneWithConnection(connection, false);
}
}
public override void CompactDatabase()
{
var connection = GetConnection(true);
try
{
string sql = "VACUUM";
using (var command = GetNewCommandObject())
{
command.CommandText = sql;
command.Connection = connection;
command.Transaction = CurrentTransaction;
command.ExecuteNonQuery();
}
}
finally
{
DoneWithConnection(connection, true);
}
}
private int GetIdentity(IDbConnection connection)
{
using (var command = new SQLiteCommand("SELECT last_insert_rowid()", connection as SQLiteConnection))
{
object id = command.ExecuteScalar();
return Convert.ToInt32(id);
}
}
protected override void GetPrimaryKeyInfo(string entityName, out string indexName, out string columnName)
{
var connection = GetConnection(true);
try
{
indexName = string.Empty;
columnName = string.Empty;
string sql = string.Format("PRAGMA table_info({0})", entityName);
using (var command = GetNewCommandObject())
{
command.CommandText = sql;
command.Connection = connection;
command.Transaction = CurrentTransaction;
using (var reader = command.ExecuteReader() as SQLiteDataReader)
{
if (reader.HasRows)
{
while (reader.Read())
{
// pk column is #5
if (Convert.ToInt32(reader[5]) != 0)
{
columnName = reader[1] as string;
}
}
}
}
}
}
finally
{
DoneWithConnection(connection, true);
}
}
private void UpdateIndexCacheForType(string entityName)
{
if (!TableExists(entityName)) return;
if (Entities.Contains(entityName))
{
// have we already cached this?
if (((SqlEntityInfo)Entities[entityName]).IndexNames != null) return;
}
else
{
return;
}
// get all iindex names for the type
var connection = GetConnection(true);
try
{
string sql = string.Format("SELECT name FROM sqlite_master WHERE (tbl_name = '{0}')", entityName);
using (var command = GetNewCommandObject())
{
command.Connection = connection;
command.CommandText = sql;
command.Transaction = CurrentTransaction;
using (var reader = command.ExecuteReader())
{
List<string> nameList = new List<string>();
while (reader.Read())
{
nameList.Add(reader.GetString(0));
}
((SqlEntityInfo)Entities[entityName]).IndexNames = nameList;
}
}
}
finally
{
DoneWithConnection(connection, true);
}
}
public override string[] GetTableNames()
{
var names = new List<string>();
var connection = GetConnection(true);
try
{
using (var command = GetNewCommandObject())
{
if(this.CurrentTransaction != null)
{
command.Transaction = this.CurrentTransaction;
}
command.Connection = connection;
var sql = "SELECT name FROM sqlite_master WHERE type = 'table'";
command.CommandText = sql;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var n = reader.GetString(0);
if (n == "sqlite_sequence") continue;
names.Add(n);
}
}
return names.ToArray();
}
}
finally
{
DoneWithConnection(connection, true);
}
}
public override bool TableExists(string tableName)
{
var connection = GetConnection(true);
try
{
using (var command = GetNewCommandObject())
{
command.Connection = connection;
var sql = string.Format("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = '{0}'", tableName);
command.CommandText = sql;
command.Transaction = CurrentTransaction;
var count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
finally
{
DoneWithConnection(connection, true);
}
}
protected override void ValidateTable(IDbConnection connection, IEntityInfo entity)
{
// first make sure the table exists
if (!TableExists(entity.EntityAttribute.NameInStore))
{
CreateTable(connection, entity);
return;
}
var fieldData = new List< object[]>();
using (var command = new SQLiteCommand())
{
command.Connection = connection as SQLiteConnection;
command.CommandText = string.Format("PRAGMA table_info({0})", entity.EntityName);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var values = new object[6];
reader.GetValues(values);
fieldData.Add(values);
}
}
}
foreach(var field in entity.Fields)
{
// 0 = cid (column id)
// 1 = name
// 2 = type
// 3 = notnull
// 4 = dflt_value
// 5 = pk
var existing = fieldData.FirstOrDefault(f => string.Compare(f[1].ToString(), field.FieldName, true) == 0);
if (existing == null)
{
// field doesn't exist - we must create it
var alter = new StringBuilder(string.Format("ALTER TABLE {0} ", entity.EntityAttribute.NameInStore));
alter.Append(string.Format("ADD [{0}] {1} {2}",
field.FieldName,
GetFieldDataTypeString(entity.EntityName, field),
GetFieldCreationAttributes(entity.EntityAttribute, field)));
using (var command = new SQLiteCommand(alter.ToString(), connection as SQLiteConnection))
{
command.ExecuteNonQuery();
}
}
else
{
// TODO: verify field length, etc.
}
}
return;
}
protected override string VerifyIndex(string entityName, string fieldName, FieldSearchOrder searchOrder, IDbConnection connection)
{
bool localConnection = false;
if (connection == null)
{
localConnection = true;
connection = GetConnection(true);
}
try
{
var indexName = string.Format("ORM_IDX_{0}_{1}_{2}", entityName, fieldName,
searchOrder == FieldSearchOrder.Descending ? "DESC" : "ASC");
if (m_indexNameCache.FirstOrDefault(ii => ii.Name == indexName) != null) return indexName;
using (var command = GetNewCommandObject())
{
command.Connection = connection;
var sql = string.Format("SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name = '{0}'", indexName);
command.CommandText = sql;
var i = (long)command.ExecuteScalar();
if (i == 0)
{
sql = string.Format("CREATE INDEX {0} ON {1}({2} {3})",
indexName,
entityName,
fieldName,
searchOrder == FieldSearchOrder.Descending ? "DESC" : string.Empty);
Debug.WriteLine(sql);
command.CommandText = sql;
command.Transaction = CurrentTransaction;
command.ExecuteNonQuery();
}
var indexinfo = new IndexInfo
{
Name = indexName,
MaxCharLength = -1
};
m_indexNameCache.Add(indexinfo);
}
return indexName;
}
finally
{
if (localConnection)
{
DoneWithConnection(connection, true);
}
}
}
protected override IEnumerable<object> Select(Type objectType, IEnumerable<FilterCondition> filters, int fetchCount, int firstRowOffset, bool fillReferences)
{
string entityName = m_entities.GetNameForType(objectType);
if (entityName == null)
{
throw new EntityNotFoundException(objectType);
}
return Select(entityName, objectType, filters, fetchCount, firstRowOffset, fillReferences);
}
private IEnumerable<object> Select(string entityName, Type objectType, IEnumerable<FilterCondition> filters, int fetchCount, int firstRowOffset, bool fillReferences)
{
if (entityName == null)
{
throw new EntityNotFoundException(objectType);
}
if (!Entities.Contains(entityName))
{
if(DiscoverDynamicEntity(entityName) == null) yield return null;
}
UpdateIndexCacheForType(entityName);
var items = new List<object>();
var connection = GetConnection(false);
SQLiteCommand command = null;
try
{
CheckOrdinals(entityName);
bool tableDirect;
command = GetSelectCommand<SQLiteCommand, SQLiteParameter>(entityName, filters, out tableDirect);
command.Connection = connection as SQLiteConnection;
command.Transaction = CurrentTransaction as SQLiteTransaction;
int searchOrdinal = -1;
// ResultSetOptions options = ResultSetOptions.Scrollable;
object matchValue = null;
string matchField = null;
// TODO: we need to ensure that the search value does not exceed the length of the indexed
// field, else we'll get an exception on the Seek call below (see the SQL CE implementation)
using (var results = command.ExecuteReader(CommandBehavior.SingleResult))
{
if (results.HasRows)
{
ReferenceAttribute[] referenceFields = null;
int currentOffset = 0;
if (matchValue != null)
{
// convert enums to an int, else the .Equals later check will fail
// this feels a bit kludgey, but for now it's all I can think of
if (matchValue.GetType().IsEnum)
{
matchValue = (int)matchValue;
}
if (searchOrdinal < 0)
{
searchOrdinal = results.GetOrdinal(matchField);
}
}
while (results.Read())
{
if (currentOffset < firstRowOffset)
{
currentOffset++;
continue;
}
// autofill references if desired
if (referenceFields == null)
{
referenceFields = Entities[entityName].References.ToArray();
}
bool fieldsSet;
object item = CreateEntityInstance(entityName, objectType, Entities[entityName].Fields, results, out fieldsSet);
object rowPK = null;
if(!fieldsSet)
{
foreach (var field in Entities[entityName].Fields)
{
MethodInfo mi = null;
if (!field.PropertyInfo.CanWrite)
{
// get a private accessor?
mi = field.PropertyInfo.GetSetMethod(true);
// not settable, so skip
if (mi == null) continue;
}
var value = results[field.Ordinal];
if (value != DBNull.Value)
{
if (field.DataType == DbType.Object)
{
if (fillReferences)
{
// get serializer
var itemType = item.GetType();
var deserializer = GetDeserializer(itemType);
if (deserializer == null)
{
throw new MissingMethodException(
string.Format("The field '{0}' requires a custom serializer/deserializer method pair in the '{1}' Entity",
field.FieldName, entityName));
}
var @object = deserializer.Invoke(item, new object[] { field.FieldName, value });
field.PropertyInfo.SetValue(item, @object, null);
}
}
else if (field.IsRowVersion)
{
// sql stores this an 8-byte array
field.PropertyInfo.SetValue(item, BitConverter.ToInt64((byte[])value, 0), null);
}
else if (field.IsTimespan)
{
// SQLite doesn't support "timespan" - and date/time must be stored as text, real or 64-bit integer (see the SQLite docs for more details)
// here we'll pull TimeSpans (since they can be negative) as an offset from a base date
var storeDate = (DateTime)value;
var storeTime = storeDate - new DateTime(1980, 1, 1);
field.PropertyInfo.SetValue(item, storeTime, null);
}
else if (field.DataType == DbType.DateTime)
{
field.PropertyInfo.SetValue(item, Convert.ToDateTime(value), null);
}
else if ((field.IsPrimaryKey) && (value is Int64))
{
if (field.PropertyInfo.PropertyType.Equals(typeof(int)))
{
// SQLite automatically makes auto-increment fields 64-bit, so this works around that behavior
field.PropertyInfo.SetValue(item, Convert.ToInt32(value), null);
}
else
{
field.PropertyInfo.SetValue(item, Convert.ToInt64(value), null);
}
}
else if ((value is Int64) || (value is double))
{
// SQLite is "interesting" in that its 'integer' has a strong affinity toward 64-bit, so int and uint properties
// end up as 64-bit fields. Decimals have a strong affinity toward 'double', so float properties
// end up as 'double'. Even more fun is that a decimal value '0' will come back as an int64
// When we query those back, we must convert to put them into the property or we crash hard
object setval;
if (field.PropertyInfo.PropertyType.Equals(typeof(UInt32)))
{
setval = Convert.ToUInt32(value);
}
else if ((field.PropertyInfo.PropertyType.Equals(typeof(Int32))) || (field.PropertyInfo.PropertyType.Equals(typeof(Int32?))))
{
setval = Convert.ToInt32(value);
}
else if (field.PropertyInfo.PropertyType.Equals(typeof(decimal)))
{
setval = Convert.ToDecimal(value);
}
else if (field.PropertyInfo.PropertyType.Equals(typeof(float)))
{
setval = Convert.ToSingle(value);
}
else if (field.PropertyInfo.PropertyType.IsEnum)
{
setval = Enum.ToObject(field.PropertyInfo.PropertyType, value);
}
else
{
setval = value;
}
if (mi == null)
{
field.PropertyInfo.SetValue(item, setval, null);
}
else
{
mi.Invoke(setval, null);
}
}
else
{
var t = value.GetType();
field.PropertyInfo.SetValue(item, value, null);
}
}
if (field.IsPrimaryKey)
{
rowPK = value;
}
}
}
if ((fillReferences) && (referenceFields.Length > 0))
{
//FillReferences(item, rowPK, referenceFields, true);
FillReferences(item, rowPK, referenceFields, false);
}
// changed from
// items.Add(item);
yield return item;
if ((fetchCount > 0) && (items.Count >= fetchCount))
{
break;
}
}
}
}
}
finally
{
if ((!UseCommandCache) && (command != null))
{
command.Dispose();
}
if (UseCommandCache)
{
Monitor.Exit(CommandCache);
}
FlushReferenceTableCache();
DoneWithConnection(connection, false);
}
}
public override void OnUpdate(object item, bool cascadeUpdates, string fieldName)
{
if (item is DynamicEntity)
{
OnUpdateDynamicEntity(item as DynamicEntity);
return;
}
object keyValue;
var changeDetected = false;
var itemType = item.GetType();
string entityName = m_entities.GetNameForType(itemType);
var insertCommand = GetNewCommandObject();
var updateSQL = new StringBuilder(string.Format("UPDATE {0} SET ", entityName));
if (entityName == null)
{
throw new EntityNotFoundException(itemType);
}
if (Entities[entityName].Fields.KeyField == null)
{
throw new PrimaryKeyRequiredException("A primary key is required on an Entity in order to perform Updates");
}
var connection = GetConnection(false);
try
{
CheckOrdinals(entityName);
CheckPrimaryKeyIndex(entityName);
using (var command = GetNewCommandObject())
{
keyValue = Entities[entityName].Fields.KeyField.PropertyInfo.GetValue(item, null);
command.Connection = connection;
command.CommandText = string.Format("SELECT * FROM {0} WHERE [{1}] = {2}keyparam",
entityName,
Entities[entityName].Fields.KeyField.FieldName,
ParameterPrefix);
command.CommandType = CommandType.Text;
command.Parameters.Add(new SQLiteParameter(ParameterPrefix + "keyparam", keyValue));
command.Transaction = CurrentTransaction;
using (var reader = command.ExecuteReader() as SQLiteDataReader)
{
if (!reader.HasRows)
{
// TODO: the PK value has changed - we need to store the original value in the entity or diallow this kind of change
throw new RecordNotFoundException("Cannot locate a record with the provided primary key. You cannot update a primary key value through the Update method");
}
reader.Read();
// update the values
foreach (var field in Entities[entityName].Fields)
{
// do not update PK fields
if (field.IsPrimaryKey)
{
continue;
}
else if (fieldName != null && field.FieldName != fieldName)
{
continue; // if we pass in a field name, skip over any fields that don't match
}
else if (field.IsRowVersion)
{
// read-only, so do nothing
}
else if (field.DataType == DbType.Object)
{
changeDetected = true;
// get serializer
var serializer = GetSerializer(itemType);
if (serializer == null)
{
throw new MissingMethodException(
string.Format("The field '{0}' requires a custom serializer/deserializer method pair in the '{1}' Entity",
field.FieldName, entityName));
}
var value = serializer.Invoke(item, new object[] { field.FieldName });
if (value == null)
{
updateSQL.AppendFormat("{0}=NULL, ", field.FieldName);
}
else
{
updateSQL.AppendFormat("{0}={1}{0}, ", field.FieldName, ParameterPrefix);
insertCommand.Parameters.Add(new SQLiteParameter(ParameterPrefix + field.FieldName, value));
}
}
else if (field.PropertyInfo.PropertyType.UnderlyingTypeIs<TimeSpan>())
{
changeDetected = true;
// SQL Compact doesn't support Time, so we're convert to ticks in both directions
var value = field.PropertyInfo.GetValue(item, null);
if (value == null)
{
updateSQL.AppendFormat("{0}=NULL, ", field.FieldName);
}
else
{
var ticks = ((TimeSpan)value).Ticks;
updateSQL.AppendFormat("{0}={1}{0}, ", field.FieldName, ParameterPrefix);
insertCommand.Parameters.Add(new SQLiteParameter(ParameterPrefix + field.FieldName, ticks));
}
}
else
{
var value = field.PropertyInfo.GetValue(item, null);
if (reader[field.FieldName] != value)
{
changeDetected = true;
if (value == null)
{
updateSQL.AppendFormat("{0}=NULL, ", field.FieldName);
}
else
{
updateSQL.AppendFormat("{0}={1}{0}, ", field.FieldName, ParameterPrefix);
insertCommand.Parameters.Add(new SQLiteParameter(ParameterPrefix + field.FieldName, value));
}
}
}
} // foreach
} // execute reader
} // using command
} // try
finally
{
DoneWithConnection(connection, false);
}
// only execute if a change occurred
if (changeDetected)
{
connection = GetConnection(false);
try
{
// remove the trailing comma and append the filter
updateSQL.Length -= 2;
updateSQL.AppendFormat(" WHERE {0} = {1}keyparam", Entities[entityName].Fields.KeyField.FieldName, ParameterPrefix);
insertCommand.Parameters.Add(new SQLiteParameter(ParameterPrefix + "keyparam", keyValue));
insertCommand.CommandText = updateSQL.ToString();
insertCommand.Connection = connection;
insertCommand.Transaction = CurrentTransaction;
insertCommand.ExecuteNonQuery();
}
finally
{
DoneWithConnection(connection, false);
}
}
if (cascadeUpdates)
{
// TODO: move this into the base DataStore class as it's not SqlCe-specific
foreach (var reference in Entities[entityName].References)
{
var itemList = reference.PropertyInfo.GetValue(item, null) as Array;
if (itemList != null)
{
foreach (var refItem in itemList)
{
if (!this.Contains(refItem))
{
var foreignKey = refItem.GetType().GetProperty(reference.ForeignReferenceField, BindingFlags.Instance | BindingFlags.Public);
foreignKey.SetValue(refItem, keyValue, null);
Insert(refItem, false);
}
else
{
Update(refItem, true, fieldName);
}
}
}
}
}
}
public override int Count<T>(IEnumerable<FilterCondition> filters)
{
var t = typeof(T);
string entityName = m_entities.GetNameForType(t);
if (entityName == null)
{
throw new EntityNotFoundException(t);
}
var connection = GetConnection(true);
try
{
using (var command = BuildFilterCommand<SQLiteCommand, SQLiteParameter>(entityName, filters, true))
{
command.Connection = connection as SQLiteConnection;
return (int)command.ExecuteScalar();
}
}
finally
{
DoneWithConnection(connection, true);
}
}
public override IEnumerable<T> Fetch<T>(int fetchCount, int firstRowOffset, string sortField, FieldSearchOrder sortOrder, FilterCondition filter, bool fillReferences)
{
throw new NotSupportedException("Fetch is not currently supported with this Provider.");
}
protected override string GetFieldDataTypeString(string entityName, FieldAttribute field)
{
// a SQLite Int64 auto-increment key requires being called "INTEGER", not "BIGINT"
if(field.IsPrimaryKey && (field.DataType == DbType.Int64))
{
if (GetEntityInfo(entityName).EntityAttribute.KeyScheme == KeyScheme.Identity)
{
return "INTEGER";
}
}
return base.GetFieldDataTypeString(entityName, field);
}
}</pre><p> </p>