Index: Doc/Extra/Provider/environment.html ================================================================== --- Doc/Extra/Provider/environment.html +++ Doc/Extra/Provider/environment.html @@ -78,10 +78,21 @@ + + + + + + + +
Name Description
AppendManifestToken_SQLiteProviderManifestIf this environment variable is set [to anything], it will be + used by the System.Data.SQLite.Linq.SQLiteProviderManifest class + (and the System.Data.SQLite.EF6.SQLiteProviderManifest class) to + modify future provider manifest tokens by appending the value of + the environment variable to the existing provider manifest token, + if any. Typically, in order for the constructed provider manifest + token to be syntactically correct, the environment variable value + [to be appended] must begin with a semicolon.
Force_SQLiteLog If this environment variable is set [to anything], the SQLite logging subsystem may be initialized in a non-default application domain. By default, this is not allowed due to the potential for @@ -151,10 +162,16 @@ system itself and should reflect the native processor architecture of the current process (e.g. a 32-bit x86 application running on a 64-bit x64 operating system should have the value "x86").
SQLite_ForceLogPrepareIf this environment variable is set [to anything], all calls to + prepare a SQL query will be logged, regardless of the flags for the + associated connection.
TypeName_SQLiteProviderServices If this environment variable is set [to anything], it will be used by the System.Data.SQLite.SQLiteFactory class as the type name containing the System.Data.Common.DbProviderServices implementation Index: Doc/Extra/Provider/version.html ================================================================== --- Doc/Extra/Provider/version.html +++ Doc/Extra/Provider/version.html @@ -53,10 +53,13 @@
  • In the SQLiteDataReader.VerifyType method, remove duplicate "if" statement for the DbType.SByte value and move the remaining "if" to the Int64 affinity. Fix for [c5cc2fb334]** Potentially Incompatible Change **
  • Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for [3e783eecbe]** Potentially Incompatible Change **
  • Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for [e796ac82c1]** Potentially Incompatible Change **
  • Make sure the interop files are copied before the PostBuildEvent. Fix for [f16c93a932]** Potentially Incompatible Change **
  • Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for [47c6fa04d3]** Potentially Incompatible Change **
  • +
  • Add AppendManifestToken_SQLiteProviderManifest environment variable to enable better integration between LINQ and the underlying store connection.
  • +
  • Add SQLite_ForceLogPrepare environment variable to force logging of all prepared SQL regardless of the flags for the associated connection.
  • +
  • Honor the DateTimeFormat, DateTimeKind, DateTimeFormatString, BinaryGUID connection string and/or provider manifest token properties from within the LINQ assembly. Fix for [8d928c3e88]** Potentially Incompatible Change **
  • 1.0.94.0 - September 9, 2014

    • Updated to SQLite 3.8.6.
    • Updated to Entity Framework 6.1.1.
    • Index: System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.EF6.ssdl ================================================================== --- System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.EF6.ssdl +++ System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.EF6.ssdl @@ -275,11 +275,11 @@ - + @@ -477,11 +477,11 @@ - + Index: System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.Linq.ssdl ================================================================== --- System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.Linq.ssdl +++ System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.Linq.ssdl @@ -1,7 +1,7 @@ - + SELECT '[' || TABLE_NAME || ']' COLLATE NOCASE [Id] @@ -275,11 +275,11 @@ - + @@ -477,11 +477,11 @@ - + Index: System.Data.SQLite.Linq/SQL Generation/SqlBuilder.cs ================================================================== --- System.Data.SQLite.Linq/SQL Generation/SqlBuilder.cs +++ System.Data.SQLite.Linq/SQL Generation/SqlBuilder.cs @@ -99,10 +99,14 @@ ISqlFragment sqlFragment = (o as ISqlFragment); if (null != sqlFragment) { sqlFragment.WriteSql(writer, sqlGenerator); } + else if (o is char) + { + writer.Write((char)o); + } else { throw new InvalidOperationException(); } } Index: System.Data.SQLite.Linq/SQL Generation/SqlGenerator.cs ================================================================== --- System.Data.SQLite.Linq/SQL Generation/SqlGenerator.cs +++ System.Data.SQLite.Linq/SQL Generation/SqlGenerator.cs @@ -118,11 +118,11 @@ /// with respect to the query. The names are derived from the original names, /// with an integer as a suffix. e.g. CustomerId will be renamed to CustomerId1, /// CustomerId2 etc. /// /// Since the names generated are globally unique, they will not conflict when the - /// columns of a JOIN SELECT statement are joined with another JOIN. + /// columns of a JOIN SELECT statement are joined with another JOIN. /// /// /// /// /// Record flattening. @@ -436,11 +436,11 @@ #endregion #region Constructor /// - /// Basic constructor. + /// Basic constructor. /// private SqlGenerator(SQLiteProviderManifest manifest) { _manifest = manifest; } @@ -464,11 +464,11 @@ DbQueryCommandTree queryCommandTree = tree as DbQueryCommandTree; if (queryCommandTree != null) { SqlGenerator sqlGen = new SqlGenerator(manifest); parameters = null; - + string sql = sqlGen.GenerateSql((DbQueryCommandTree)tree); return sql; } @@ -873,13 +873,11 @@ case PrimitiveTypeKind.Int32: result.Append(e.Value.ToString()); break; case PrimitiveTypeKind.Binary: - result.Append(" X'"); - result.Append(ByteArrayToBinaryString((Byte[])e.Value)); - result.Append("' "); + ToBlobLiteral((byte[])e.Value, result); break; case PrimitiveTypeKind.Boolean: result.Append((bool)e.Value ? "1" : "0"); break; @@ -887,11 +885,25 @@ case PrimitiveTypeKind.Byte: result.Append(e.Value.ToString()); break; case PrimitiveTypeKind.DateTime: - result.Append(EscapeSingleQuote(SQLiteConvert.ToString((System.DateTime)e.Value, SQLiteDateFormats.ISO8601, DateTimeKind.Unspecified, null), false /* IsUnicode */)); + bool needQuotes = NeedSingleQuotes(_manifest._dateTimeFormat); + + string dateString = SQLiteConvert.ToString( + (System.DateTime)e.Value, _manifest._dateTimeFormat, + _manifest._dateTimeKind, _manifest._dateTimeFormatString); + + if (needQuotes) + { + result.Append(EscapeSingleQuote( + dateString, false /* IsUnicode */)); + } + else + { + result.Append(dateString); + } break; case PrimitiveTypeKind.Decimal: string strDecimal = ((Decimal)e.Value).ToString(CultureInfo.InvariantCulture); // if the decimal value has no decimal part, cast as decimal to preserve type @@ -921,11 +933,18 @@ case PrimitiveTypeKind.Double: result.Append(((Double)e.Value).ToString(CultureInfo.InvariantCulture)); break; case PrimitiveTypeKind.Guid: - result.Append(EscapeSingleQuote(e.Value.ToString(), false /* IsUnicode */)); + { + object value = e.Value; + + if (_manifest._binaryGuid && (value is Guid)) + ToBlobLiteral(((Guid)value).ToByteArray(), result); + else + result.Append(EscapeSingleQuote(e.Value.ToString(), false /* IsUnicode */)); + } break; case PrimitiveTypeKind.Int16: result.Append(e.Value.ToString()); break; @@ -1125,11 +1144,11 @@ /// /// Canonical Functions - We recognize these by their dataspace, it is DataSpace.CSpace /// Store Functions - We recognize these by the BuiltInAttribute and not being Canonical /// User-defined Functions - All the rest except for Lambda functions /// - /// We handle Canonical and Store functions the same way: If they are in the list of functions + /// We handle Canonical and Store functions the same way: If they are in the list of functions /// that need special handling, we invoke the appropriate handler, otherwise we translate them to /// FunctionName(arg1, arg2, ..., argn). /// We translate user-defined functions to NamespaceName.FunctionName(arg1, arg2, ..., argn). /// /// @@ -1176,43 +1195,43 @@ /// /// for general details. /// We modify both the GroupBy and the Select fields of the SqlSelectStatement. /// GroupBy gets just the keys without aliases, /// and Select gets the keys and the aggregates with aliases. - /// + /// /// Whenever there exists at least one aggregate with an argument that is not is not a simple - /// over , - /// we create a nested query in which we alias the arguments to the aggregates. + /// over , + /// we create a nested query in which we alias the arguments to the aggregates. /// That is due to the following two limitations of Sql Server: /// - /// If an expression being aggregated contains an outer reference, then that outer + /// If an expression being aggregated contains an outer reference, then that outer /// reference must be the only column referenced in the expression - /// Sql Server cannot perform an aggregate function on an expression containing + /// Sql Server cannot perform an aggregate function on an expression containing /// an aggregate or a subquery. /// - /// - /// The default translation, without inner query is: - /// - /// SELECT - /// kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, + /// + /// The default translation, without inner query is: + /// + /// SELECT + /// kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, /// aggf1(aexpr1) AS agg1, .. aggfn(aexprn) AS aggn /// FROM input AS a /// GROUP BY kexp1, kexp2, .. kexpn - /// + /// /// When we inject an innner query, the equivalent translation is: - /// - /// SELECT - /// key1 AS key1, key2 AS key2, .. keyn AS keys, + /// + /// SELECT + /// key1 AS key1, key2 AS key2, .. keyn AS keys, /// aggf1(agg1) AS agg1, aggfn(aggn) AS aggn /// FROM ( - /// SELECT - /// kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, + /// SELECT + /// kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, /// aexpr1 AS agg1, .. aexprn AS aggn /// FROM input AS a /// ) as a /// GROUP BY key1, key2, keyn - /// + /// /// /// /// A public override ISqlFragment Visit(DbGroupByExpression e) { @@ -1238,11 +1257,11 @@ // The enumerator is shared by both the keys and the aggregates, // so, we do not close it in between. RowType groupByType = MetadataHelpers.GetEdmType(MetadataHelpers.GetEdmType(e.ResultType).TypeUsage); - //Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression + //Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression // over a VarRefExpression, we need a nested query in which we alias the arguments to the aggregates. bool needsInnerQuery = NeedsInnerQuery(e.Aggregates); SqlSelectStatement result; if (needsInnerQuery) @@ -1290,11 +1309,11 @@ innerQuery.Select.AppendLine(); innerQuery.Select.Append(keySql); innerQuery.Select.Append(" AS "); innerQuery.Select.Append(alias); - //The outer resulting query projects over the key aliased in the inner query: + //The outer resulting query projects over the key aliased in the inner query: // fromSymbol.Alias AS Alias result.Select.Append(separator); result.Select.AppendLine(); result.Select.Append(fromSymbol); result.Select.Append("."); @@ -1464,11 +1483,11 @@ SqlBuilder result = new SqlBuilder(); result.Append(e.Argument.Accept(this)); result.Append(" LIKE "); result.Append(e.Pattern.Accept(this)); - // if the ESCAPE expression is a DbNullExpression, then that's tantamount to + // if the ESCAPE expression is a DbNullExpression, then that's tantamount to // not having an ESCAPE at all if (e.Escape.ExpressionKind != DbExpressionKind.Null) { result.Append(" ESCAPE "); result.Append(e.Escape.Accept(this)); @@ -1814,14 +1833,14 @@ /// /// For Sql9 it translates to: /// SELECT Y.x1, Y.x2, ..., Y.xn /// FROM ( - /// SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number] - /// FROM input as X + /// SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number] + /// FROM input as X /// ) as Y - /// WHERE Y.[row_number] > count + /// WHERE Y.[row_number] > count /// ORDER BY sk1, sk2, ... /// /// /// A public override ISqlFragment Visit(DbSkipExpression e) @@ -2617,12 +2636,12 @@ return handlers[e.Function.Name](this, e); } /// /// Handles functions that are translated into TSQL operators. - /// The given function should have one or two arguments. - /// Functions with one arguemnt are translated into + /// The given function should have one or two arguments. + /// Functions with one arguemnt are translated into /// op arg /// Functions with two arguments are translated into /// arg0 op arg1 /// Also, the arguments can be optionaly enclosed in parethesis /// @@ -2689,11 +2708,11 @@ private static ISqlFragment HandleGetDateFunction(SqlGenerator sqlgen, DbFunctionExpression e) { SqlBuilder result = new SqlBuilder(); Debug.Assert(e.Arguments.Count == 0, "Canonical getdate function should have no arguments"); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append("(STRFTIME('%s', 'now') * 10000000 + 621355968000000000)"); break; case SQLiteDateFormats.JulianDay: @@ -2710,11 +2729,11 @@ private static ISqlFragment HandleGetUtcDateFunction(SqlGenerator sqlgen, DbFunctionExpression e) { SqlBuilder result = new SqlBuilder(); Debug.Assert(e.Arguments.Count == 0, "Canonical getutcdate function should have no arguments"); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append("(STRFTIME('%s', 'now', 'utc') * 10000000 + 621355968000000000)"); break; case SQLiteDateFormats.JulianDay: @@ -2769,11 +2788,11 @@ // expand the datepart literal as tsql kword result.Append(trans); result.Append("', "); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[1].Accept(sqlgen))); break; default: @@ -2785,11 +2804,11 @@ } else { result.Append("CAST(SUBSTR(STRFTIME('%f', "); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[1].Accept(sqlgen))); break; default: @@ -2812,11 +2831,11 @@ private static ISqlFragment HandleCanonicalFunctionDateAdd(SqlGenerator sqlgen, DbFunctionExpression e) { SqlBuilder result = new SqlBuilder(); Debug.Assert(e.Arguments.Count == 2, "Canonical datepart functions should have exactly two arguments"); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append(String.Format("(STRFTIME('%s', JULIANDAY({1}) + ({0} / 86400.0)) * 10000000 + 621355968000000000)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen))); break; case SQLiteDateFormats.JulianDay: @@ -2829,21 +2848,21 @@ return result; } /// - /// DateSubtract(datetime1, datetime2) -> DATEDIFF ( seconds , startdate , enddate ) + /// DateSubtract(datetime1, datetime2) -> DATEDIFF ( seconds , startdate , enddate ) /// /// /// /// private static ISqlFragment HandleCanonicalFunctionDateSubtract(SqlGenerator sqlgen, DbFunctionExpression e) { SqlBuilder result = new SqlBuilder(); Debug.Assert(e.Arguments.Count == 2, "Canonical datepart functions should have exactly two arguments"); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append(String.Format("CAST((({0} - 621355968000000000) / 10000000.0) - (({1} - 621355968000000000) / 10000000.0) * 86400.0 AS integer)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen))); break; default: @@ -2853,11 +2872,11 @@ return result; } /// - /// Handler for canonical functions for extracting date parts. + /// Handler for canonical functions for extracting date parts. /// For example: /// Year(date) -> DATEPART( year, date) /// /// /// @@ -2875,11 +2894,11 @@ result.Append(trans); result.Append("', "); Debug.Assert(e.Arguments.Count == 1, "Canonical datepart functions should have exactly one argument"); - switch (sqlgen._manifest._dateFormat) + switch (sqlgen._manifest._dateTimeFormat) { case SQLiteDateFormats.Ticks: result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[0].Accept(sqlgen))); break; default: @@ -3312,11 +3331,11 @@ } } /// /// Translates a list of SortClauses. - /// Used in the translation of OrderBy + /// Used in the translation of OrderBy /// /// The SqlBuilder to which the sort keys should be appended /// void AddSortKeys(SqlBuilder orderByClause, IList sortKeys) { @@ -3423,10 +3442,29 @@ return selectStatement; } + /// + /// Determines if values of the specified + /// require wrapping in single quotes. + /// + /// + /// The format. + /// + /// + /// Non-zero if single quotes are required for a value in the specified + /// . + /// + private static bool NeedSingleQuotes( + SQLiteDateFormats format + ) + { + return format != SQLiteDateFormats.Ticks && + format != SQLiteDateFormats.JulianDay && + format != SQLiteDateFormats.UnixEpoch; + } /// /// Before we embed a string literal in a SQL string, we should /// convert all ' to '', and enclose the whole string in single quotes. /// @@ -3437,12 +3475,12 @@ { return "'" + s.Replace("'", "''") + "'"; } /// - /// Returns the sql primitive/native type name. - /// It will include size, precision or scale depending on type information present in the + /// Returns the sql primitive/native type name. + /// It will include size, precision or scale depending on type information present in the /// type facets /// /// /// private string GetSqlPrimitiveType(TypeUsage type) @@ -3565,11 +3603,11 @@ { ISqlFragment result; if (e.ExpressionKind == DbExpressionKind.Constant) { - //For constant expression we should not cast the value, + //For constant expression we should not cast the value, // thus we don't go throught the default DbConstantExpression handling SqlBuilder sqlBuilder = new SqlBuilder(); sqlBuilder.Append(((DbConstantExpression)e).Value.ToString()); result = sqlBuilder; } @@ -3650,11 +3688,11 @@ { switch (expressionKind) { case DbExpressionKind.Distinct: return result.Top == null - // The projection after distinct may not project all + // The projection after distinct may not project all // columns used in the Order By && result.OrderBy.IsEmpty; case DbExpressionKind.Filter: return result.Select.IsEmpty @@ -3773,11 +3811,11 @@ /// -- originally {expression} /// -- change that to /// SELECT * /// FROM {expression} as c /// - /// + /// /// DbLimitExpression needs to start the statement but not add the default columns /// /// /// /// @@ -3959,24 +3997,50 @@ //result.Append("."); result.Append(QuoteIdentifier(storeFunctionName)); } } - static string ByteArrayToBinaryString(Byte[] binaryArray) - { - StringBuilder sb = new StringBuilder(binaryArray.Length * 2); - for (int i = 0; i < binaryArray.Length; i++) - { - sb.Append(hexDigits[(binaryArray[i] & 0xF0) >> 4]).Append(hexDigits[binaryArray[i] & 0x0F]); - } - return sb.ToString(); + /// + /// Appends the literal BLOB string representation of the specified + /// byte array to the . + /// + /// + /// The byte array to be formatted as a literal BLOB string. + /// + /// + /// The object to use. If null, an exception + /// will be thrown. + /// + private static void ToBlobLiteral( + byte[] bytes, + SqlBuilder builder + ) + { + if (builder == null) + throw new ArgumentNullException("builder"); + + if (bytes == null) + { + builder.Append("NULL"); /* TODO: Reasonable? */ + return; + } + + builder.Append(" X'"); + + for (int index = 0; index < bytes.Length; index++) + { + builder.Append(hexDigits[(bytes[index] & 0xF0) >> 4]); + builder.Append(hexDigits[bytes[index] & 0x0F]); + } + + builder.Append("' "); } /// /// Helper method for the Group By visitor /// Returns true if at least one of the aggregates in the given list - /// has an argument that is not a + /// has an argument that is not a /// over /// /// /// static bool NeedsInnerQuery(IList aggregates) @@ -3991,11 +4055,11 @@ } return false; } /// - /// Determines whether the given expression is a + /// Determines whether the given expression is a /// over /// /// /// static bool IsPropertyOverVarRef(DbExpression expression) @@ -4012,11 +4076,11 @@ } return true; } #endregion - + private class KeyFieldExpressionComparer : IEqualityComparer { // Fields internal static readonly SqlGenerator.KeyFieldExpressionComparer Singleton = new SqlGenerator.KeyFieldExpressionComparer(); Index: System.Data.SQLite.Linq/SQLiteProviderManifest.cs ================================================================== --- System.Data.SQLite.Linq/SQLiteProviderManifest.cs +++ System.Data.SQLite.Linq/SQLiteProviderManifest.cs @@ -10,49 +10,204 @@ #else namespace System.Data.SQLite.Linq #endif { using System; - using System.Data; - using System.Reflection; + using System.Collections.Generic; using System.IO; + using System.Reflection; + +#if !PLATFORM_COMPACTFRAMEWORK + using System.Text; +#endif + using System.Xml; - using System.Data.Common; #if USE_ENTITY_FRAMEWORK_6 using System.Data.Entity.Core; using System.Data.Entity.Core.Common; using System.Data.Entity.Core.Metadata.Edm; #else + using System.Data.Common; using System.Data.Metadata.Edm; #endif /// /// The Provider Manifest for SQL Server /// internal sealed class SQLiteProviderManifest : DbXmlEnabledProviderManifest { - internal SQLiteDateFormats _dateFormat; + internal SQLiteDateFormats _dateTimeFormat; + internal DateTimeKind _dateTimeKind; + internal string _dateTimeFormatString; + internal bool _binaryGuid; /// /// Constructs the provider manifest. /// /// - /// We pass the token as a DateTimeFormat enum text, because all the datetime functions - /// are vastly different depending on how the user is opening the connection + /// Previously, the manifest token was interpreted as a , + /// because the functions are vastly different depending on the + /// connection was opened. However, the manifest token may specify a connection string + /// instead. WARNING: Only the "DateTimeFormat", "DateTimeKind", "DateTimeFormatString", + /// and "BinaryGUID" connection parameters are extracted from it. All other connection + /// parameters, if any are present, are silently ignored. /// - /// A token used to infer the capabilities of the store + /// + /// A token used to infer the capabilities of the store. + /// public SQLiteProviderManifest(string manifestToken) - : base(SQLiteProviderManifest.GetProviderManifest()) + : base(GetProviderManifest()) { - _dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), manifestToken, true); + SetFromOptions(ParseProviderManifestToken(GetProviderManifestToken(manifestToken))); } - internal static XmlReader GetProviderManifest() + private static XmlReader GetProviderManifest() { return GetXmlResource("System.Data.SQLite.SQLiteProviderServices.ProviderManifest.xml"); } + + /// + /// Determines and returns the effective provider manifest token to use, + /// based on the specified provider manifest token and the environment, + /// if applicable. + /// + /// + /// The original provider manifest token passed to the constructor for this + /// class. + /// + /// + /// The effective provider manifest token. + /// + private static string GetProviderManifestToken( + string manifestToken + ) + { +#if !PLATFORM_COMPACTFRAMEWORK + string value = UnsafeNativeMethods.GetSettingValue( + "AppendManifestToken_SQLiteProviderManifest", null); + + if (String.IsNullOrEmpty(value)) + return manifestToken; + + int capacity = value.Length; + + if (manifestToken != null) + capacity += manifestToken.Length; + + StringBuilder builder = new StringBuilder(capacity); + + builder.Append(manifestToken); + builder.Append(value); + + return builder.ToString(); +#else + // + // NOTE: The .NET Compact Framework lacks environment variable support. + // Therefore, just return the original provider manifest token + // verbatim in this case. + // + return manifestToken; +#endif + } + + /// + /// Attempts to parse a provider manifest token. It must contain either a + /// legacy string that specifies the value + /// -OR- string that uses the standard connection string syntax; otherwise, + /// the results are undefined. + /// + /// + /// The manifest token to parse. + /// + /// + /// The dictionary containing the connection string parameters. + /// + private static SortedList ParseProviderManifestToken( + string manifestToken + ) + { + return SQLiteConnection.ParseConnectionString(manifestToken, false, true); + } + + /// + /// Attempts to set the provider manifest options from the specified + /// connection string parameters. An exception may be thrown if one + /// or more of the connection string parameter values do not conform + /// to the expected type. + /// + /// + /// The dictionary containing the connection string parameters. + /// + internal void SetFromOptions( + SortedList opts + ) + { + _dateTimeFormat = SQLiteConnection.DefaultDateTimeFormat; + _dateTimeKind = SQLiteConnection.DefaultDateTimeKind; + _dateTimeFormatString = SQLiteConnection.DefaultDateTimeFormatString; + _binaryGuid = /* SQLiteConnection.DefaultBinaryGUID; */ false; /* COMPAT: Legacy. */ + + if (opts == null) + return; + +#if !PLATFORM_COMPACTFRAMEWORK + string[] names = Enum.GetNames(typeof(SQLiteDateFormats)); +#else + string[] names = { + SQLiteDateFormats.Ticks.ToString(), + SQLiteDateFormats.ISO8601.ToString(), + SQLiteDateFormats.JulianDay.ToString(), + SQLiteDateFormats.UnixEpoch.ToString(), + SQLiteDateFormats.InvariantCulture.ToString(), + SQLiteDateFormats.CurrentCulture.ToString(), + "Default" + }; +#endif + + foreach (string name in names) + { + if (String.IsNullOrEmpty(name)) + continue; + + object value = SQLiteConnection.FindKey(opts, name, null); + + if (value == null) + continue; + + _dateTimeFormat = (SQLiteDateFormats)Enum.Parse( + typeof(SQLiteDateFormats), name, true); + } + + object enumValue; + + enumValue = SQLiteConnection.TryParseEnum( + typeof(SQLiteDateFormats), SQLiteConnection.FindKey( + opts, "DateTimeFormat", null), true); + + if (enumValue is SQLiteDateFormats) + _dateTimeFormat = (SQLiteDateFormats)enumValue; + + enumValue = SQLiteConnection.TryParseEnum( + typeof(DateTimeKind), SQLiteConnection.FindKey( + opts, "DateTimeKind", null), true); + + if (enumValue is DateTimeKind) + _dateTimeKind = (DateTimeKind)enumValue; + + string stringValue = SQLiteConnection.FindKey( + opts, "DateTimeFormatString", null); + + if (stringValue != null) + _dateTimeFormatString = stringValue; + + stringValue = SQLiteConnection.FindKey( + opts, "BinaryGUID", null); + + if (stringValue != null) + _binaryGuid = SQLiteConvert.ToBoolean(stringValue); + } /// /// Returns manifest information for the provider /// /// The name of the information to be retrieved. Index: System.Data.SQLite.Linq/SQLiteProviderServices.cs ================================================================== --- System.Data.SQLite.Linq/SQLiteProviderServices.cs +++ System.Data.SQLite.Linq/SQLiteProviderServices.cs @@ -9,16 +9,16 @@ namespace System.Data.SQLite.EF6 #else namespace System.Data.SQLite.Linq #endif { - using System; - using System.Data.Common; - using System.Diagnostics; + using System; using System.Collections.Generic; - using System.Text; + using System.Data.Common; + using System.Diagnostics; using System.Globalization; + using System.Text; #if USE_ENTITY_FRAMEWORK_6 using System.Data.Entity.Core.Common; using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Core.Common.CommandTrees; @@ -107,24 +107,18 @@ throw; } } protected override string GetDbProviderManifestToken(DbConnection connection) - { - if (String.IsNullOrEmpty(connection.ConnectionString)) - throw new ArgumentNullException("ConnectionString"); - - bool parseViaFramework = false; - - if (connection is SQLiteConnection) - parseViaFramework = ((SQLiteConnection)connection).ParseViaFramework; - - SortedList opts = parseViaFramework ? - SQLiteConnection.ParseConnectionStringViaFramework(connection.ConnectionString, false) : - SQLiteConnection.ParseConnectionString(connection.ConnectionString); - - return SQLiteConnection.FindKey(opts, "DateTimeFormat", "ISO8601"); + { + if (connection == null) + throw new ArgumentNullException("connection"); + + if (String.IsNullOrEmpty(connection.ConnectionString)) + throw new ArgumentNullException("ConnectionString"); + + return connection.ConnectionString; } protected override DbProviderManifest GetDbProviderManifest(string versionHint) { return new SQLiteProviderManifest(versionHint); Index: System.Data.SQLite/Configurations/System.Data.SQLite.dll.config ================================================================== --- System.Data.SQLite/Configurations/System.Data.SQLite.dll.config +++ System.Data.SQLite/Configurations/System.Data.SQLite.dll.config @@ -7,10 +7,24 @@ * Released to the public domain, use at your own risk! * --> + + + + + +