diff --git a/.nuget/packages.config b/.nuget/packages.config deleted file mode 100644 index af17f43c..00000000 --- a/.nuget/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Dapper.Contrib/Dapper.Contrib.1.0.nupkg b/Dapper.Contrib/Dapper.Contrib.1.0.nupkg deleted file mode 100644 index 3ff7c017..00000000 Binary files a/Dapper.Contrib/Dapper.Contrib.1.0.nupkg and /dev/null differ diff --git a/Dapper.Contrib/Dapper.Contrib.csproj b/Dapper.Contrib/Dapper.Contrib.csproj new file mode 100644 index 00000000..d2045728 --- /dev/null +++ b/Dapper.Contrib/Dapper.Contrib.csproj @@ -0,0 +1,27 @@ + + + Dapper.Contrib + orm;sql;micro-orm;dapper + Dapper.Contrib + The official collection of get, insert, update and delete helpers for Dapper.net. Also handles lists of entities and optional "dirty" tracking of interface-based entities. + Sam Saffron;Johan Danforth + net40;net45;netstandard1.3 + + false + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dapper.Contrib/Dapper.Contrib.xproj b/Dapper.Contrib/Dapper.Contrib.xproj deleted file mode 100644 index 1ae445ae..00000000 --- a/Dapper.Contrib/Dapper.Contrib.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 4e409f8f-cfbb-4332-8b0a-fd5a283051fd - Dapper.Contrib - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper.Contrib/Properties/AssemblyInfo.cs b/Dapper.Contrib/Properties/AssemblyInfo.cs deleted file mode 100644 index de92a912..00000000 --- a/Dapper.Contrib/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Dapper.Contrib")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Dapper.Contrib")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("6dde1c15-4e92-45e7-93fc-88778d15ff31")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.50.0.0")] -[assembly: AssemblyFileVersion("1.50.0.0")] diff --git a/Dapper.Contrib/project.json b/Dapper.Contrib/project.json deleted file mode 100644 index 3d510c94..00000000 --- a/Dapper.Contrib/project.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "packOptions": { - "summary": "Dapper.Contrib", - "tags": [ "orm", "sql", "micro-orm", "dapper" ], - "owners": [ "johandanforth", "marc.gravell", "nick.craver" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Sam Saffron", "Johan Danforth" ], - "description": "The official collection of get, insert, update and delete helpers for dapper.net. Also handles lists of entities and optional \"dirty\" tracking of interface-based entities.", - "version": "1.50.2-*", - "title": "Dapper.Contrib", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - "Dapper": { - "version": "1.50.2-*", - "target": "project" - } - }, - "buildOptions": { - "warningsAsErrors": true, - "compile": { - "include": [ - "**/*.cs" - ], - "includeFiles": [ - "../Dapper/TypeExtensions.cs" - ] - } - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0" - }, - "dependencies": { - } - }, - "netstandard1.3": { - "buildOptions": { - "define": [ "ASYNC", "COREFX" ] - }, - "dependencies": { - "Microsoft.CSharp": "4.0.1" - } - } - } -} \ No newline at end of file diff --git a/Dapper.EntityFramework.StrongName/Dapper.EntityFramework.StrongName.xproj b/Dapper.EntityFramework.StrongName/Dapper.EntityFramework.StrongName.xproj deleted file mode 100644 index e7adcfc7..00000000 --- a/Dapper.EntityFramework.StrongName/Dapper.EntityFramework.StrongName.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 1a70b6d7-244e-41ed-8ff5-6f0e8e26a764 - Dapper.EntityFramework.StrongName - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper.EntityFramework.StrongName/project.json b/Dapper.EntityFramework.StrongName/project.json deleted file mode 100644 index 0475f7cd..00000000 --- a/Dapper.EntityFramework.StrongName/project.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "packOptions": { - "summary": "Adds support for DbGeography, etc.", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "marc.gravell", "nick.craver" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Marc Gravell", "Nick Craver" ], - "description": "Extension handlers for entity framework", - "version": "1.50.2-*", - "title": "Dapper entity framework type handlers (with a strong name)", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - "Dapper.StrongName": { - "version": "1.50.2-*", - "target": "project" - } - }, - "buildOptions": { - "xmlDoc": true, - "warningsAsErrors": true, - "keyFile": "../Dapper.snk", - "compile": { - "include": [ - "../Dapper.EntityFramework/**/*.cs" - ], - "exclude": [ - "../Dapper.EntityFramework/obj/" - ] - } - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0", - "System.Xml": "4.0.0.0" - }, - "dependencies": { - "EntityFramework": "6.1.3", - "Microsoft.SqlServer.Types": "11.0.2" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0", - "System.Xml": "4.0.0.0" - }, - "dependencies": { - "EntityFramework": "6.1.3", - "Microsoft.SqlServer.Types": "11.0.2" - } - } - } -} \ No newline at end of file diff --git a/Dapper.EntityFramework/Dapper.EntityFramework.StrongName.csproj b/Dapper.EntityFramework/Dapper.EntityFramework.StrongName.csproj new file mode 100644 index 00000000..b74179dd --- /dev/null +++ b/Dapper.EntityFramework/Dapper.EntityFramework.StrongName.csproj @@ -0,0 +1,25 @@ + + + Dapper.EntityFramework.StrongName + Dapper: Entity Framework type handlers (with a strong name) + Extension handlers for entity framework + Marc Gravell;Nick Craver + net40;net45 + ../Dapper.snk + true + true + Dapper.EntityFramework.StrongName + orm;sql;micro-orm + + + + + + + + + + + + + diff --git a/Dapper.EntityFramework/Dapper.EntityFramework.csproj b/Dapper.EntityFramework/Dapper.EntityFramework.csproj new file mode 100644 index 00000000..f4fb3d22 --- /dev/null +++ b/Dapper.EntityFramework/Dapper.EntityFramework.csproj @@ -0,0 +1,22 @@ + + + Dapper.EntityFramework + Extension handlers for entity framework + Dapper entity framework type handlers + 1.50.2 + Marc Gravell;Nick Craver + net40;net45 + orm;sql;micro-orm + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dapper.EntityFramework/Dapper.EntityFramework.xproj b/Dapper.EntityFramework/Dapper.EntityFramework.xproj deleted file mode 100644 index d1164faa..00000000 --- a/Dapper.EntityFramework/Dapper.EntityFramework.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - be401f7b-8611-4a1e-aeaa-5cb700128c16 - Dapper.EntityFramework - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper.EntityFramework/Properties/AssemblyInfo.cs b/Dapper.EntityFramework/Properties/AssemblyInfo.cs deleted file mode 100644 index 790774b6..00000000 --- a/Dapper.EntityFramework/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Dapper.EntityFramework")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Dapper.EntityFramework")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("2400e6b9-a925-45d8-ab5a-07f119b0eedb")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Dapper.EntityFramework/project.json b/Dapper.EntityFramework/project.json deleted file mode 100644 index 58e081c0..00000000 --- a/Dapper.EntityFramework/project.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "packOptions": { - "summary": "Adds support for DbGeography, etc.", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "marc.gravell", "nick.craver" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Marc Gravell", "Nick Craver" ], - "description": "Extension handlers for entity framework", - "version": "1.50.2-*", - "title": "Dapper entity framework type handlers", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - "Dapper": { - "version": "1.50.2-*", - "target": "project" - } - }, - "buildOptions": { - "xmlDoc": true, - "warningsAsErrors": true - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0", - "System.Xml": "4.0.0.0" - }, - "dependencies": { - "EntityFramework": "6.1.3", - "Microsoft.SqlServer.Types": "11.0.2" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0", - "System.Xml": "4.0.0.0" - }, - "dependencies": { - "EntityFramework": "6.1.3", - "Microsoft.SqlServer.Types": "11.0.2" - } - } - } -} \ No newline at end of file diff --git a/Dapper.Rainbow/Dapper.Rainbow.csproj b/Dapper.Rainbow/Dapper.Rainbow.csproj new file mode 100644 index 00000000..84ce55ac --- /dev/null +++ b/Dapper.Rainbow/Dapper.Rainbow.csproj @@ -0,0 +1,29 @@ + + + Dapper.Rainbow + orm;sql;micro-orm + Dapper.Rainbow + Trivial micro-orm implemented on Dapper, provides with CRUD helpers. + Sam Saffron + 2017 Sam Saffron + net40;net45;netstandard1.3 + + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dapper.Rainbow/Dapper.Rainbow.xproj b/Dapper.Rainbow/Dapper.Rainbow.xproj deleted file mode 100644 index 1f09cf7f..00000000 --- a/Dapper.Rainbow/Dapper.Rainbow.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 8a74f0b6-188f-45d2-8a4b-51e4f211805a - Dapper.Rainbow - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper.Rainbow/Database.cs b/Dapper.Rainbow/Database.cs index 109089f8..d0f6c48b 100644 --- a/Dapper.Rainbow/Database.cs +++ b/Dapper.Rainbow/Database.cs @@ -106,7 +106,7 @@ public IEnumerable All() return database.Query("select * from " + TableName); } - static ConcurrentDictionary> paramNameCache = new ConcurrentDictionary>(); + private static ConcurrentDictionary> paramNameCache = new ConcurrentDictionary>(); internal static List GetParamNames(object o) { @@ -127,7 +127,7 @@ internal static List GetParamNames(object o) if (attr==null || (!attr.Value)) { paramNames.Add(prop.Name); - } + } } paramNameCache[o.GetType()] = paramNames; } @@ -142,9 +142,9 @@ public Table(Database database, string likelyTableName) } } - DbConnection _connection; - int _commandTimeout; - DbTransaction _transaction; + private DbConnection _connection; + private int _commandTimeout; + private DbTransaction _transaction; public static TDatabase Init(DbConnection connection, int commandTimeout) { @@ -240,7 +240,7 @@ protected Action CreateTableConstructor(params Type[] tableTypes) return (Action)dm.CreateDelegate(typeof(Action)); } - static ConcurrentDictionary tableNameMap = new ConcurrentDictionary(); + private static ConcurrentDictionary tableNameMap = new ConcurrentDictionary(); private string DetermineTableName(string likelyTableName) { string name; diff --git a/Dapper.Rainbow/Properties/AssemblyInfo.cs b/Dapper.Rainbow/Properties/AssemblyInfo.cs deleted file mode 100644 index 8ea5cd0f..00000000 --- a/Dapper.Rainbow/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Dapper.Rainbow")] -[assembly: AssemblyDescription("I sample mini ORM implementation using Dapper")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Dapper.Rainbow")] -[assembly: AssemblyCopyright("Sam Saffron Copyright © 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("e763c106-eef4-4654-afcc-c28fded057e5")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("0.1.0.0")] -[assembly: AssemblyFileVersion("0.1.0.0")] diff --git a/Dapper.Rainbow/Snapshotter.cs b/Dapper.Rainbow/Snapshotter.cs index f4bc57cb..043f2b42 100644 --- a/Dapper.Rainbow/Snapshotter.cs +++ b/Dapper.Rainbow/Snapshotter.cs @@ -1,203 +1,200 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; - -namespace Dapper -{ - public static class Snapshotter - { - public static Snapshot Start(T obj) - { - return new Snapshot(obj); - } - - public class Snapshot - { - static Func cloner; - static Func> differ; - T memberWiseClone; - T trackedObject; - - public Snapshot(T original) - { - memberWiseClone = Clone(original); - trackedObject = original; - } - - public class Change - { - public string Name { get; set; } - public object NewValue { get; set; } - } - - public DynamicParameters Diff() - { - return Diff(memberWiseClone, trackedObject); - } - - - private static T Clone(T myObject) - { - cloner = cloner ?? GenerateCloner(); - return cloner(myObject); - } - - private static DynamicParameters Diff(T original, T current) - { - var dm = new DynamicParameters(); - differ = differ ?? GenerateDiffer(); - foreach (var pair in differ(original, current)) - { - dm.Add(pair.Name, pair.NewValue); - } - return dm; - } - - - static List RelevantProperties() - { - return typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(p => - p.GetSetMethod(true) != null && - p.GetGetMethod(true) != null && +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace Dapper +{ + public static class Snapshotter + { + public static Snapshot Start(T obj) + { + return new Snapshot(obj); + } + + public class Snapshot + { + static Func cloner; + static Func> differ; + T memberWiseClone; + T trackedObject; + + public Snapshot(T original) + { + memberWiseClone = Clone(original); + trackedObject = original; + } + + public class Change + { + public string Name { get; set; } + public object NewValue { get; set; } + } + + public DynamicParameters Diff() + { + return Diff(memberWiseClone, trackedObject); + } + + private static T Clone(T myObject) + { + cloner = cloner ?? GenerateCloner(); + return cloner(myObject); + } + + private static DynamicParameters Diff(T original, T current) + { + var dm = new DynamicParameters(); + differ = differ ?? GenerateDiffer(); + foreach (var pair in differ(original, current)) + { + dm.Add(pair.Name, pair.NewValue); + } + return dm; + } + + static List RelevantProperties() + { + return typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(p => + p.GetSetMethod(true) != null && + p.GetGetMethod(true) != null && (p.PropertyType == typeof(string) || - p.PropertyType.IsValueType() || - (p.PropertyType.IsGenericType() && p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))) - ).ToList(); - } - - // This is used by IL, ReSharper is wrong. + p.PropertyType.IsValueType() || + (p.PropertyType.IsGenericType() && p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))) + ).ToList(); + } + + // This is used by IL, ReSharper is wrong. // ReSharper disable UnusedMember.Local - private static bool AreEqual(U first, U second) - { - if (first == null && second == null) return true; - if (first == null) return false; - return first.Equals(second); + private static bool AreEqual(U first, U second) + { + if (first == null && second == null) return true; + if (first == null) return false; + return first.Equals(second); } // ReSharper restore UnusedMember.Local - private static Func> GenerateDiffer() - { - - var dm = new DynamicMethod("DoDiff", typeof(List), new[] { typeof(T), typeof(T) }, true); - - var il = dm.GetILGenerator(); - // change list - il.DeclareLocal(typeof(List)); - il.DeclareLocal(typeof(Change)); - il.DeclareLocal(typeof(object)); // boxed change - - il.Emit(OpCodes.Newobj, typeof(List).GetConstructor(Type.EmptyTypes)); - // [list] - il.Emit(OpCodes.Stloc_0); - - foreach (var prop in RelevantProperties()) - { - // [] - il.Emit(OpCodes.Ldarg_0); - // [original] - il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true)); - // [original prop val] - il.Emit(OpCodes.Ldarg_1); - // [original prop val, current] - il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true)); - // [original prop val, current prop val] - - il.Emit(OpCodes.Dup); - // [original prop val, current prop val, current prop val] - - if (prop.PropertyType != typeof(string)) - { - il.Emit(OpCodes.Box, prop.PropertyType); - // [original prop val, current prop val, current prop val boxed] - } - - il.Emit(OpCodes.Stloc_2); - // [original prop val, current prop val] - - il.EmitCall(OpCodes.Call, typeof(Snapshot).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type[] { prop.PropertyType }), null); - // [result] - - Label skip = il.DefineLabel(); - il.Emit(OpCodes.Brtrue_S, skip); - // [] - - il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes)); - // [change] - il.Emit(OpCodes.Dup); - // [change,change] - - il.Emit(OpCodes.Stloc_1); - // [change] - - il.Emit(OpCodes.Ldstr, prop.Name); - // [change, name] - il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name")); - // [] - - il.Emit(OpCodes.Ldloc_1); - // [change] - - il.Emit(OpCodes.Ldloc_2); - // [change, boxed] - - il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue")); - // [] - - il.Emit(OpCodes.Ldloc_0); - // [change list] - il.Emit(OpCodes.Ldloc_1); - // [change list, change] - il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("Add")); - // [] - - il.MarkLabel(skip); - } - - il.Emit(OpCodes.Ldloc_0); - // [change list] - il.Emit(OpCodes.Ret); - - return (Func>)dm.CreateDelegate(typeof(Func>)); - } - - - // adapted from http://stackoverflow.com/a/966466/17174 - private static Func GenerateCloner() - { - var dm = new DynamicMethod("DoClone", typeof(T), new Type[] { typeof(T) }, true); - var ctor = typeof(T).GetConstructor(new Type[] { }); - - var il = dm.GetILGenerator(); - - il.DeclareLocal(typeof(T)); - - il.Emit(OpCodes.Newobj, ctor); - il.Emit(OpCodes.Stloc_0); - - foreach (var prop in RelevantProperties()) - { - il.Emit(OpCodes.Ldloc_0); - // [clone] - il.Emit(OpCodes.Ldarg_0); - // [clone, source] - il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true)); - // [clone, source val] - il.Emit(OpCodes.Callvirt, prop.GetSetMethod(true)); - // [] - } - - // Load new constructed obj on eval stack -> 1 item on stack - il.Emit(OpCodes.Ldloc_0); - // Return constructed object. --> 0 items on stack - il.Emit(OpCodes.Ret); - - var myExec = dm.CreateDelegate(typeof(Func)); - - return (Func)myExec; - } - } - } -} + private static Func> GenerateDiffer() + { + var dm = new DynamicMethod("DoDiff", typeof(List), new[] { typeof(T), typeof(T) }, true); + + var il = dm.GetILGenerator(); + // change list + il.DeclareLocal(typeof(List)); + il.DeclareLocal(typeof(Change)); + il.DeclareLocal(typeof(object)); // boxed change + + il.Emit(OpCodes.Newobj, typeof(List).GetConstructor(Type.EmptyTypes)); + // [list] + il.Emit(OpCodes.Stloc_0); + + foreach (var prop in RelevantProperties()) + { + // [] + il.Emit(OpCodes.Ldarg_0); + // [original] + il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true)); + // [original prop val] + il.Emit(OpCodes.Ldarg_1); + // [original prop val, current] + il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true)); + // [original prop val, current prop val] + + il.Emit(OpCodes.Dup); + // [original prop val, current prop val, current prop val] + + if (prop.PropertyType != typeof(string)) + { + il.Emit(OpCodes.Box, prop.PropertyType); + // [original prop val, current prop val, current prop val boxed] + } + + il.Emit(OpCodes.Stloc_2); + // [original prop val, current prop val] + + il.EmitCall(OpCodes.Call, typeof(Snapshot).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type[] { prop.PropertyType }), null); + // [result] + + Label skip = il.DefineLabel(); + il.Emit(OpCodes.Brtrue_S, skip); + // [] + + il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes)); + // [change] + il.Emit(OpCodes.Dup); + // [change,change] + + il.Emit(OpCodes.Stloc_1); + // [change] + + il.Emit(OpCodes.Ldstr, prop.Name); + // [change, name] + il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name")); + // [] + + il.Emit(OpCodes.Ldloc_1); + // [change] + + il.Emit(OpCodes.Ldloc_2); + // [change, boxed] + + il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue")); + // [] + + il.Emit(OpCodes.Ldloc_0); + // [change list] + il.Emit(OpCodes.Ldloc_1); + // [change list, change] + il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("Add")); + // [] + + il.MarkLabel(skip); + } + + il.Emit(OpCodes.Ldloc_0); + // [change list] + il.Emit(OpCodes.Ret); + + return (Func>)dm.CreateDelegate(typeof(Func>)); + } + + + // adapted from https://stackoverflow.com/a/966466/17174 + private static Func GenerateCloner() + { + var dm = new DynamicMethod("DoClone", typeof(T), new Type[] { typeof(T) }, true); + var ctor = typeof(T).GetConstructor(new Type[] { }); + + var il = dm.GetILGenerator(); + + il.DeclareLocal(typeof(T)); + + il.Emit(OpCodes.Newobj, ctor); + il.Emit(OpCodes.Stloc_0); + + foreach (var prop in RelevantProperties()) + { + il.Emit(OpCodes.Ldloc_0); + // [clone] + il.Emit(OpCodes.Ldarg_0); + // [clone, source] + il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true)); + // [clone, source val] + il.Emit(OpCodes.Callvirt, prop.GetSetMethod(true)); + // [] + } + + // Load new constructed obj on eval stack -> 1 item on stack + il.Emit(OpCodes.Ldloc_0); + // Return constructed object. --> 0 items on stack + il.Emit(OpCodes.Ret); + + var myExec = dm.CreateDelegate(typeof(Func)); + + return (Func)myExec; + } + } + } +} diff --git a/Dapper.Rainbow/project.json b/Dapper.Rainbow/project.json deleted file mode 100644 index b2cdc1e2..00000000 --- a/Dapper.Rainbow/project.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "packOptions": { - "summary": "A demo is available at https://gist.github.com/1599013", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "samsaffron" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Sam Saffron" ], - "description": "Trivial micro-orm implemented on Dapper, provides with CRUD helpers.", - "version": "0.1.3-beta1", - "title": "Dapper.Rainbow", - "copyright": "2012 Sam Saffron", - "dependencies": { - "Dapper": { - "version": "1.50.2-*", - "target": "project" - } - }, - "buildOptions": { - "warningsAsErrors": true, - "compile": { - "include": [ - "**/*.cs" - ], - "includeFiles": [ - "../Dapper/TypeExtensions.cs" - ] - } - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0" - } - }, - "netstandard1.3": { - "buildOptions": { - "define": [ "ASYNC", "COREFX" ] - }, - "dependencies": { - "Microsoft.CSharp": "4.0.1" - } - } - } -} \ No newline at end of file diff --git a/Dapper.SqlBuilder/Dapper.SqlBuilder.csproj b/Dapper.SqlBuilder/Dapper.SqlBuilder.csproj new file mode 100644 index 00000000..519a417a --- /dev/null +++ b/Dapper.SqlBuilder/Dapper.SqlBuilder.csproj @@ -0,0 +1,25 @@ + + + Dapper.SqlBuilder + orm;sql;micro-orm;query;sql-builder + Dapper SqlBuilder component + The Dapper SqlBuilder component, for building SQL queries dynamically. + Sam Saffron, Johan Danforth + net40;net45;netstandard1.3 + + false + false + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dapper.SqlBuilder/Dapper.SqlBuilder.xproj b/Dapper.SqlBuilder/Dapper.SqlBuilder.xproj deleted file mode 100644 index 90346437..00000000 --- a/Dapper.SqlBuilder/Dapper.SqlBuilder.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 196928f0-7052-4585-90e8-817bd720f5e3 - Dapper.SqlBuilder - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper.SqlBuilder/Properties/AssemblyInfo.cs b/Dapper.SqlBuilder/Properties/AssemblyInfo.cs deleted file mode 100644 index e49e8049..00000000 --- a/Dapper.SqlBuilder/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Dapper.SqlBuilder")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Dapper.SqlBuilder")] -[assembly: AssemblyCopyright("Copyright © 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("27491c26-c95d-44e5-b907-30559ef11265")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.50.0.0")] -[assembly: AssemblyFileVersion("1.50.0.0")] diff --git a/Dapper.SqlBuilder/SqlBuilder.cs b/Dapper.SqlBuilder/SqlBuilder.cs index a395abe7..729ede03 100644 --- a/Dapper.SqlBuilder/SqlBuilder.cs +++ b/Dapper.SqlBuilder/SqlBuilder.cs @@ -21,7 +21,7 @@ class Clauses : List private readonly string _joiner; private readonly string _prefix; private readonly string _postfix; - + public Clauses(string joiner, string prefix = "", string postfix = "") { _joiner = joiner; @@ -56,7 +56,7 @@ public class Template private readonly SqlBuilder _builder; private readonly object _initParams; private int _dataSeq = -1; // Unresolved - + public Template(SqlBuilder builder, string sql, dynamic parameters) { _initParams = parameters; @@ -110,73 +110,73 @@ protected void AddClause(string name, string sql, object parameters, string join clauses.Add(new Clause { Sql = sql, Parameters = parameters, IsInclusive = isInclusive }); _seq++; } - + public SqlBuilder Intersect(string sql, dynamic parameters = null) { AddClause("intersect", sql, parameters, "\nINTERSECT\n ", "\n ", "\n", false); return this; } - + public SqlBuilder InnerJoin(string sql, dynamic parameters = null) { AddClause("innerjoin", sql, parameters, "\nINNER JOIN ", "\nINNER JOIN ", "\n", false); return this; } - + public SqlBuilder LeftJoin(string sql, dynamic parameters = null) { AddClause("leftjoin", sql, parameters, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n", false); return this; } - + public SqlBuilder RightJoin(string sql, dynamic parameters = null) { AddClause("rightjoin", sql, parameters, "\nRIGHT JOIN ", "\nRIGHT JOIN ", "\n", false); return this; } - + public SqlBuilder Where(string sql, dynamic parameters = null) { AddClause("where", sql, parameters, " AND ", "WHERE ", "\n", false); return this; } - + public SqlBuilder OrWhere(string sql, dynamic parameters = null) { AddClause("where", sql, parameters, " OR ", "WHERE ", "\n", true); return this; } - + public SqlBuilder OrderBy(string sql, dynamic parameters = null) { AddClause("orderby", sql, parameters, " , ", "ORDER BY ", "\n", false); return this; } - + public SqlBuilder Select(string sql, dynamic parameters = null) { AddClause("select", sql, parameters, " , ", "", "\n", false); return this; } - + public SqlBuilder AddParameters(dynamic parameters) { AddClause("--parameters", "", parameters, "", "", "", false); return this; } - + public SqlBuilder Join(string sql, dynamic parameters = null) { AddClause("join", sql, parameters, "\nJOIN ", "\nJOIN ", "\n", false); return this; } - + public SqlBuilder GroupBy(string sql, dynamic parameters = null) { AddClause("groupby", sql, parameters, " , ", "\nGROUP BY ", "\n", false); return this; } - + public SqlBuilder Having(string sql, dynamic parameters = null) { AddClause("having", sql, parameters, "\nAND ", "HAVING ", "\n", false); diff --git a/Dapper.SqlBuilder/project.json b/Dapper.SqlBuilder/project.json deleted file mode 100644 index 1ef6e1bd..00000000 --- a/Dapper.SqlBuilder/project.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "packOptions": { - "summary": "Dapper.SqlBuilder", - "tags": [ "orm", "sql", "micro-orm", "query", "sql-builder" ], - "owners": [ "AEckenberger", "nick.craver" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Sam Saffron, Johan Danforth" ], - "description": "The Dapper SqlBuilder component, for building SQL queries dynamically.", - "version": "1.50.2-*", - "title": "Dapper SqlBuilder component", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - "Dapper": { - "version": "1.50.2-*", - "target": "project" - } - }, - "buildOptions": { - "warningsAsErrors": true - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0" - } - }, - "netstandard1.3": { - "buildOptions": { - "define": [ "ASYNC", "COREFX" ] - }, - "dependencies": { - "Microsoft.CSharp": "4.0.1" - } - } - } -} \ No newline at end of file diff --git a/Dapper.StrongName/Dapper.StrongName.xproj b/Dapper.StrongName/Dapper.StrongName.xproj deleted file mode 100644 index 5e17c192..00000000 --- a/Dapper.StrongName/Dapper.StrongName.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 549c51a1-222b-4e12-96f1-3aeff45a7709 - Dapper.StrongName - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper.StrongName/project.json b/Dapper.StrongName/project.json deleted file mode 100644 index 487c0f72..00000000 --- a/Dapper.StrongName/project.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "packOptions": { - "summary": "A high performance Micro-ORM", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "marc.gravell", "nick.craver" ], - "releaseNotes": "http://stackexchange.github.io/dapper-dot-net/", - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Sam Saffron", "Marc Gravell", "Nick Craver" ], - "description": "A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc..", - "version": "1.50.2-*", - "title": "Dapper dot net (strong named)", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - }, - "buildOptions": { - "xmlDoc": true, - "keyFile": "../Dapper.snk", - "warningsAsErrors": true, - "compile": { - "include": [ - "../Dapper/**/*.cs" - ], - "exclude": [ - "../Dapper/obj/" - ] - } - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - } - }, - "net451": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - } - }, - "netstandard1.3": { - "buildOptions": { - "define": [ "ASYNC", "COREFX" ] - }, - "dependencies": { - "System.Collections": "4.0.11", - "System.Collections.Concurrent": "4.0.12", - "System.Collections.NonGeneric": "4.0.1", - "System.Data.SqlClient": "4.1.0", - "System.Dynamic.Runtime": "4.0.11", - "System.Linq": "4.1.0", - "System.Reflection": "4.1.0", - "System.Reflection.Emit": "4.0.1", - "System.Reflection.Emit.Lightweight": "4.0.1", - "System.Reflection.Extensions": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.RegularExpressions": "4.1.0", - "System.Threading": "4.0.11", - "System.Xml.XDocument": "4.0.11", - "System.Xml.XmlDocument": "4.0.1" - } - } - } -} diff --git a/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj b/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj new file mode 100644 index 00000000..2c594a27 --- /dev/null +++ b/Dapper.Tests.Contrib/Dapper.Tests.Contrib.csproj @@ -0,0 +1,32 @@ + + + Dapper.Tests.Contrib + Dapper.Tests.Contrib + Dapper Contrib Test Suite + portable + Exe + false + netcoreapp1.0 + + + $(DefineConstants);COREFX; + + + + + + + + + + + + + + + + + + + + diff --git a/Dapper.Tests.Contrib/Dapper.Tests.Contrib.xproj b/Dapper.Tests.Contrib/Dapper.Tests.Contrib.xproj deleted file mode 100644 index 322c83f0..00000000 --- a/Dapper.Tests.Contrib/Dapper.Tests.Contrib.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - dab3c5b7-bcd1-4a5f-bb6b-50d2bb63db4a - Dapper.Tests.Contrib - .\obj - .\bin\ - - - 2.0 - - - - - - \ No newline at end of file diff --git a/Dapper.Tests.Contrib/Program.cs b/Dapper.Tests.Contrib/Program.cs deleted file mode 100644 index 74fd3286..00000000 --- a/Dapper.Tests.Contrib/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Dapper.Tests.Contrib -{ - public class Program - { - static void Main(string[] args) - { - - } - } -} diff --git a/Dapper.Tests.Contrib/Properties/AssemblyInfo.cs b/Dapper.Tests.Contrib/Properties/AssemblyInfo.cs deleted file mode 100644 index d421e183..00000000 --- a/Dapper.Tests.Contrib/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Dapper.Tests.Contrib")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Dapper.Tests.Contrib")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9d5920b6-d6af-41ca-b851-803ac922d933")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Dapper.Tests.Contrib/TestSuite.Async.cs b/Dapper.Tests.Contrib/TestSuite.Async.cs index 9be5c726..39f28bf6 100644 --- a/Dapper.Tests.Contrib/TestSuite.Async.cs +++ b/Dapper.Tests.Contrib/TestSuite.Async.cs @@ -1,15 +1,10 @@ -#if ASYNC -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Dapper.Contrib.Extensions; -#if XUNIT2 using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute; -#else -using Xunit; -#endif namespace Dapper.Tests.Contrib { @@ -25,34 +20,34 @@ public async Task InsertGetUpdateDeleteWithExplicitKeyAsync() { var guid = Guid.NewGuid().ToString(); var o1 = new ObjectX { ObjectXId = guid, Name = "Foo" }; - var originalxCount = (await connection.QueryAsync("Select Count(*) From ObjectX")).First(); - await connection.InsertAsync(o1); - var list1 = (await connection.QueryAsync("select * from ObjectX")).ToList(); + var originalxCount = (await connection.QueryAsync("Select Count(*) From ObjectX").ConfigureAwait(false)).First(); + await connection.InsertAsync(o1).ConfigureAwait(false); + var list1 = (await connection.QueryAsync("select * from ObjectX").ConfigureAwait(false)).ToList(); list1.Count.IsEqualTo(originalxCount + 1); - o1 = await connection.GetAsync(guid); + o1 = await connection.GetAsync(guid).ConfigureAwait(false); o1.ObjectXId.IsEqualTo(guid); o1.Name = "Bar"; - await connection.UpdateAsync(o1); - o1 = await connection.GetAsync(guid); + await connection.UpdateAsync(o1).ConfigureAwait(false); + o1 = await connection.GetAsync(guid).ConfigureAwait(false); o1.Name.IsEqualTo("Bar"); - await connection.DeleteAsync(o1); - o1 = await connection.GetAsync(guid); + await connection.DeleteAsync(o1).ConfigureAwait(false); + o1 = await connection.GetAsync(guid).ConfigureAwait(false); o1.IsNull(); const int id = 42; var o2 = new ObjectY { ObjectYId = id, Name = "Foo" }; var originalyCount = connection.Query("Select Count(*) From ObjectY").First(); - await connection.InsertAsync(o2); - var list2 = (await connection.QueryAsync("select * from ObjectY")).ToList(); + await connection.InsertAsync(o2).ConfigureAwait(false); + var list2 = (await connection.QueryAsync("select * from ObjectY").ConfigureAwait(false)).ToList(); list2.Count.IsEqualTo(originalyCount+1); - o2 = await connection.GetAsync(id); + o2 = await connection.GetAsync(id).ConfigureAwait(false); o2.ObjectYId.IsEqualTo(id); o2.Name = "Bar"; - await connection.UpdateAsync(o2); - o2 = await connection.GetAsync(id); + await connection.UpdateAsync(o2).ConfigureAwait(false); + o2 = await connection.GetAsync(id).ConfigureAwait(false); o2.Name.IsEqualTo("Bar"); - await connection.DeleteAsync(o2); - o2 = await connection.GetAsync(id); + await connection.DeleteAsync(o2).ConfigureAwait(false); + o2 = await connection.GetAsync(id).ConfigureAwait(false); o2.IsNull(); } } @@ -63,14 +58,14 @@ public async Task TableNameAsync() using (var connection = GetOpenConnection()) { // tests against "Automobiles" table (Table attribute) - var id = await connection.InsertAsync(new Car { Name = "VolvoAsync" }); - var car = await connection.GetAsync(id); + var id = await connection.InsertAsync(new Car { Name = "VolvoAsync" }).ConfigureAwait(false); + var car = await connection.GetAsync(id).ConfigureAwait(false); car.IsNotNull(); car.Name.IsEqualTo("VolvoAsync"); - (await connection.UpdateAsync(new Car { Id = id, Name = "SaabAsync" })).IsEqualTo(true); - (await connection.GetAsync(id)).Name.IsEqualTo("SaabAsync"); - (await connection.DeleteAsync(new Car { Id = id })).IsEqualTo(true); - (await connection.GetAsync(id)).IsNull(); + (await connection.UpdateAsync(new Car { Id = id, Name = "SaabAsync" }).ConfigureAwait(false)).IsEqualTo(true); + (await connection.GetAsync(id).ConfigureAwait(false)).Name.IsEqualTo("SaabAsync"); + (await connection.DeleteAsync(new Car { Id = id }).ConfigureAwait(false)).IsEqualTo(true); + (await connection.GetAsync(id).ConfigureAwait(false)).IsNull(); } } @@ -79,11 +74,11 @@ public async Task TestSimpleGetAsync() { using (var connection = GetOpenConnection()) { - var id = await connection.InsertAsync(new User { Name = "Adama", Age = 10 }); - var user = await connection.GetAsync(id); + var id = await connection.InsertAsync(new User { Name = "Adama", Age = 10 }).ConfigureAwait(false); + var user = await connection.GetAsync(id).ConfigureAwait(false); user.Id.IsEqualTo(id); user.Name.IsEqualTo("Adama"); - await connection.DeleteAsync(user); + await connection.DeleteAsync(user).ConfigureAwait(false); } } @@ -92,37 +87,37 @@ public async Task InsertGetUpdateAsync() { using (var connection = GetOpenConnection()) { - (await connection.GetAsync(30)).IsNull(); + (await connection.GetAsync(30).ConfigureAwait(false)).IsNull(); - var originalCount = (await connection.QueryAsync("select Count(*) from Users")).First(); + var originalCount = (await connection.QueryAsync("select Count(*) from Users").ConfigureAwait(false)).First(); - var id = await connection.InsertAsync(new User { Name = "Adam", Age = 10 }); + var id = await connection.InsertAsync(new User { Name = "Adam", Age = 10 }).ConfigureAwait(false); //get a user with "isdirty" tracking - var user = await connection.GetAsync(id); + var user = await connection.GetAsync(id).ConfigureAwait(false); user.Name.IsEqualTo("Adam"); - (await connection.UpdateAsync(user)).IsEqualTo(false); //returns false if not updated, based on tracking + (await connection.UpdateAsync(user).ConfigureAwait(false)).IsEqualTo(false); //returns false if not updated, based on tracking user.Name = "Bob"; - (await connection.UpdateAsync(user)).IsEqualTo(true); //returns true if updated, based on tracking - user = await connection.GetAsync(id); + (await connection.UpdateAsync(user).ConfigureAwait(false)).IsEqualTo(true); //returns true if updated, based on tracking + user = await connection.GetAsync(id).ConfigureAwait(false); user.Name.IsEqualTo("Bob"); //get a user with no tracking - var notrackedUser = await connection.GetAsync(id); + var notrackedUser = await connection.GetAsync(id).ConfigureAwait(false); notrackedUser.Name.IsEqualTo("Bob"); - (await connection.UpdateAsync(notrackedUser)).IsEqualTo(true); + (await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)).IsEqualTo(true); //returns true, even though user was not changed notrackedUser.Name = "Cecil"; - (await connection.UpdateAsync(notrackedUser)).IsEqualTo(true); - (await connection.GetAsync(id)).Name.IsEqualTo("Cecil"); + (await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)).IsEqualTo(true); + (await connection.GetAsync(id).ConfigureAwait(false)).Name.IsEqualTo("Cecil"); - (await connection.QueryAsync("select * from Users")).Count().IsEqualTo(originalCount+1); - (await connection.DeleteAsync(user)).IsEqualTo(true); - (await connection.QueryAsync("select * from Users")).Count().IsEqualTo(originalCount); + (await connection.QueryAsync("select * from Users").ConfigureAwait(false)).Count().IsEqualTo(originalCount+1); + (await connection.DeleteAsync(user).ConfigureAwait(false)).IsEqualTo(true); + (await connection.QueryAsync("select * from Users").ConfigureAwait(false)).Count().IsEqualTo(originalCount); - (await connection.UpdateAsync(notrackedUser)).IsEqualTo(false); //returns false, user not found + (await connection.UpdateAsync(notrackedUser).ConfigureAwait(false)).IsEqualTo(false); //returns false, user not found - (await connection.InsertAsync(new User {Name = "Adam", Age = 10})).IsMoreThan(originalCount + 1); + (await connection.InsertAsync(new User {Name = "Adam", Age = 10}).ConfigureAwait(false)).IsMoreThan(originalCount + 1); } } @@ -131,11 +126,11 @@ public async Task InsertCheckKeyAsync() { using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - (await connection.GetAsync(3)).IsNull(); + (await connection.GetAsync(3).ConfigureAwait(false)).IsNull(); var user = new User { Name = "Adamb", Age = 10 }; - var id = await connection.InsertAsync(user); + var id = await connection.InsertAsync(user).ConfigureAwait(false); user.Id.IsEqualTo(id); } } @@ -145,7 +140,7 @@ public async Task BuilderSelectClauseAsync() { using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); var rand = new Random(8675309); var data = new List(); @@ -153,7 +148,7 @@ public async Task BuilderSelectClauseAsync() { var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() }; data.Add(nU); - nU.Id = await connection.InsertAsync(nU); + nU.Id = await connection.InsertAsync(nU).ConfigureAwait(false); } var builder = new SqlBuilder(); @@ -162,8 +157,8 @@ public async Task BuilderSelectClauseAsync() builder.Select("Id"); - var ids = await connection.QueryAsync(justId.RawSql, justId.Parameters); - var users = await connection.QueryAsync(all.RawSql, all.Parameters); + var ids = await connection.QueryAsync(justId.RawSql, justId.Parameters).ConfigureAwait(false); + var users = await connection.QueryAsync(all.RawSql, all.Parameters).ConfigureAwait(false); foreach (var u in data) { @@ -185,11 +180,11 @@ public async Task BuilderTemplateWithoutCompositionAsync() using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - await connection.InsertAsync(new User { Age = 5, Name = "Testy McTestington" }); + await connection.InsertAsync(new User { Age = 5, Name = "Testy McTestington" }).ConfigureAwait(false); - if ((await connection.QueryAsync(template.RawSql, template.Parameters)).Single() != 1) + if ((await connection.QueryAsync(template.RawSql, template.Parameters).ConfigureAwait(false)).Single() != 1) throw new Exception("Query failed"); } } @@ -197,13 +192,13 @@ public async Task BuilderTemplateWithoutCompositionAsync() [Fact] public async Task InsertArrayAsync() { - await InsertHelperAsync(src => src.ToArray()); + await InsertHelperAsync(src => src.ToArray()).ConfigureAwait(false); } [Fact] public async Task InsertListAsync() { - await InsertHelperAsync(src => src.ToList()); + await InsertHelperAsync(src => src.ToList()).ConfigureAwait(false); } private async Task InsertHelperAsync(Func, T> helper) @@ -217,9 +212,9 @@ private async Task InsertHelperAsync(Func, T> helper) using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - var total = await connection.InsertAsync(helper(users)); + var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); total.IsEqualTo(numberOfEntities); users = connection.Query("select * from Users").ToList(); users.Count.IsEqualTo(numberOfEntities); @@ -229,13 +224,13 @@ private async Task InsertHelperAsync(Func, T> helper) [Fact] public async Task UpdateArrayAsync() { - await UpdateHelperAsync(src => src.ToArray()); + await UpdateHelperAsync(src => src.ToArray()).ConfigureAwait(false); } [Fact] public async Task UpdateListAsync() { - await UpdateHelperAsync(src => src.ToList()); + await UpdateHelperAsync(src => src.ToList()).ConfigureAwait(false); } private async Task UpdateHelperAsync(Func, T> helper) @@ -249,17 +244,17 @@ private async Task UpdateHelperAsync(Func, T> helper) using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - var total = await connection.InsertAsync(helper(users)); + var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); total.IsEqualTo(numberOfEntities); users = connection.Query("select * from Users").ToList(); users.Count.IsEqualTo(numberOfEntities); foreach (var user in users) { - user.Name = user.Name + " updated"; + user.Name += " updated"; } - await connection.UpdateAsync(helper(users)); + await connection.UpdateAsync(helper(users)).ConfigureAwait(false); var name = connection.Query("select * from Users").First().Name; name.Contains("updated").IsTrue(); } @@ -268,13 +263,13 @@ private async Task UpdateHelperAsync(Func, T> helper) [Fact] public async Task DeleteArrayAsync() { - await DeleteHelperAsync(src => src.ToArray()); + await DeleteHelperAsync(src => src.ToArray()).ConfigureAwait(false); } [Fact] public async Task DeleteListAsync() { - await DeleteHelperAsync(src => src.ToList()); + await DeleteHelperAsync(src => src.ToList()).ConfigureAwait(false); } private async Task DeleteHelperAsync(Func, T> helper) @@ -288,15 +283,15 @@ private async Task DeleteHelperAsync(Func, T> helper) using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - var total = await connection.InsertAsync(helper(users)); + var total = await connection.InsertAsync(helper(users)).ConfigureAwait(false); total.IsEqualTo(numberOfEntities); users = connection.Query("select * from Users").ToList(); users.Count.IsEqualTo(numberOfEntities); var usersToDelete = users.Take(10).ToList(); - await connection.DeleteAsync(helper(usersToDelete)); + await connection.DeleteAsync(helper(usersToDelete)).ConfigureAwait(false); users = connection.Query("select * from Users").ToList(); users.Count.IsEqualTo(numberOfEntities - 10); } @@ -313,13 +308,13 @@ public async Task GetAllAsync() using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - var total = await connection.InsertAsync(users); + var total = await connection.InsertAsync(users).ConfigureAwait(false); total.IsEqualTo(numberOfEntities); - users = (List)await connection.GetAllAsync(); + users = (List)await connection.GetAllAsync().ConfigureAwait(false); users.Count.IsEqualTo(numberOfEntities); - var iusers = await connection.GetAllAsync(); + var iusers = await connection.GetAllAsync().ConfigureAwait(false); iusers.ToList().Count.IsEqualTo(numberOfEntities); } } @@ -329,10 +324,10 @@ public async Task InsertFieldWithReservedNameAsync() { using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); - var id = await connection.InsertAsync(new Result { Name = "Adam", Order = 1 }); + await connection.DeleteAllAsync().ConfigureAwait(false); + var id = await connection.InsertAsync(new Result { Name = "Adam", Order = 1 }).ConfigureAwait(false); - var result = await connection.GetAsync(id); + var result = await connection.GetAsync(id).ConfigureAwait(false); result.Order.IsEqualTo(1); } } @@ -342,15 +337,14 @@ public async Task DeleteAllAsync() { using (var connection = GetOpenConnection()) { - await connection.DeleteAllAsync(); + await connection.DeleteAllAsync().ConfigureAwait(false); - var id1 = await connection.InsertAsync(new User { Name = "Alice", Age = 32 }); - var id2 = await connection.InsertAsync(new User { Name = "Bob", Age = 33 }); - await connection.DeleteAllAsync(); - (await connection.GetAsync(id1)).IsNull(); - (await connection.GetAsync(id2)).IsNull(); + var id1 = await connection.InsertAsync(new User { Name = "Alice", Age = 32 }).ConfigureAwait(false); + var id2 = await connection.InsertAsync(new User { Name = "Bob", Age = 33 }).ConfigureAwait(false); + await connection.DeleteAllAsync().ConfigureAwait(false); + (await connection.GetAsync(id1).ConfigureAwait(false)).IsNull(); + (await connection.GetAsync(id2).ConfigureAwait(false)).IsNull(); } } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Dapper.Tests.Contrib/TestSuite.cs b/Dapper.Tests.Contrib/TestSuite.cs index 02e17918..8cfe2298 100644 --- a/Dapper.Tests.Contrib/TestSuite.cs +++ b/Dapper.Tests.Contrib/TestSuite.cs @@ -9,11 +9,7 @@ using System.Data.SqlServerCe; using System.Transactions; #endif -#if XUNIT2 using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute; -#else -using Xunit; -#endif namespace Dapper.Tests.Contrib { @@ -32,7 +28,7 @@ public class ObjectY public int ObjectYId { get; set; } public string Name { get; set; } } - + [Table("ObjectZ")] public class ObjectZ { @@ -171,7 +167,7 @@ public void InsertGetUpdateDeleteWithExplicitKey() o2.IsNull(); } } - + [Fact] public void GetAllWithExplicitKey() { @@ -187,7 +183,7 @@ public void GetAllWithExplicitKey() } } - [Fact] + [Fact] public void InsertGetUpdateDeleteWithExplicitKeyNamedId() { using (var connection = GetOpenConnection()) @@ -208,8 +204,8 @@ public void InsertGetUpdateDeleteWithExplicitKeyNamedId() //o2.IsNull(); } } - - [Fact] + + [Fact] public void ShortIdentity() { using (var connection = GetOpenConnection()) @@ -233,7 +229,6 @@ public void NullDateTime() var stuff = connection.Query("select * from Stuff").ToList(); stuff.First().Created.IsNull(); stuff.Last().Created.IsNotNull(); - } } @@ -342,7 +337,7 @@ private void UpdateHelper(Func, T> helper) users.Count.IsEqualTo(numberOfEntities); foreach (var user in users) { - user.Name = user.Name + " updated"; + user.Name += " updated"; } connection.Update(helper(users)); var name = connection.Query("select * from Users").First().Name; @@ -509,7 +504,6 @@ public void GetAll() for (var i = 0; i < numberOfEntities; i++) iusers[i].Age.IsEqualTo(i); } - } [Fact] @@ -622,7 +616,6 @@ public void InsertFieldWithReservedName() var result = connection.Get(id); result.Order.IsEqualTo(1); } - } [Fact] diff --git a/Dapper.Tests.Contrib/TestSuites.cs b/Dapper.Tests.Contrib/TestSuites.cs index 320d20eb..afee435b 100644 --- a/Dapper.Tests.Contrib/TestSuites.cs +++ b/Dapper.Tests.Contrib/TestSuites.cs @@ -19,14 +19,14 @@ namespace Dapper.Tests.Contrib // the entire set of tests without declarations per method // If we want to support a new provider, they need only be added here - not in multiple places -#if XUNIT2 [XunitTestCaseDiscoverer("Dapper.Tests.SkippableFactDiscoverer", "Dapper.Tests.Contrib")] - public class SkippableFactAttribute : FactAttribute { } -#endif + public class SkippableFactAttribute : FactAttribute + { + } public class SqlServerTestSuite : TestSuite { - const string DbName = "tempdb"; + private const string DbName = "tempdb"; public static string ConnectionString => IsAppVeyor ? @"Server=(local)\SQL2014;Database=tempdb;User ID=sa;Password=Password12!" @@ -115,10 +115,8 @@ static MySqlServerTestSuite() } } } -#endif -#if !COREFX && !DNX451 - // This doesn't work on DNX right now due to: + // This doesn't work on COREFX right now due to: // In Visual Studio: Interop loads (works from console, though) // In general: parameter names, see https://github.com/StackExchange/dapper-dot-net/issues/375 public class SQLiteTestSuite : TestSuite @@ -148,9 +146,7 @@ static SQLiteTestSuite() } } } -#endif -#if !COREFX public class SqlCETestSuite : TestSuite { const string FileName = "Test.DB.sdf"; diff --git a/Dapper.Tests.Contrib/project.json b/Dapper.Tests.Contrib/project.json deleted file mode 100644 index a3e4d046..00000000 --- a/Dapper.Tests.Contrib/project.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "packOptions": { - "summary": "Dapper Contrib Test Suite", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "johandanforth", "marc.gravell", "nick.craver" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Sam Saffron", "Johan Danforth" ], - "description": "Dapper Contrib Test Suite", - "title": "Dapper.Tests.Contrib", - "version": "1.0.0", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - "Dapper": { - "target": "project" - }, - "Dapper.Contrib": { - "target": "project" - }, - "Dapper.SqlBuilder": { - "target": "project" - }, - "xunit": "2.2.0-beta2-build3300", - "dotnet-test-xunit": "2.2.0-preview2-build1029" - }, - "buildOptions": { - "warningsAsErrors": true, - "emitEntryPoint": true, - "debugType": "portable", - "compile": { - "include": [ - "**/*.cs" - ], - "includeFiles": [ - "../Dapper.Tests/Assert.cs", - "../Dapper.Tests/XunitSkippable.cs", - "../Dapper/TypeExtensions.cs" - ] - } - }, - "testRunner": "xunit", - "frameworks": { - //"net40": { - // "frameworkAssemblies": { - // "System.Configuration": "4.0.0.0", - // "System.Data": "4.0.0.0", - // "System.Data.Linq": "4.0.0.0", - // "System.Transactions": "4.0.0.0", - // "System.Xml": "4.0.0.0" - // }, - // "dependencies": { - // "Microsoft.SqlServer.Compact": "4.0.8876.1", - // "MySql.Data": "6.9.8", - // "System.Data.SQLite.Core": "1.0.98.1", - // "xunit": "1.9.2" - // } - //}, - //"net45": { - // "compilationOptions": { - // "define": [ "ASYNC", "XUNIT2" ] - // }, - // "frameworkAssemblies": { - // "System.Configuration": "4.0.0.0", - // "System.Data": "4.0.0.0", - // "System.Data.Linq": "4.0.0.0", - // "System.Runtime": "4.0.0.0", - // "System.Transactions": "4.0.0.0", - // "System.Xml": "4.0.0.0" - // }, - // "dependencies": { - // "Microsoft.SqlServer.Compact": "4.0.8876.1", - // "MySql.Data": "6.9.8", - // "System.Data.SQLite.Core": "1.0.98.1", - // "xunit": "2.1.0" - // } - //}, - //"net451": { - // "buildOptions": { - // "define": [ "ASYNC", "XUNIT2" ] - // }, - // "frameworkAssemblies": { - // "System.Configuration": "4.0.0.0", - // "System.Data.Linq": "4.0.0.0", - // "System.Transactions": "4.0.0.0" - // }, - // "dependencies": { - // "Microsoft.SqlServer.Compact": "4.0.8876.1", - // "MySql.Data": "6.9.8", - // "System.Data.SQLite.Core": "1.0.98.1", - // "xunit": "2.1.0", - // "dotnet-test-xunit": "1.0.0-rc3-*" - // } - //}, - "netcoreapp1.0": { - "imports": [ - "portable-net451+win8", - "dnxcore50" - ], - "buildOptions": { - "define": [ "COREFX", "ASYNC", "XUNIT2" ] - }, - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.0", - "type": "platform" - }, - "Microsoft.Data.Sqlite": "1.0.0" - } - } - } -} \ No newline at end of file diff --git a/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj b/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj new file mode 100644 index 00000000..c1da1927 --- /dev/null +++ b/Dapper.Tests.Performance/Dapper.Tests.Performance.csproj @@ -0,0 +1,41 @@ + + + Dapper.Tests.Performance + Dapper.Tests.Performance + Dapper Core Performance Suite + Exe + false + net462 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dapper.Tests/EntityFramework/EFContext.cs b/Dapper.Tests.Performance/EntityFramework/EFContext.cs similarity index 56% rename from Dapper.Tests/EntityFramework/EFContext.cs rename to Dapper.Tests.Performance/EntityFramework/EFContext.cs index 31791e09..0316dd56 100644 --- a/Dapper.Tests/EntityFramework/EFContext.cs +++ b/Dapper.Tests.Performance/EntityFramework/EFContext.cs @@ -1,13 +1,14 @@ -#if ENTITY_FRAMEWORK -using System.Data.Common; +using System.Data.Common; using System.Data.Entity; -namespace Dapper.Tests.EntityFramework +namespace Dapper.Tests.Performance.EntityFramework { public class EFContext : DbContext { - public EFContext(DbConnection connection, bool owned = false) : base(connection, owned) { } + public EFContext(DbConnection connection, bool owned = false) : base(connection, owned) + { + } + public DbSet Posts { get;set; } } } -#endif diff --git a/Dapper.Tests/Linq2Sql/DataClasses.dbml b/Dapper.Tests.Performance/Linq2Sql/DataClasses.dbml similarity index 85% rename from Dapper.Tests/Linq2Sql/DataClasses.dbml rename to Dapper.Tests.Performance/Linq2Sql/DataClasses.dbml index a6ae1ce2..01e3f6fe 100644 --- a/Dapper.Tests/Linq2Sql/DataClasses.dbml +++ b/Dapper.Tests.Performance/Linq2Sql/DataClasses.dbml @@ -1,5 +1,4 @@  - diff --git a/Dapper.Tests/Linq2Sql/DataClasses.dbml.layout b/Dapper.Tests.Performance/Linq2Sql/DataClasses.dbml.layout similarity index 100% rename from Dapper.Tests/Linq2Sql/DataClasses.dbml.layout rename to Dapper.Tests.Performance/Linq2Sql/DataClasses.dbml.layout diff --git a/Dapper.Tests/Linq2Sql/DataClasses.designer.cs b/Dapper.Tests.Performance/Linq2Sql/DataClasses.designer.cs similarity index 94% rename from Dapper.Tests/Linq2Sql/DataClasses.designer.cs rename to Dapper.Tests.Performance/Linq2Sql/DataClasses.designer.cs index 6c488b06..0f1ca005 100644 --- a/Dapper.Tests/Linq2Sql/DataClasses.designer.cs +++ b/Dapper.Tests.Performance/Linq2Sql/DataClasses.designer.cs @@ -10,7 +10,7 @@ // //------------------------------------------------------------------------------ -namespace Dapper.Tests.Linq2Sql +namespace Dapper.Tests.Performance.Linq2Sql { using System.Data.Linq; using System.Data.Linq.Mapping; @@ -36,12 +36,6 @@ public partial class DataClassesDataContext : System.Data.Linq.DataContext partial void DeletePost(Post instance); #endregion - public DataClassesDataContext() : - base(global::Dapper.Tests.Properties.Settings.Default.tempdbConnectionString, mappingSource) - { - OnCreated(); - } - public DataClassesDataContext(string connection) : base(connection, mappingSource) { diff --git a/Dapper.Tests/Massive/Massive.cs b/Dapper.Tests.Performance/Massive/Massive.cs similarity index 100% rename from Dapper.Tests/Massive/Massive.cs rename to Dapper.Tests.Performance/Massive/Massive.cs diff --git a/Dapper.Tests/NHibernate/NHibernateHelper.cs b/Dapper.Tests.Performance/NHibernate/NHibernateHelper.cs similarity index 67% rename from Dapper.Tests/NHibernate/NHibernateHelper.cs rename to Dapper.Tests.Performance/NHibernate/NHibernateHelper.cs index 2896ede4..9c349211 100644 --- a/Dapper.Tests/NHibernate/NHibernateHelper.cs +++ b/Dapper.Tests.Performance/NHibernate/NHibernateHelper.cs @@ -1,10 +1,9 @@ -#if NHIBERNATE -using NHibernate; +using NHibernate; using NHibernate.Cfg; -namespace Dapper.Tests.NHibernate +namespace Dapper.Tests.Performance.NHibernate { - public class NHibernateHelper + public static class NHibernateHelper { private static ISessionFactory _sessionFactory; @@ -15,9 +14,9 @@ private static ISessionFactory SessionFactory if (_sessionFactory == null) { var configuration = new Configuration(); - configuration.Configure(@"..\Dapper.Tests\NHibernate\hibernate.cfg.xml"); + configuration.Configure(@".\NHibernate\hibernate.cfg.xml"); configuration.AddAssembly(typeof(Post).Assembly); - configuration.AddXmlFile(@"..\Dapper.Tests\NHibernate\Post.hbm.xml"); + configuration.AddXmlFile(@".\NHibernate\Post.hbm.xml"); _sessionFactory = configuration.BuildSessionFactory(); } @@ -30,5 +29,4 @@ public static IStatelessSession OpenSession() return SessionFactory.OpenStatelessSession(); } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Dapper.Tests/NHibernate/Post.hbm.xml b/Dapper.Tests.Performance/NHibernate/Post.hbm.xml similarity index 87% rename from Dapper.Tests/NHibernate/Post.hbm.xml rename to Dapper.Tests.Performance/NHibernate/Post.hbm.xml index 963a1df9..58591255 100644 --- a/Dapper.Tests/NHibernate/Post.hbm.xml +++ b/Dapper.Tests.Performance/NHibernate/Post.hbm.xml @@ -1,5 +1,5 @@  - + diff --git a/Dapper.Tests/NHibernate/hibernate.cfg.xml b/Dapper.Tests.Performance/NHibernate/hibernate.cfg.xml similarity index 69% rename from Dapper.Tests/NHibernate/hibernate.cfg.xml rename to Dapper.Tests.Performance/NHibernate/hibernate.cfg.xml index 8ec1f107..49212ff6 100644 --- a/Dapper.Tests/NHibernate/hibernate.cfg.xml +++ b/Dapper.Tests.Performance/NHibernate/hibernate.cfg.xml @@ -4,8 +4,7 @@ NHibernate.Connection.DriverConnectionProvider NHibernate.Dialect.MsSql2005Dialect NHibernate.Driver.SqlClientDriver - Smackdown.Properties.Settings.tempdbConnectionString - NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu + Main false diff --git a/Dapper.Tests/PerformanceTests.cs b/Dapper.Tests.Performance/PerformanceTests.cs similarity index 60% rename from Dapper.Tests/PerformanceTests.cs rename to Dapper.Tests.Performance/PerformanceTests.cs index 3c1bb76c..6d8fb7a0 100644 --- a/Dapper.Tests/PerformanceTests.cs +++ b/Dapper.Tests.Performance/PerformanceTests.cs @@ -1,74 +1,67 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Linq; using System.Data.SqlClient; using System.Diagnostics; using System.Linq; -using Dapper.Tests.Linq2Sql; +//using BLToolkit.Data; // Note: this doesn't load in the new .csproj system...likely a bug +using Dapper.Tests.Performance.EntityFramework; +using Dapper.Tests.Performance.Linq2Sql; +using Dapper.Tests.Performance.NHibernate; using Dapper.Contrib.Extensions; - - -#if SOMA -using Soma.Core; -#endif -#if NHIBERNATE +using Massive; using NHibernate.Criterion; using NHibernate.Linq; -using Dapper.Tests.NHibernate; -#endif -#if LINQ2SQL -using System.Data.Linq; -#endif -#if MASSIVE -using Massive; -#endif -#if ORMLITE using ServiceStack.OrmLite; -using ServiceStack.OrmLite.SqlServer; -using ServiceStack.OrmLite.Converters; using ServiceStack.OrmLite.Dapper; -#endif -#if BLTOOLKIT -using BLToolkit.Data; -#endif -#if ENTITY_FRAMEWORK -using Dapper.Tests.EntityFramework; -#endif -#if SUSANOO using Susanoo; -#endif - +using System.Configuration; +using System.Threading.Tasks; -namespace Dapper.Tests +namespace Dapper.Tests.Performance { - public class PerformanceTests + public partial class PerformanceTests { - class Test + private class Test { - public static Test Create(Action iteration, string name) + public Test(Action iteration, string name) { - return new Test {Iteration = iteration, Name = name }; + Iteration = iteration; + Name = name; + } + + public Test(Func iterationAsync, string name) + { + IterationAsync = iterationAsync; + Name = name; } public Action Iteration { get; set; } + public Func IterationAsync { get; set; } public string Name { get; set; } public Stopwatch Watch { get; set; } } - class Tests : List + private class Tests : List { public void Add(Action iteration, string name) { - Add(Test.Create(iteration, name)); + Add(new Test(iteration, name)); + } + public void AddAsync(Func iterationAsync, string name) + { + Add(new Test(iterationAsync, name)); } - public void Run(int iterations) - { + public async Task RunAsync(int iterations) + { // warmup foreach (var test in this) { - test.Iteration(iterations + 1); + test.Iteration?.Invoke(iterations + 1); + if (test.IterationAsync != null) await test.IterationAsync(iterations + 1).ConfigureAwait(false); test.Watch = new Stopwatch(); test.Watch.Reset(); } @@ -79,7 +72,8 @@ public void Run(int iterations) foreach (var test in this.OrderBy(ignore => rand.Next())) { test.Watch.Start(); - test.Iteration(i); + test.Iteration?.Invoke(i); + if (test.IterationAsync != null) await test.IterationAsync(i).ConfigureAwait(false); test.Watch.Stop(); } } @@ -90,181 +84,153 @@ public void Run(int iterations) } } } -#if LINQ2SQL - static DataClassesDataContext GetL2SContext(SqlConnection connection) + + public static string ConnectionString { get; } = ConfigurationManager.ConnectionStrings["Main"].ConnectionString; + + public static SqlConnection GetOpenConnection() { - return new DataClassesDataContext(connection); + var connection = new SqlConnection(ConnectionString); + connection.Open(); + return connection; } -#endif - -#if SOMA - internal class SomaConfig : Soma.Core.MsSqlConfig - { - public override string ConnectionString => TestSuite.ConnectionString; + private static DataClassesDataContext GetL2SContext(SqlConnection connection) => + new DataClassesDataContext(connection); - public override Action Logger - { - get { return noOp; } - } - static readonly Action noOp = x => { }; - } -#endif - static void Try(Action action, string blame) + private static void Try(Action action, string blame) { try { action(); - } catch(Exception ex) + } + catch (Exception ex) { Console.Error.WriteLine($"{blame}: {ex.Message}"); } } - public void Run(int iterations) + + public async Task RunAsync(int iterations) { - using (var connection = TestSuite.GetOpenConnection()) + using (var connection = GetOpenConnection()) { var tests = new Tests(); -#if LINQ2SQL + + // Linq2SQL Try(() => { var l2scontext1 = GetL2SContext(connection); - tests.Add(id => l2scontext1.Posts.First(p => p.Id == id), "Linq 2 SQL"); + tests.Add(id => l2scontext1.Posts.First(p => p.Id == id), "Linq2Sql: Normal"); var l2scontext2 = GetL2SContext(connection); var compiledGetPost = CompiledQuery.Compile((Linq2Sql.DataClassesDataContext ctx, int id) => ctx.Posts.First(p => p.Id == id)); - tests.Add(id => compiledGetPost(l2scontext2, id), "Linq 2 SQL Compiled"); + tests.Add(id => compiledGetPost(l2scontext2, id), "Linq2Sql: Compiled"); var l2scontext3 = GetL2SContext(connection); - tests.Add(id => l2scontext3.ExecuteQuery("select * from Posts where Id = {0}", id).First(), "Linq 2 SQL ExecuteQuery"); + tests.Add(id => l2scontext3.ExecuteQuery("select * from Posts where Id = {0}", id).First(), "Linq2Sql: ExecuteQuery"); }, "LINQ-to-SQL"); -#endif -#if ENTITY_FRAMEWORK + // Entity Framework Try(() => { var entityContext = new EFContext(connection); - tests.Add(id => entityContext.Posts.First(p => p.Id == id), "Entity framework"); - + tests.Add(id => entityContext.Posts.First(p => p.Id == id), "Entity Framework"); var entityContext2 = new EFContext(connection); - tests.Add(id => entityContext2.Database.SqlQuery("select * from Posts where Id = {0}", id).First(), "Entity framework SqlQuery"); + tests.Add(id => entityContext2.Database.SqlQuery("select * from Posts where Id = {0}", id).First(), "Entity Framework: SqlQuery"); //var entityContext3 = new EFContext(connection); - //tests.Add(id => entityFrameworkCompiled(entityContext3, id), "Entity framework CompiledQuery"); + //tests.Add(id => entityFrameworkCompiled(entityContext3, id), "Entity Framework CompiledQuery"); //var entityContext4 = new EFContext(connection); - //tests.Add(id => entityContext4.Posts.Where("it.Id = @id", new System.Data.Objects.ObjectParameter("id", id)).First(), "Entity framework ESQL"); + //tests.Add(id => entityContext4.Posts.Where("it.Id = @id", new System.Data.Objects.ObjectParameter("id", id)).First(), "Entity Framework ESQL"); var entityContext5 = new EFContext(connection); - tests.Add(id => entityContext5.Posts.AsNoTracking().First(p => p.Id == id), "Entity framework No Tracking"); + tests.Add(id => entityContext5.Posts.AsNoTracking().First(p => p.Id == id), "Entity Framework: No Tracking"); }, "Entity Framework"); -#endif + + // Dapper Try(() => { - var mapperConnection = TestSuite.GetOpenConnection(); - tests.Add(id => mapperConnection.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: true).First(), "Mapper Query (buffered)"); - tests.Add(id => mapperConnection.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: false).First(), "Mapper Query (non-buffered)"); - tests.Add(id => mapperConnection.QueryFirstOrDefault("select * from Posts where Id = @Id", new { Id = id }), "Mapper QueryFirstOrDefault"); + var mapperConnection = GetOpenConnection(); + tests.Add(id => mapperConnection.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: true).First(), "Dapper: Query (buffered)"); + tests.Add(id => mapperConnection.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: false).First(), "Dapper: Query (non-buffered)"); + tests.Add(id => mapperConnection.QueryFirstOrDefault("select * from Posts where Id = @Id", new { Id = id }), "Dapper: QueryFirstOrDefault"); - var mapperConnection2 = TestSuite.GetOpenConnection(); - tests.Add(id => mapperConnection2.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: true).First(), "Dynamic Mapper Query (buffered)"); - tests.Add(id => mapperConnection2.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: false).First(), "Dynamic Mapper Query (non-buffered)"); - tests.Add(id => mapperConnection2.QueryFirstOrDefault("select * from Posts where Id = @Id", new { Id = id }), "Dynamic Mapper QueryQueryFirstOrDefault"); + var mapperConnection2 = GetOpenConnection(); + tests.Add(id => mapperConnection2.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: true).First(), "Dapper: Dynamic Query (buffered)"); + tests.Add(id => mapperConnection2.Query("select * from Posts where Id = @Id", new { Id = id }, buffered: false).First(), "Dapper: Dynamic Query (non-buffered)"); + tests.Add(id => mapperConnection2.QueryFirstOrDefault("select * from Posts where Id = @Id", new { Id = id }), "Dapper: Dynamic QueryFirstOrDefault"); // dapper.contrib - var mapperConnection3 = TestSuite.GetOpenConnection(); + var mapperConnection3 = GetOpenConnection(); tests.Add(id => mapperConnection3.Get(id), "Dapper.Contrib"); }, "Dapper"); -#if MASSIVE + // Massive Try(() => { - // massive - var massiveModel = new DynamicModel(TestSuite.ConnectionString); - var massiveConnection = TestSuite.GetOpenConnection(); - tests.Add(id => massiveModel.Query("select * from Posts where Id = @0", massiveConnection, id).First(), "Dynamic Massive ORM Query"); + var massiveModel = new DynamicModel(ConnectionString); + var massiveConnection = GetOpenConnection(); + tests.Add(id => massiveModel.Query("select * from Posts where Id = @0", massiveConnection, id).First(), "Massive: Dynamic ORM Query"); }, "Massive"); -#endif -#if PETAPOCO + // PetaPoco Try(() => { // PetaPoco test with all default options - var petapoco = new PetaPoco.Database(TestSuite.ConnectionString, "System.Data.SqlClient"); + var petapoco = new PetaPoco.Database(ConnectionString, "System.Data.SqlClient"); petapoco.OpenSharedConnection(); - tests.Add(id => petapoco.Fetch("SELECT * from Posts where Id=@0", id).First(), "PetaPoco (Normal)"); + tests.Add(id => petapoco.Fetch("SELECT * from Posts where Id=@0", id).First(), "PetaPoco: Normal"); // PetaPoco with some "smart" functionality disabled - var petapocoFast = new PetaPoco.Database(TestSuite.ConnectionString, "System.Data.SqlClient"); + var petapocoFast = new PetaPoco.Database(ConnectionString, "System.Data.SqlClient"); petapocoFast.OpenSharedConnection(); petapocoFast.EnableAutoSelect = false; petapocoFast.EnableNamedParams = false; petapocoFast.ForceDateTimesToUtc = false; - tests.Add(id => petapocoFast.Fetch("SELECT * from Posts where Id=@0", id).First(), "PetaPoco (Fast)"); + tests.Add(id => petapocoFast.Fetch("SELECT * from Posts where Id=@0", id).First(), "PetaPoco: Fast"); }, "PetaPoco"); -#endif - -#if SUBSONIC - Try(() => - { - // Subsonic ActiveRecord - tests.Add(id => SubSonic.Post.SingleOrDefault(x => x.Id == id), "SubSonic ActiveRecord.SingleOrDefault"); - // Subsonic coding horror - SubSonic.tempdbDB db = new SubSonic.tempdbDB(); - tests.Add(id => new SubSonic.Query.CodingHorror(db.Provider, "select * from Posts where Id = @0", id).ExecuteTypedList(), "SubSonic Coding Horror"); - }, "Subsonic"); -#endif // NHibernate - -#if NHIBERNATE - Try(() => { + Try(() => + { var nhSession1 = NHibernateHelper.OpenSession(); tests.Add(id => nhSession1.CreateSQLQuery(@"select * from Posts where Id = :id") .SetInt32("id", id) - .List(), "NHibernate SQL"); + .List(), "NHibernate: SQL"); var nhSession2 = NHibernateHelper.OpenSession(); tests.Add(id => nhSession2.CreateQuery(@"from Post as p where p.Id = :id") .SetInt32("id", id) - .List(), "NHibernate HQL"); + .List(), "NHibernate: HQL"); var nhSession3 = NHibernateHelper.OpenSession(); tests.Add(id => nhSession3.CreateCriteria() .Add(Restrictions.IdEq(id)) - .List(), "NHibernate Criteria"); + .List(), "NHibernate: Criteria"); var nhSession4 = NHibernateHelper.OpenSession(); tests.Add(id => nhSession4 .Query() - .First(p => p.Id == id), "NHibernate LINQ"); + .First(p => p.Id == id), "NHibernate: LINQ"); var nhSession5 = NHibernateHelper.OpenSession(); - tests.Add(id => nhSession5.Get(id), "NHibernate Session.Get"); + tests.Add(id => nhSession5.Get(id), "NHibernate: Session.Get"); }, "NHibernate"); -#endif -#if BLTOOLKIT - // bltoolkit - var db1 = new DbManager(TestSuite.GetOpenConnection()); - tests.Add(id => db1.SetCommand("select * from Posts where Id = @id", db1.Parameter("id", id)).ExecuteList(), "BLToolkit"); -#endif -#if SIMPLEDATA + // Simple.Data Try(() => { - var sdb = Simple.Data.Database.OpenConnection(TestSuite.ConnectionString); + var sdb = Simple.Data.Database.OpenConnection(ConnectionString); tests.Add(id => sdb.Posts.FindById(id).FirstOrDefault(), "Simple.Data"); }, "Simple.Data"); -#endif - -#if BELGRADE + // Belgrade Try(() => { - var query = new Belgrade.SqlClient.SqlDb.QueryMapper(TestSuite.GetOpenConnection()); - tests.Add(id => query.ExecuteReader("SELECT TOP 1 * FROM Posts WHERE Id = " + id, + var query = new Belgrade.SqlClient.SqlDb.QueryMapper(ConnectionString); + tests.AddAsync(id => query.ExecuteReader("SELECT TOP 1 * FROM Posts WHERE Id = " + id, reader => { var post = new Post(); @@ -285,67 +251,51 @@ public void Run(int iterations) }), "Belgrade Sql Client"); }, "Belgrade Sql Client"); -#endif - -#if SUSANOO //Susanoo - var susanooDb = new DatabaseManager("Smackdown.Properties.Settings.tempdbConnectionString"); - var susanooDb2 = new DatabaseManager("Smackdown.Properties.Settings.tempdbConnectionString"); - + var susanooDb = new DatabaseManager(connection); var susanooPreDefinedCommand = - CommandManager.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) .DefineResults() - .Realize("PostById"); + .Realize(); var susanooDynamicPreDefinedCommand = - CommandManager.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) .DefineResults() - .Realize("DynamicById"); + .Realize(); tests.Add(Id => - CommandManager.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) .DefineResults() - .Realize("PostById") - .Execute(susanooDb, new { Id }).First(), "Susanoo Mapping Cache Retrieval"); + .Realize() + .Execute(susanooDb, new { Id }).First(), "Susanoo: Mapping Cache Retrieval"); tests.Add(Id => - CommandManager.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) + CommandManager.Instance.DefineCommand("SELECT * FROM Posts WHERE Id = @Id", CommandType.Text) .DefineResults() - .Realize("DynamicById") - .Execute(susanooDb, new { Id }).First(), "Susanoo Dynamic Mapping Cache Retrieval"); + .Realize() + .Execute(susanooDb, new { Id }).First(), "Susanoo: Dynamic Mapping Cache Retrieval"); - tests.Add(Id => - susanooDynamicPreDefinedCommand - .Execute(susanooDb, new { Id }).First(), "Susanoo Dynamic Mapping Static"); - - tests.Add(Id => - susanooPreDefinedCommand - .Execute(susanooDb, new { Id }).First(), "Susanoo Mapping Static"); -#endif + tests.Add(Id => susanooDynamicPreDefinedCommand + .Execute(susanooDb, new { Id }).First(), "Susanoo: Dynamic Mapping Static"); -#if SOMA - // Soma + tests.Add(Id => susanooPreDefinedCommand + .Execute(susanooDb, new { Id }).First(), "Susanoo: Mapping Static"); - // DISABLED: assembly fail loading FSharp.PowerPack, Version=2.0.0.0 - // var somadb = new Soma.Core.Db(new SomaConfig()); - // tests.Add(id => somadb.Find(id), "Soma"); -#endif -#if ORMLITE //ServiceStack's OrmLite: - - // DISABLED: can't find QueryById - //OrmLiteConfig.DialectProvider = SqlServerOrmLiteDialectProvider.Instance; //Using SQL Server - //IDbCommand ormLiteCmd = TestSuite.GetOpenConnection().CreateCommand(); - // tests.Add(id => ormLiteCmd.QueryById(id), "OrmLite QueryById"); -#endif - // HAND CODED - + Try(() => + { + var dbFactory = new OrmLiteConnectionFactory(ConnectionString, SqlServerDialect.Provider); + var db = dbFactory.Open(); + tests.Add(id => db.SingleById(id), "ServiceStack.OrmLite: SingleById"); + }, "ServiceStack.OrmLite"); + + // Hand Coded var postCommand = new SqlCommand(); postCommand.Connection = connection; postCommand.CommandText = @"select Id, [Text], [CreationDate], LastChangeDate, Counter1,Counter2,Counter3,Counter4,Counter5,Counter6,Counter7,Counter8,Counter9 from Posts where Id = @Id"; - var idParam = postCommand.Parameters.Add("@Id", System.Data.SqlDbType.Int); + var idParam = postCommand.Parameters.Add("@Id", SqlDbType.Int); tests.Add(id => { @@ -370,10 +320,26 @@ public void Run(int iterations) post.Counter8 = reader.GetNullableValue(11); post.Counter9 = reader.GetNullableValue(12); } - }, "hand coded"); + }, "Hand Coded"); + + // Subsonic isn't maintained anymore - doesn't import correctly + //Try(() => + // { + // // Subsonic ActiveRecord + // tests.Add(id => 3SubSonic.Post.SingleOrDefault(x => x.Id == id), "SubSonic ActiveRecord.SingleOrDefault"); + + // // Subsonic coding horror + // SubSonic.tempdbDB db = new SubSonic.tempdbDB(); + // tests.Add(id => new SubSonic.Query.CodingHorror(db.Provider, "select * from Posts where Id = @0", id).ExecuteTypedList(), "SubSonic Coding Horror"); + //}, "Subsonic"); + + //// BLToolkit - doesn't import correctly in the new .csproj world + //var db1 = new DbManager(GetOpenConnection()); + //tests.Add(id => db1.SetCommand("select * from Posts where Id = @id", db1.Parameter("id", id)).ExecuteList(), "BLToolkit"); + #if !COREFX - DataTable table = new DataTable + var table = new DataTable { Columns = { @@ -406,32 +372,8 @@ public void Run(int iterations) #endif Console.WriteLine(); Console.WriteLine("Running..."); - tests.Run(iterations); - } - } - - } - - static class SqlDataReaderHelper - { - public static string GetNullableString(this SqlDataReader reader, int index) - { - object tmp = reader.GetValue(index); - if (tmp != DBNull.Value) - { - return (string)tmp; - } - return null; - } - - public static Nullable GetNullableValue(this SqlDataReader reader, int index) where T : struct - { - object tmp = reader.GetValue(index); - if (tmp != DBNull.Value) - { - return (T)tmp; + await tests.RunAsync(iterations).ConfigureAwait(false); } - return null; } } } \ No newline at end of file diff --git a/Dapper.Tests/PetaPoco/PetaPoco.cs b/Dapper.Tests.Performance/PetaPoco/PetaPoco.cs similarity index 96% rename from Dapper.Tests/PetaPoco/PetaPoco.cs rename to Dapper.Tests.Performance/PetaPoco/PetaPoco.cs index 1d8a5b63..2b7e8b14 100644 --- a/Dapper.Tests/PetaPoco/PetaPoco.cs +++ b/Dapper.Tests.Performance/PetaPoco/PetaPoco.cs @@ -714,7 +714,7 @@ public static bool SplitSqlForPaging(string sql, out string sqlCount, out string public T Single(string sql, params object[] args) where T : new() { T val = SingleOrDefault(sql, args); - if (val != null) + if (!EqualityComparer.Default.Equals(val, default(T))) return val; else throw new InvalidOperationException("The sequence contains no elements"); @@ -722,7 +722,7 @@ public static bool SplitSqlForPaging(string sql, out string sqlCount, out string public T First(string sql, params object[] args) where T : new() { T val = FirstOrDefault(sql, args); - if (val != null) + if (!EqualityComparer.Default.Equals(val, default(T))) return val; else throw new InvalidOperationException("The sequence contains no elements"); diff --git a/Dapper.Tests.Performance/Post.cs b/Dapper.Tests.Performance/Post.cs new file mode 100644 index 00000000..78c71ccd --- /dev/null +++ b/Dapper.Tests.Performance/Post.cs @@ -0,0 +1,25 @@ +using System; +using Soma.Core; + +namespace Dapper.Tests.Performance +{ + [ServiceStack.DataAnnotations.Alias("Posts")] + [Table(Name = "Posts")] + public class Post + { + [Id(IdKind.Identity)] + public int Id { get; set; } + public string Text { get; set; } + public DateTime CreationDate { get; set; } + public DateTime LastChangeDate { get; set; } + public int? Counter1 { get; set; } + public int? Counter2 { get; set; } + public int? Counter3 { get; set; } + public int? Counter4 { get; set; } + public int? Counter5 { get; set; } + public int? Counter6 { get; set; } + public int? Counter7 { get; set; } + public int? Counter8 { get; set; } + public int? Counter9 { get; set; } + } +} diff --git a/Dapper.Tests/Program.cs b/Dapper.Tests.Performance/Program.cs similarity index 54% rename from Dapper.Tests/Program.cs rename to Dapper.Tests.Performance/Program.cs index de780908..56d96749 100644 --- a/Dapper.Tests/Program.cs +++ b/Dapper.Tests.Performance/Program.cs @@ -1,54 +1,31 @@ using System; +using System.Threading.Tasks; -namespace Dapper.Tests +namespace Dapper.Tests.Performance { -#if ORMLITE - [ServiceStack.DataAnnotations.Alias("Posts")] -#endif -#if SOMA - [Soma.Core.Table(Name = "Posts")] -#endif - public class Post - { -#if SOMA - [Soma.Core.Id(Soma.Core.IdKind.Identity)] -#endif - public int Id { get; set; } - public string Text { get; set; } - public DateTime CreationDate { get; set; } - public DateTime LastChangeDate { get; set; } - public int? Counter1 { get; set; } - public int? Counter2 { get; set; } - public int? Counter3 { get; set; } - public int? Counter4 { get; set; } - public int? Counter5 { get; set; } - public int? Counter6 { get; set; } - public int? Counter7 { get; set; } - public int? Counter8 { get; set; } - public int? Counter9 { get; set; } - } - - class Program + // Note: VSTest injects an entry point in .NET Core land...so we have to split this out into + // a separate project...so here we are. + // See https://github.com/Microsoft/vstest/issues/636 for details + public static class Program { - static void Main() + public static void Main() { #if DEBUG var fg = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Warning: DEBUG configuration; performance may be impacted"); -#if DNX - Console.WriteLine("use: dnx --configuration release perf"); -#endif Console.ForegroundColor = fg; Console.WriteLine(); #endif + Console.WriteLine("Using ConnectionString: " + PerformanceTests.ConnectionString); + EnsureDBSetup(); - RunPerformanceTests(); + RunPerformanceTestsAsync().GetAwaiter().GetResult(); } private static void EnsureDBSetup() { - using (var cnn = TestSuite.GetOpenConnection()) + using (var cnn = PerformanceTests.GetOpenConnection()) { var cmd = cnn.CreateCommand(); cmd.CommandText = @" @@ -95,12 +72,12 @@ insert Posts ([Text],CreationDate, LastChangeDate) values (replicate('x', 2000), } } - static void RunPerformanceTests() + private static async Task RunPerformanceTestsAsync() { var test = new PerformanceTests(); const int iterations = 500; Console.WriteLine("Running {0} iterations that load up a post entity", iterations); - test.Run(iterations); + await test.RunAsync(iterations).ConfigureAwait(false); } } } diff --git a/Dapper.Tests.Performance/Soma/SomaConfig.cs b/Dapper.Tests.Performance/Soma/SomaConfig.cs new file mode 100644 index 00000000..0716e98d --- /dev/null +++ b/Dapper.Tests.Performance/Soma/SomaConfig.cs @@ -0,0 +1,14 @@ +using Soma.Core; +using System; + +namespace Dapper.Tests.Performance.Soma +{ + internal class SomaConfig : MsSqlConfig + { + public override string ConnectionString => PerformanceTests.ConnectionString; + + public override Action Logger => noOp; + + private static readonly Action noOp = x => { }; + } +} diff --git a/Dapper.Tests.Performance/SqlDataReaderHelper.cs b/Dapper.Tests.Performance/SqlDataReaderHelper.cs new file mode 100644 index 00000000..a7137582 --- /dev/null +++ b/Dapper.Tests.Performance/SqlDataReaderHelper.cs @@ -0,0 +1,28 @@ +using System; +using System.Data.SqlClient; + +namespace Dapper.Tests.Performance +{ + public static class SqlDataReaderHelper + { + public static string GetNullableString(this SqlDataReader reader, int index) + { + object tmp = reader.GetValue(index); + if (tmp != DBNull.Value) + { + return (string)tmp; + } + return null; + } + + public static T? GetNullableValue(this SqlDataReader reader, int index) where T : struct + { + object tmp = reader.GetValue(index); + if (tmp != DBNull.Value) + { + return (T)tmp; + } + return null; + } + } +} diff --git a/Dapper.Tests.Performance/app.config b/Dapper.Tests.Performance/app.config new file mode 100644 index 00000000..5ea174a5 --- /dev/null +++ b/Dapper.Tests.Performance/app.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dapper.Tests/Tests.Async.cs b/Dapper.Tests/AsyncTests.cs similarity index 83% rename from Dapper.Tests/Tests.Async.cs rename to Dapper.Tests/AsyncTests.cs index 4b0fd364..f67d2424 100644 --- a/Dapper.Tests/Tests.Async.cs +++ b/Dapper.Tests/AsyncTests.cs @@ -1,5 +1,4 @@ -#if ASYNC -using System.Linq; +using System.Linq; using System.Data; using System.Diagnostics; using System; @@ -10,12 +9,15 @@ namespace Dapper.Tests { - public partial class TestSuite + public class Tests : TestBase { + private SqlConnection _marsConnection; + private SqlConnection marsConnection => _marsConnection ?? (_marsConnection = GetOpenConnection(true)); + [Fact] public async Task TestBasicStringUsageAsync() { - var query = await connection.QueryAsync("select 'abc' as [Value] union all select @txt", new { txt = "def" }); + var query = await connection.QueryAsync("select 'abc' as [Value] union all select @txt", new { txt = "def" }).ConfigureAwait(false); var arr = query.ToArray(); arr.IsSequenceEqualTo(new[] { "abc", "def" }); } @@ -23,35 +25,35 @@ public async Task TestBasicStringUsageAsync() [Fact] public async Task TestBasicStringUsageQueryFirstAsync() { - var str = await connection.QueryFirstAsync(new CommandDefinition("select 'abc' as [Value] union all select @txt", new {txt = "def"})); + var str = await connection.QueryFirstAsync(new CommandDefinition("select 'abc' as [Value] union all select @txt", new {txt = "def"})).ConfigureAwait(false); str.IsEqualTo("abc"); } [Fact] public async Task TestBasicStringUsageQueryFirstOrDefaultAsync() { - var str = await connection.QueryFirstOrDefaultAsync(new CommandDefinition("select null as [Value] union all select @txt", new {txt = "def"})); + var str = await connection.QueryFirstOrDefaultAsync(new CommandDefinition("select null as [Value] union all select @txt", new {txt = "def"})).ConfigureAwait(false); str.IsNull(); } [Fact] public async Task TestBasicStringUsageQuerySingleAsync() { - var str = await connection.QuerySingleAsync(new CommandDefinition("select 'abc' as [Value]")); + var str = await connection.QuerySingleAsync(new CommandDefinition("select 'abc' as [Value]")).ConfigureAwait(false); str.IsEqualTo("abc"); } [Fact] public async Task TestBasicStringUsageQuerySingleOrDefaultAsync() { - var str = await connection.QuerySingleAsync(new CommandDefinition("select null as [Value]")); + var str = await connection.QuerySingleAsync(new CommandDefinition("select null as [Value]")).ConfigureAwait(false); str.IsNull(); } [Fact] public async Task TestBasicStringUsageAsyncNonBuffered() { - var query = await connection.QueryAsync(new CommandDefinition("select 'abc' as [Value] union all select @txt", new { txt = "def" }, flags: CommandFlags.None)); + var query = await connection.QueryAsync(new CommandDefinition("select 'abc' as [Value] union all select @txt", new { txt = "def" }, flags: CommandFlags.None)).ConfigureAwait(false); var arr = query.ToArray(); arr.IsSequenceEqualTo(new[] { "abc", "def" }); } @@ -77,7 +79,7 @@ public void TestLongOperationWithCancellation() [Fact] public async Task TestBasicStringUsageClosedAsync() { - var query = await connection.QueryAsync("select 'abc' as [Value] union all select @txt", new { txt = "def" }); + var query = await connection.QueryAsync("select 'abc' as [Value] union all select @txt", new { txt = "def" }).ConfigureAwait(false); var arr = query.ToArray(); arr.IsSequenceEqualTo(new[] { "abc", "def" }); } @@ -85,7 +87,7 @@ public async Task TestBasicStringUsageClosedAsync() [Fact] public async Task TestQueryDynamicAsync() { - var row = (await connection.QueryAsync("select 'abc' as [Value]")).Single(); + var row = (await connection.QueryAsync("select 'abc' as [Value]").ConfigureAwait(false)).Single(); string value = row.Value; value.IsEqualTo("abc"); } @@ -93,7 +95,7 @@ public async Task TestQueryDynamicAsync() [Fact] public async Task TestClassWithStringUsageAsync() { - var query = await connection.QueryAsync("select 'abc' as [Value] union all select @txt", new { txt = "def" }); + var query = await connection.QueryAsync("select 'abc' as [Value] union all select @txt", new { txt = "def" }).ConfigureAwait(false); var arr = query.ToArray(); arr.Select(x => x.Value).IsSequenceEqualTo(new[] { "abc", "def" }); } @@ -101,7 +103,7 @@ public async Task TestClassWithStringUsageAsync() [Fact] public async Task TestExecuteAsync() { - var val = await connection.ExecuteAsync("declare @foo table(id int not null); insert @foo values(@id);", new { id = 1 }); + var val = await connection.ExecuteAsync("declare @foo table(id int not null); insert @foo values(@id);", new { id = 1 }).ConfigureAwait(false); val.Equals(1); } @@ -121,7 +123,7 @@ public async Task TestMultiMapWithSplitAsync() { prod.Category = cat; return prod; - }); + }).ConfigureAwait(false); var product = productQuery.First(); // assertions @@ -139,7 +141,7 @@ public async Task TestMultiMapArbitraryWithSplitAsync() var prod = (Product)objects[0]; prod.Category = (Category)objects[1]; return prod; - }); + }).ConfigureAwait(false); var product = productQuery.First(); // assertions @@ -152,14 +154,14 @@ public async Task TestMultiMapArbitraryWithSplitAsync() [Fact] public async Task TestMultiMapWithSplitClosedConnAsync() { - var sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name"; + const string sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name"; using (var conn = GetClosedConnection()) { var productQuery = await conn.QueryAsync(sql, (prod, cat) => { prod.Category = cat; return prod; - }); + }).ConfigureAwait(false); var product = productQuery.First(); // assertions @@ -173,7 +175,7 @@ public async Task TestMultiMapWithSplitClosedConnAsync() [Fact] public async Task TestMultiAsync() { - using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2")) + using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2").ConfigureAwait(false)) { multi.ReadAsync().Result.Single().IsEqualTo(1); multi.ReadAsync().Result.Single().IsEqualTo(2); @@ -183,7 +185,7 @@ public async Task TestMultiAsync() [Fact] public async Task TestMultiAsyncViaFirstOrDefault() { - using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2; select 3; select 4; select 5")) + using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2; select 3; select 4; select 5").ConfigureAwait(false)) { multi.ReadFirstOrDefaultAsync().Result.IsEqualTo(1); multi.ReadAsync().Result.Single().IsEqualTo(2); @@ -196,7 +198,7 @@ public async Task TestMultiAsyncViaFirstOrDefault() [Fact] public async Task TestMultiClosedConnAsync() { - using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2")) + using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2").ConfigureAwait(false)) { multi.ReadAsync().Result.Single().IsEqualTo(1); multi.ReadAsync().Result.Single().IsEqualTo(2); @@ -206,7 +208,7 @@ public async Task TestMultiClosedConnAsync() [Fact] public async Task TestMultiClosedConnAsyncViaFirstOrDefault() { - using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2; select 3; select 4; select 5;")) + using (SqlMapper.GridReader multi = await connection.QueryMultipleAsync("select 1; select 2; select 3; select 4; select 5;").ConfigureAwait(false)) { multi.ReadFirstOrDefaultAsync().Result.IsEqualTo(1); multi.ReadAsync().Result.Single().IsEqualTo(2); @@ -221,7 +223,7 @@ public async Task TestMultiClosedConnAsyncViaFirstOrDefault() public async Task ExecuteReaderOpenAsync() { var dt = new DataTable(); - dt.Load(await connection.ExecuteReaderAsync("select 3 as [three], 4 as [four]")); + dt.Load(await connection.ExecuteReaderAsync("select 3 as [three], 4 as [four]").ConfigureAwait(false)); dt.Columns.Count.IsEqualTo(2); dt.Columns[0].ColumnName.IsEqualTo("three"); dt.Columns[1].ColumnName.IsEqualTo("four"); @@ -229,14 +231,14 @@ public async Task ExecuteReaderOpenAsync() ((int)dt.Rows[0][0]).IsEqualTo(3); ((int)dt.Rows[0][1]).IsEqualTo(4); } - + [Fact] public async Task ExecuteReaderClosedAsync() { using (var conn = GetClosedConnection()) { var dt = new DataTable(); - dt.Load(await conn.ExecuteReaderAsync("select 3 as [three], 4 as [four]")); + dt.Load(await conn.ExecuteReaderAsync("select 3 as [three], 4 as [four]").ConfigureAwait(false)); dt.Columns.Count.IsEqualTo(2); dt.Columns[0].ColumnName.IsEqualTo("three"); dt.Columns[1].ColumnName.IsEqualTo("four"); @@ -250,64 +252,70 @@ public async Task ExecuteReaderClosedAsync() [Fact] public async Task LiteralReplacementOpen() { - await LiteralReplacement(connection); + await LiteralReplacement(connection).ConfigureAwait(false); } + [Fact] public async Task LiteralReplacementClosed() { - using (var conn = GetClosedConnection()) await LiteralReplacement(conn); + using (var conn = GetClosedConnection()) await LiteralReplacement(conn).ConfigureAwait(false); } + private async Task LiteralReplacement(IDbConnection conn) { try { - await conn.ExecuteAsync("drop table literal1"); - } catch { } - await conn.ExecuteAsync("create table literal1 (id int not null, foo int not null)"); - await conn.ExecuteAsync("insert literal1 (id,foo) values ({=id}, @foo)", new { id = 123, foo = 456 }); + await conn.ExecuteAsync("drop table literal1").ConfigureAwait(false); + } + catch { /* don't care */ } + await conn.ExecuteAsync("create table literal1 (id int not null, foo int not null)").ConfigureAwait(false); + await conn.ExecuteAsync("insert literal1 (id,foo) values ({=id}, @foo)", new { id = 123, foo = 456 }).ConfigureAwait(false); var rows = new[] { new { id = 1, foo = 2 }, new { id = 3, foo = 4 } }; - await conn.ExecuteAsync("insert literal1 (id,foo) values ({=id}, @foo)", rows); - var count = (await conn.QueryAsync("select count(1) from literal1 where id={=foo}", new { foo = 123 })).Single(); + await conn.ExecuteAsync("insert literal1 (id,foo) values ({=id}, @foo)", rows).ConfigureAwait(false); + var count = (await conn.QueryAsync("select count(1) from literal1 where id={=foo}", new { foo = 123 }).ConfigureAwait(false)).Single(); count.IsEqualTo(1); - int sum = (await conn.QueryAsync("select sum(id) + sum(foo) from literal1")).Single(); + int sum = (await conn.QueryAsync("select sum(id) + sum(foo) from literal1").ConfigureAwait(false)).Single(); sum.IsEqualTo(123 + 456 + 1 + 2 + 3 + 4); } [Fact] public async Task LiteralReplacementDynamicOpen() { - await LiteralReplacementDynamic(connection); + await LiteralReplacementDynamic(connection).ConfigureAwait(false); } + [Fact] public async Task LiteralReplacementDynamicClosed() { - using (var conn = GetClosedConnection()) await LiteralReplacementDynamic(conn); + using (var conn = GetClosedConnection()) await LiteralReplacementDynamic(conn).ConfigureAwait(false); } + private async Task LiteralReplacementDynamic(IDbConnection conn) { var args = new DynamicParameters(); args.Add("id", 123); - try { await conn.ExecuteAsync("drop table literal2"); } catch { } - await conn.ExecuteAsync("create table literal2 (id int not null)"); - await conn.ExecuteAsync("insert literal2 (id) values ({=id})", args); + try { await conn.ExecuteAsync("drop table literal2").ConfigureAwait(false); } + catch { /* don't care */ } + await conn.ExecuteAsync("create table literal2 (id int not null)").ConfigureAwait(false); + await conn.ExecuteAsync("insert literal2 (id) values ({=id})", args).ConfigureAwait(false); args = new DynamicParameters(); args.Add("foo", 123); - var count = (await conn.QueryAsync("select count(1) from literal2 where id={=foo}", args)).Single(); + var count = (await conn.QueryAsync("select count(1) from literal2 where id={=foo}", args).ConfigureAwait(false)).Single(); count.IsEqualTo(1); } [Fact] public async Task LiteralInAsync() { - await connection.ExecuteAsync("create table #literalin(id int not null);"); + await connection.ExecuteAsync("create table #literalin(id int not null);").ConfigureAwait(false); await connection.ExecuteAsync("insert #literalin (id) values (@id)", new[] { new { id = 1 }, new { id = 2 }, new { id = 3 }, - }); + }).ConfigureAwait(false); var count = (await connection.QueryAsync("select count(1) from #literalin where id in {=ids}", - new { ids = new[] { 1, 3, 4 } })).Single(); + new { ids = new[] { 1, 3, 4 } }).ConfigureAwait(false)).Single(); count.IsEqualTo(2); } @@ -315,15 +323,15 @@ await connection.ExecuteAsync("insert #literalin (id) values (@id)", new[] { public async Task RunSequentialVersusParallelAsync() { var ids = Enumerable.Range(1, 20000).Select(id => new { id }).ToArray(); - await marsConnection.ExecuteAsync(new CommandDefinition("select @id", ids.Take(5), flags: CommandFlags.None)); + await marsConnection.ExecuteAsync(new CommandDefinition("select @id", ids.Take(5), flags: CommandFlags.None)).ConfigureAwait(false); var watch = Stopwatch.StartNew(); - await marsConnection.ExecuteAsync(new CommandDefinition("select @id", ids, flags: CommandFlags.None)); + await marsConnection.ExecuteAsync(new CommandDefinition("select @id", ids, flags: CommandFlags.None)).ConfigureAwait(false); watch.Stop(); Console.WriteLine("No pipeline: {0}ms", watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); - await marsConnection.ExecuteAsync(new CommandDefinition("select @id", ids, flags: CommandFlags.Pipelined)); + await marsConnection.ExecuteAsync(new CommandDefinition("select @id", ids, flags: CommandFlags.Pipelined)).ConfigureAwait(false); watch.Stop(); Console.WriteLine("Pipeline: {0}ms", watch.ElapsedMilliseconds); } @@ -348,7 +356,7 @@ public void RunSequentialVersusParallelSync() [Fact] public void AssertNoCacheWorksForQueryMultiple() { - int a = 123, b = 456; + const int a = 123, b = 456; var cmdDef = new CommandDefinition(@"select @a; select @b;", new { a, b @@ -368,8 +376,8 @@ public void AssertNoCacheWorksForQueryMultiple() c.IsEqualTo(123); d.IsEqualTo(456); } - - class BasicType + + private class BasicType { public string Value { get; set; } } @@ -377,9 +385,9 @@ class BasicType [Fact] public async Task TypeBasedViaTypeAsync() { - Type type = GetSomeType(); - - dynamic actual = (await marsConnection.QueryAsync(type, "select @A as [A], @B as [B]", new { A = 123, B = "abc" })).FirstOrDefault(); + Type type = Common.GetSomeType(); + + dynamic actual = (await marsConnection.QueryAsync(type, "select @A as [A], @B as [B]", new { A = 123, B = "abc" }).ConfigureAwait(false)).FirstOrDefault(); ((object)actual).GetType().IsEqualTo(type); int a = actual.A; string b = actual.B; @@ -390,9 +398,9 @@ public async Task TypeBasedViaTypeAsync() [Fact] public async Task TypeBasedViaTypeAsyncFirstOrDefault() { - Type type = GetSomeType(); + Type type = Common.GetSomeType(); - dynamic actual = (await marsConnection.QueryFirstOrDefaultAsync(type, "select @A as [A], @B as [B]", new { A = 123, B = "abc" })); + dynamic actual = await marsConnection.QueryFirstOrDefaultAsync(type, "select @A as [A], @B as [B]", new { A = 123, B = "abc" }).ConfigureAwait(false); ((object)actual).GetType().IsEqualTo(type); int a = actual.A; string b = actual.B; @@ -403,26 +411,26 @@ public async Task TypeBasedViaTypeAsyncFirstOrDefault() [Fact] public async Task Issue22_ExecuteScalarAsync() { - int i = await connection.ExecuteScalarAsync("select 123"); + int i = await connection.ExecuteScalarAsync("select 123").ConfigureAwait(false); i.IsEqualTo(123); - i = await connection.ExecuteScalarAsync("select cast(123 as bigint)"); + i = await connection.ExecuteScalarAsync("select cast(123 as bigint)").ConfigureAwait(false); i.IsEqualTo(123); - long j = await connection.ExecuteScalarAsync("select 123"); + long j = await connection.ExecuteScalarAsync("select 123").ConfigureAwait(false); j.IsEqualTo(123L); - j = await connection.ExecuteScalarAsync("select cast(123 as bigint)"); + j = await connection.ExecuteScalarAsync("select cast(123 as bigint)").ConfigureAwait(false); j.IsEqualTo(123L); - int? k = await connection.ExecuteScalarAsync("select @i", new { i = default(int?) }); + int? k = await connection.ExecuteScalarAsync("select @i", new { i = default(int?) }).ConfigureAwait(false); k.IsNull(); } [Fact] public async Task Issue346_QueryAsyncConvert() { - int i = (await connection.QueryAsync("Select Cast(123 as bigint)")).First(); + int i = (await connection.QueryAsync("Select Cast(123 as bigint)").ConfigureAwait(false)).First(); i.IsEqualTo(123); } @@ -444,7 +452,7 @@ await connection.ExecuteAsync(@" SET @PersonId = @PersonId + 1 SET @NumberOfLegs = @NumberOfLegs - 1 SET @AddressName = 'bobs burgers' -SET @AddressPersonId = @PersonId", p); +SET @AddressPersonId = @PersonId", p).ConfigureAwait(false); bob.Occupation.IsEqualTo("grillmaster"); bob.PersonId.IsEqualTo(2); @@ -472,7 +480,7 @@ public async Task TestSupportForDynamicParametersOutputExpressions_ScalarAsync() SET @NumberOfLegs = @NumberOfLegs - 1 SET @AddressName = 'bobs burgers' SET @AddressPersonId = @PersonId -select 42", p)); +select 42", p).ConfigureAwait(false)); bob.Occupation.IsEqualTo("grillmaster"); bob.PersonId.IsEqualTo(2); @@ -500,7 +508,7 @@ public async Task TestSupportForDynamicParametersOutputExpressions_Query_Default SET @NumberOfLegs = @NumberOfLegs - 1 SET @AddressName = 'bobs burgers' SET @AddressPersonId = @PersonId -select 42", p)).Single(); +select 42", p).ConfigureAwait(false)).Single(); bob.Occupation.IsEqualTo("grillmaster"); bob.PersonId.IsEqualTo(2); @@ -528,7 +536,7 @@ public async Task TestSupportForDynamicParametersOutputExpressions_Query_Buffere SET @NumberOfLegs = @NumberOfLegs - 1 SET @AddressName = 'bobs burgers' SET @AddressPersonId = @PersonId -select 42", p, flags: CommandFlags.Buffered))).Single(); +select 42", p, flags: CommandFlags.Buffered)).ConfigureAwait(false)).Single(); bob.Occupation.IsEqualTo("grillmaster"); bob.PersonId.IsEqualTo(2); @@ -556,7 +564,7 @@ public async Task TestSupportForDynamicParametersOutputExpressions_Query_NonBuff SET @NumberOfLegs = @NumberOfLegs - 1 SET @AddressName = 'bobs burgers' SET @AddressPersonId = @PersonId -select 42", p, flags: CommandFlags.None))).Single(); +select 42", p, flags: CommandFlags.None)).ConfigureAwait(false)).Single(); bob.Occupation.IsEqualTo("grillmaster"); bob.PersonId.IsEqualTo(2); @@ -586,7 +594,7 @@ public async Task TestSupportForDynamicParametersOutputExpressions_QueryMultiple SET @AddressName = 'bobs burgers' select 42 select 17 -SET @AddressPersonId = @PersonId", p)) +SET @AddressPersonId = @PersonId", p).ConfigureAwait(false)) { x = multi.ReadAsync().Result.Single(); y = multi.ReadAsync().Result.Single(); @@ -604,45 +612,48 @@ select 17 [Fact] public async Task TestSubsequentQueriesSuccessAsync() { - var data0 = (await connection.QueryAsync("select 1 as [Id] where 1 = 0")).ToList(); + var data0 = (await connection.QueryAsync("select 1 as [Id] where 1 = 0").ConfigureAwait(false)).ToList(); data0.Count.IsEqualTo(0); - var data1 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered))).ToList(); + var data1 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered)).ConfigureAwait(false)).ToList(); data1.Count.IsEqualTo(0); - var data2 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None))).ToList(); + var data2 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None)).ConfigureAwait(false)).ToList(); data2.Count.IsEqualTo(0); - data0 = (await connection.QueryAsync("select 1 as [Id] where 1 = 0")).ToList(); + data0 = (await connection.QueryAsync("select 1 as [Id] where 1 = 0").ConfigureAwait(false)).ToList(); data0.Count.IsEqualTo(0); - data1 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered))).ToList(); + data1 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered)).ConfigureAwait(false)).ToList(); data1.Count.IsEqualTo(0); - data2 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None))).ToList(); + data2 = (await connection.QueryAsync(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None)).ConfigureAwait(false)).ToList(); data2.Count.IsEqualTo(0); } - class AsyncFoo0 { public int Id { get; set; } } - class AsyncFoo1 { public int Id { get; set; } } - class AsyncFoo2 { public int Id { get; set; } } + + private class AsyncFoo0 { public int Id { get; set; } } + + private class AsyncFoo1 { public int Id { get; set; } } + + private class AsyncFoo2 { public int Id { get; set; } } [Fact] public async Task TestSchemaChangedViaFirstOrDefaultAsync() { - await connection.ExecuteAsync("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); + await connection.ExecuteAsync("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')").ConfigureAwait(false); try { - var d = await connection.QueryFirstOrDefaultAsync("select * from #dog"); + var d = await connection.QueryFirstOrDefaultAsync("select * from #dog").ConfigureAwait(false); d.Name.IsEqualTo("Alf"); d.Age.IsEqualTo(1); connection.Execute("alter table #dog drop column Name"); - d = await connection.QueryFirstOrDefaultAsync("select * from #dog"); + d = await connection.QueryFirstOrDefaultAsync("select * from #dog").ConfigureAwait(false); d.Name.IsNull(); d.Age.IsEqualTo(1); } finally { - await connection.ExecuteAsync("drop table #dog"); + await connection.ExecuteAsync("drop table #dog").ConfigureAwait(false); } } @@ -666,7 +677,7 @@ public async Task TestMultiMapArbitraryMapsAsync() insert #ReviewBoards values(1, 'Review Board 1', 1, 2, 3, 4, 5, 6, 7, 8, 9) "; - await connection.ExecuteAsync(createSql); + await connection.ExecuteAsync(createSql).ConfigureAwait(false); try { const string sql = @" @@ -702,7 +713,7 @@ public async Task TestMultiMapArbitraryMapsAsync() return board; }; - var data = (await connection.QueryAsync(sql, types, mapper)).ToList(); + var data = (await connection.QueryAsync(sql, types, mapper).ConfigureAwait(false)).ToList(); var p = data.First(); p.Id.IsEqualTo(1); @@ -738,14 +749,14 @@ public async Task Issue157_ClosedReaderAsync() var args = new { x = 42 }; const string sql = @"select 123 as [A], 'abc' as [B] where @x=42"; var row = (await connection.QueryAsync(new CommandDefinition( - sql, args, flags:CommandFlags.None))).Single(); + sql, args, flags:CommandFlags.None)).ConfigureAwait(false)).Single(); row.IsNotNull(); row.A.IsEqualTo(123); row.B.IsEqualTo("abc"); args = new { x = 5 }; (await connection.QueryAsync(new CommandDefinition( - sql, args, flags: CommandFlags.None))).Any().IsFalse(); + sql, args, flags: CommandFlags.None)).ConfigureAwait(false)).Any().IsFalse(); } [Fact] @@ -755,14 +766,14 @@ public async Task TestAtEscaping() declare @@Name int select @@Name = @Id+1 select @@Name - ", new Product { Id = 1 })).Single(); + ", new Product { Id = 1 }).ConfigureAwait(false)).Single(); id.IsEqualTo(2); } [Fact] public async Task Issue1281_DataReaderOutOfOrderAsync() { - using (var reader = await connection.ExecuteReaderAsync("Select 0, 1, 2")) + using (var reader = await connection.ExecuteReaderAsync("Select 0, 1, 2").ConfigureAwait(false)) { reader.Read().IsTrue(); reader.GetInt32(2).IsEqualTo(2); @@ -777,11 +788,10 @@ public async Task Issue563_QueryAsyncShouldThrowException() { try { - var data = (await connection.QueryAsync("select 1 union all select 2; RAISERROR('after select', 16, 1);")).ToList(); + var data = (await connection.QueryAsync("select 1 union all select 2; RAISERROR('after select', 16, 1);").ConfigureAwait(false)).ToList(); Assert.Fail(); } catch (SqlException ex) when (ex.Message == "after select") { } } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Dapper.Tests/Tests.Constructors.cs b/Dapper.Tests/ConstructorTests.cs similarity index 84% rename from Dapper.Tests/Tests.Constructors.cs rename to Dapper.Tests/ConstructorTests.cs index bfa1280e..82429c29 100644 --- a/Dapper.Tests/Tests.Constructors.cs +++ b/Dapper.Tests/ConstructorTests.cs @@ -1,12 +1,11 @@ -using Dapper; -using System; +using System; using System.Data; using System.Linq; using Xunit; namespace Dapper.Tests { - public partial class TestSuite + public class ConstructorTests : TestBase { [Fact] public void TestAbstractInheritance() @@ -35,7 +34,6 @@ public void TestConstructorsWithAccessModifiers() value.B.IsEqualTo("Dapper!"); } - [Fact] public void TestNoDefaultConstructor() { @@ -59,7 +57,6 @@ public void TestNoDefaultConstructorWithChar() nodef.Char3.IsEqualTo(c3); } - [Fact] public void TestNoDefaultConstructorWithEnum() { @@ -85,7 +82,8 @@ Field INT NOT NULL PRIMARY KEY IDENTITY(1,1), rows[0].Field_1.IsEqualTo(1); rows[0].GetWentThroughProperConstructor().IsTrue(); } - class _ExplicitConstructors + + private class _ExplicitConstructors { public int Field { get; set; } public int Field_1 { get; set; } @@ -105,9 +103,9 @@ public bool GetWentThroughProperConstructor() return WentThroughProperConstructor; } } - + #if LINQ2SQL - class NoDefaultConstructorWithBinary + private class NoDefaultConstructorWithBinary { public System.Data.Linq.Binary Value { get; set; } public int Ynt { get; set; } @@ -116,6 +114,7 @@ public NoDefaultConstructorWithBinary(System.Data.Linq.Binary val) Value = val; } } + [Fact] public void TestNoDefaultConstructorBinary() { @@ -154,13 +153,13 @@ SomeBlargValue nvarchar(200), parameterDoesNot.Id.IsEqualTo(1); parameterDoesNot.SomeValue.IsEqualTo("what up?"); parameterDoesNot.SomeBlargValue.Value.IsEqualTo(Expected); - - } - class Blarg // I would usually expect this to be a struct; using a class - { // so that we can't pass unexpectedly due to forcing an unsafe cast - want - // to see an InvalidCastException if it is wrong + // I would usually expect this to be a struct; using a class + // so that we can't pass unexpectedly due to forcing an unsafe cast - want + // to see an InvalidCastException if it is wrong + private class Blarg + { public Blarg(string value) { Value = value; } public string Value { get; } public override string ToString() @@ -168,7 +167,8 @@ public override string ToString() return Value; } } - class Issue461_BlargHandler : SqlMapper.TypeHandler + + private class Issue461_BlargHandler : SqlMapper.TypeHandler { public override void SetValue(IDbDataParameter parameter, Blarg value) { @@ -182,7 +182,7 @@ public override Blarg Parse(object value) } } - class Issue461_ParameterlessTypeConstructor + private class Issue461_ParameterlessTypeConstructor { public int Id { get; set; } @@ -190,7 +190,7 @@ class Issue461_ParameterlessTypeConstructor public Blarg SomeBlargValue { get; set; } } - class Issue461_ParameterisedTypeConstructor + private class Issue461_ParameterisedTypeConstructor { public Issue461_ParameterisedTypeConstructor(int id, string someValue, Blarg someBlargValue) { @@ -205,7 +205,7 @@ public Issue461_ParameterisedTypeConstructor(int id, string someValue, Blarg som public Blarg SomeBlargValue { get; } } - public class AbstractInheritance + public static class AbstractInheritance { public abstract class Order { @@ -222,36 +222,39 @@ public class ConcreteOrder : Order } } - class MultipleConstructors + private class MultipleConstructors { public MultipleConstructors() { - } + public MultipleConstructors(int a, string b) { A = a + 1; B = b + "!"; } + public int A { get; set; } public string B { get; set; } } - class ConstructorsWithAccessModifiers + private class ConstructorsWithAccessModifiers { private ConstructorsWithAccessModifiers() { } + public ConstructorsWithAccessModifiers(int a, string b) { A = a + 1; B = b + "!"; } + public int A { get; set; } public string B { get; set; } } - class NoDefaultConstructor + private class NoDefaultConstructor { public NoDefaultConstructor(int a1, int? b1, float f1, string s1, Guid G1) { @@ -261,6 +264,7 @@ public NoDefaultConstructor(int a1, int? b1, float f1, string s1, Guid G1) S = s1; G = G1; } + public int A { get; set; } public int? B { get; set; } public float F { get; set; } @@ -268,7 +272,7 @@ public NoDefaultConstructor(int a1, int? b1, float f1, string s1, Guid G1) public Guid G { get; set; } } - class NoDefaultConstructorWithChar + private class NoDefaultConstructorWithChar { public NoDefaultConstructorWithChar(char c1, char? c2, char? c3) { @@ -276,12 +280,13 @@ public NoDefaultConstructorWithChar(char c1, char? c2, char? c3) Char2 = c2; Char3 = c3; } + public char Char1 { get; set; } public char? Char2 { get; set; } public char? Char3 { get; set; } } - class NoDefaultConstructorWithEnum + private class NoDefaultConstructorWithEnum { public NoDefaultConstructorWithEnum(ShortEnum e1, ShortEnum? n1, ShortEnum? n2) { @@ -289,9 +294,25 @@ public NoDefaultConstructorWithEnum(ShortEnum e1, ShortEnum? n1, ShortEnum? n2) NE1 = n1; NE2 = n2; } + public ShortEnum E { get; set; } public ShortEnum? NE1 { get; set; } public ShortEnum? NE2 { get; set; } } + + private class WithPrivateConstructor + { + public int Foo { get; set; } + private WithPrivateConstructor() + { + } + } + + [Fact] + public void TestWithNonPublicConstructor() + { + var output = connection.Query("select 1 as Foo").First(); + output.Foo.IsEqualTo(1); + } } } diff --git a/Dapper.Tests/Dapper.Tests.csproj b/Dapper.Tests/Dapper.Tests.csproj new file mode 100644 index 00000000..9ed9d39a --- /dev/null +++ b/Dapper.Tests/Dapper.Tests.csproj @@ -0,0 +1,56 @@ + + + Dapper.Tests + Dapper.Tests + Dapper Core Test Suite + false + true + true + net452;netcoreapp1.0 + + + + $(DefineConstants);NET45;MYSQL;ENTITY_FRAMEWORK;LINQ2SQL;FIREBIRD;SQL_CE;POSTGRESQL;OLEDB;SQLITE + + + $(DefineConstants);COREFX + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dapper.Tests/Dapper.Tests.xproj b/Dapper.Tests/Dapper.Tests.xproj deleted file mode 100644 index 9f0655d0..00000000 --- a/Dapper.Tests/Dapper.Tests.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 052c0817-db26-4925-8929-8c5e42d148d5 - Dapper.Tests - .\obj - .\bin\ - - - 2.0 - - - - - - \ No newline at end of file diff --git a/Dapper.Tests/Tests.IDataReader.cs b/Dapper.Tests/DataReaderTests.cs similarity index 85% rename from Dapper.Tests/Tests.IDataReader.cs rename to Dapper.Tests/DataReaderTests.cs index 8226dbf2..2a78f33c 100644 --- a/Dapper.Tests/Tests.IDataReader.cs +++ b/Dapper.Tests/DataReaderTests.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Dapper.Tests { - public partial class TestSuite + public partial class DataReaderTests : TestBase { [Fact] public void GetSameReaderForSameShape() @@ -82,7 +80,7 @@ union all [Fact] public void DiscriminatedUnionWithMultiMapping() { - List result = new List(); + var result = new List(); using (var reader = connection.ExecuteReader(@" select 'abc' as Name, 1 as Type, 3.0 as Value, 1 as Id, 'zxc' as Name union all @@ -131,18 +129,20 @@ union all bar.HazNameIdObject.Name.IsEqualTo("qwe"); } - abstract class Discriminated_BaseType + private abstract class Discriminated_BaseType { public abstract int Type { get; } } - class Discriminated_Foo : Discriminated_BaseType + + private class Discriminated_Foo : Discriminated_BaseType { public string Name { get; set; } public override int Type { get { return 1; } } } - class Discriminated_Bar : Discriminated_BaseType + + private class Discriminated_Bar : Discriminated_BaseType { public float Value { get; set; } public override int Type @@ -151,12 +151,12 @@ public override int Type } } - - abstract class DiscriminatedWithMultiMapping_BaseType : Discriminated_BaseType + private abstract class DiscriminatedWithMultiMapping_BaseType : Discriminated_BaseType { public abstract HazNameId HazNameIdObject { get; set; } } - class DiscriminatedWithMultiMapping_Foo : DiscriminatedWithMultiMapping_BaseType + + private class DiscriminatedWithMultiMapping_Foo : DiscriminatedWithMultiMapping_BaseType { public override HazNameId HazNameIdObject { get; set; } public string Name { get; set; } @@ -165,7 +165,8 @@ public override int Type get { return 1; } } } - class DiscriminatedWithMultiMapping_Bar : DiscriminatedWithMultiMapping_BaseType + + private class DiscriminatedWithMultiMapping_Bar : DiscriminatedWithMultiMapping_BaseType { public override HazNameId HazNameIdObject { get; set; } public float Value { get; set; } @@ -173,12 +174,6 @@ public override int Type { get { return 2; } } - - } - public class HazNameId - { - public string Name { get; set; } - public int Id { get; set; } } } -} +} \ No newline at end of file diff --git a/Dapper.Tests/DecimalTests.cs b/Dapper.Tests/DecimalTests.cs new file mode 100644 index 00000000..16997797 --- /dev/null +++ b/Dapper.Tests/DecimalTests.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Dapper.Tests +{ + public class DecimalTests : TestBase + { + [Fact] + public void Issue261_Decimals() + { + var parameters = new DynamicParameters(); + parameters.Add("c", dbType: DbType.Decimal, direction: ParameterDirection.Output, precision: 10, scale: 5); + connection.Execute("create proc #Issue261 @c decimal(10,5) OUTPUT as begin set @c=11.884 end"); + connection.Execute("#Issue261", parameters, commandType: CommandType.StoredProcedure); + var c = parameters.Get("c"); + c.IsEqualTo(11.884M); + } + + [Fact] + public void Issue261_Decimals_ADONET_SetViaBaseClass() => Issue261_Decimals_ADONET(true); + + [Fact] + public void Issue261_Decimals_ADONET_SetViaConcreteClass() => Issue261_Decimals_ADONET(false); + + private void Issue261_Decimals_ADONET(bool setPrecisionScaleViaAbstractApi) + { + try + { + using (var cmd = connection.CreateCommand()) + { + cmd.CommandText = "create proc #Issue261Direct @c decimal(10,5) OUTPUT as begin set @c=11.884 end"; + cmd.ExecuteNonQuery(); + } + } + catch { /* we don't care that it already exists */ } + + using (var cmd = connection.CreateCommand()) + { + cmd.CommandType = CommandType.StoredProcedure; + cmd.CommandText = "#Issue261Direct"; + var c = cmd.CreateParameter(); + c.ParameterName = "c"; + c.Direction = ParameterDirection.Output; + c.Value = DBNull.Value; + c.DbType = DbType.Decimal; + + if (setPrecisionScaleViaAbstractApi) + { + IDbDataParameter baseParam = c; + baseParam.Precision = 10; + baseParam.Scale = 5; + } + else + { + c.Precision = 10; + c.Scale = 5; + } + + cmd.Parameters.Add(c); + cmd.ExecuteNonQuery(); + decimal value = (decimal)c.Value; + value.IsEqualTo(11.884M); + } + } + + [Fact] + public void BasicDecimals() + { + var c = connection.Query("select @c", new { c = 11.884M }).Single(); + c.IsEqualTo(11.884M); + } + + [Fact] + public void TestDoubleDecimalConversions_SO18228523_RightWay() + { + var row = connection.Query( + "select cast(1 as float) as A, cast(2 as float) as B, cast(3 as decimal) as C, cast(4 as decimal) as D").Single(); + row.A.Equals(1.0); + row.B.Equals(2.0); + row.C.Equals(3.0M); + row.D.Equals(4.0M); + } + + [Fact] + public void TestDoubleDecimalConversions_SO18228523_WrongWay() + { + var row = connection.Query( + "select cast(1 as decimal) as A, cast(2 as decimal) as B, cast(3 as float) as C, cast(4 as float) as D").Single(); + row.A.Equals(1.0); + row.B.Equals(2.0); + row.C.Equals(3.0M); + row.D.Equals(4.0M); + } + + [Fact] + public void TestDoubleDecimalConversions_SO18228523_Nulls() + { + var row = connection.Query( + "select cast(null as decimal) as A, cast(null as decimal) as B, cast(null as float) as C, cast(null as float) as D").Single(); + row.A.Equals(0.0); + row.B.IsNull(); + row.C.Equals(0.0M); + row.D.IsNull(); + } + + private class HasDoubleDecimal + { + public double A { get; set; } + public double? B { get; set; } + public decimal C { get; set; } + public decimal? D { get; set; } + } + } +} diff --git a/Dapper.Tests/Tests.Enums.cs b/Dapper.Tests/EnumTests.cs similarity index 56% rename from Dapper.Tests/Tests.Enums.cs rename to Dapper.Tests/EnumTests.cs index 7026505f..e49cb36e 100644 --- a/Dapper.Tests/Tests.Enums.cs +++ b/Dapper.Tests/EnumTests.cs @@ -1,12 +1,10 @@ -using Dapper; -using System; -using System.Data; +using System.Data; using System.Linq; using Xunit; namespace Dapper.Tests { - public partial class TestSuite + public class EnumTests : TestBase { [Fact] public void TestEnumWeirdness() @@ -48,37 +46,84 @@ public void TestEnumParamsWithoutNullable() obj.B.IsEqualTo(EnumParam.B); obj.C.IsEqualTo((EnumParam)0); } - enum EnumParam : short + + private enum EnumParam : short { - None, A, B + None = 0, + A = 1, + B = 2 } - class EnumParamObject + + private class EnumParamObject { public EnumParam A { get; set; } public EnumParam? B { get; set; } public EnumParam? C { get; set; } } - class EnumParamObjectNonNullable + + private class EnumParamObjectNonNullable { public EnumParam A { get; set; } public EnumParam? B { get; set; } public EnumParam? C { get; set; } } - - - - enum TestEnum : byte + private enum TestEnum : byte { Bla = 1 } - class TestEnumClass + + private class TestEnumClass { public TestEnum? EnumEnum { get; set; } } - class TestEnumClassNoNull + + private class TestEnumClassNoNull { public TestEnum EnumEnum { get; set; } } + + [Fact] + public void AdoNetEnumValue() + { + using (var cmd = connection.CreateCommand()) + { + cmd.CommandText = "select @foo"; + var p = cmd.CreateParameter(); + p.ParameterName = "@foo"; + p.DbType = DbType.Int32; // it turns out that this is the key piece; setting the DbType + p.Value = AnEnum.B; + cmd.Parameters.Add(p); + object value = cmd.ExecuteScalar(); + AnEnum val = (AnEnum)value; + val.IsEqualTo(AnEnum.B); + } + } + + [Fact] + public void DapperEnumValue_SqlServer() => Common.DapperEnumValue(connection); + + private enum SO27024806Enum + { + Foo = 0, + Bar = 1 + } + + private class SO27024806Class + { + public SO27024806Class(SO27024806Enum myField) + { + MyField = myField; + } + + public SO27024806Enum MyField { get; set; } + } + + [Fact] + public void SO27024806_TestVarcharEnumMemberWithExplicitConstructor() + { + var foo = connection.Query("SELECT 'Foo' AS myField").Single(); + foo.MyField.IsEqualTo(SO27024806Enum.Foo); + } } } diff --git a/Dapper.Tests/Assert.cs b/Dapper.Tests/Helpers/Assert.cs similarity index 95% rename from Dapper.Tests/Assert.cs rename to Dapper.Tests/Helpers/Assert.cs index b72ae852..06af3857 100644 --- a/Dapper.Tests/Assert.cs +++ b/Dapper.Tests/Helpers/Assert.cs @@ -13,11 +13,12 @@ public static void IsSequenceEqualTo(this IEnumerable actual, IEnumerable< { Xunit.Assert.Equal(expected, actual ?? new T[0]); } + public static void IsMoreThan(this int a, int b) { Xunit.Assert.True(a > b, $"{a} should be larger than {b}"); } - + public static void IsMoreThan(this long a, int b) { Xunit.Assert.True(a > b, $"{a} should be larger than {b}"); @@ -27,6 +28,7 @@ public static void Fail(string message = null) { Xunit.Assert.True(false, message ?? "Expectation failed"); } + public static void IsFalse(this bool b) { Xunit.Assert.False(b); diff --git a/Dapper.Tests/Attributes.cs b/Dapper.Tests/Helpers/Attributes.cs similarity index 86% rename from Dapper.Tests/Attributes.cs rename to Dapper.Tests/Helpers/Attributes.cs index 1ed3a1c8..434608a3 100644 --- a/Dapper.Tests/Attributes.cs +++ b/Dapper.Tests/Helpers/Attributes.cs @@ -14,6 +14,7 @@ public FactUnlessCoreCLRAttribute(string url) #endif this.Url = url; } + public string Url { get; private set; } } @@ -26,8 +27,10 @@ public FactLongRunningAttribute() Skip = "Long running"; #endif } + public string Url { get; private set; } } + public class FactRequiredCompatibilityLevelAttribute : FactAttribute { public FactRequiredCompatibilityLevelAttribute(int level) : base() @@ -37,21 +40,23 @@ public FactRequiredCompatibilityLevelAttribute(int level) : base() Skip = $"Compatibility level {level} required; detected {DetectedLevel}"; } } + public const int SqlServer2016 = 130; public static readonly int DetectedLevel; static FactRequiredCompatibilityLevelAttribute() { - using (var conn = TestSuite.GetOpenConnection()) + using (var conn = TestBase.GetOpenConnection()) { try { DetectedLevel = conn.QuerySingle("SELECT compatibility_level FROM sys.databases where name = DB_NAME()"); } - catch { } + catch { /* don't care */ } } } } - public class FactUnlessCaseSensitiveDatabaseAttribute : FactAttribute + + public class FactUnlessCaseSensitiveDatabaseAttribute : FactAttribute { public FactUnlessCaseSensitiveDatabaseAttribute() : base() { @@ -64,7 +69,7 @@ public FactUnlessCaseSensitiveDatabaseAttribute() : base() public static readonly bool IsCaseSensitive; static FactUnlessCaseSensitiveDatabaseAttribute() { - using (var conn = TestSuite.GetOpenConnection()) + using (var conn = TestBase.GetOpenConnection()) { try { diff --git a/Dapper.Tests/Helpers/Common.cs b/Dapper.Tests/Helpers/Common.cs new file mode 100644 index 00000000..23fc6e59 --- /dev/null +++ b/Dapper.Tests/Helpers/Common.cs @@ -0,0 +1,68 @@ +using System; +using System.Data; +using System.Data.Common; + +namespace Dapper.Tests +{ + public static class Common + { + public static Type GetSomeType() => typeof(SomeType); + + public static void DapperEnumValue(IDbConnection connection) + { + // test passing as AsEnum, reading as int + var v = (AnEnum)connection.QuerySingle("select @v, @y, @z", new { v = AnEnum.B, y = (AnEnum?)AnEnum.B, z = (AnEnum?)null }); + v.IsEqualTo(AnEnum.B); + + var args = new DynamicParameters(); + args.Add("v", AnEnum.B); + args.Add("y", AnEnum.B); + args.Add("z", null); + v = (AnEnum)connection.QuerySingle("select @v, @y, @z", args); + v.IsEqualTo(AnEnum.B); + + // test passing as int, reading as AnEnum + var k = (int)connection.QuerySingle("select @v, @y, @z", new { v = (int)AnEnum.B, y = (int?)(int)AnEnum.B, z = (int?)null }); + k.IsEqualTo((int)AnEnum.B); + + args = new DynamicParameters(); + args.Add("v", (int)AnEnum.B); + args.Add("y", (int)AnEnum.B); + args.Add("z", null); + k = (int)connection.QuerySingle("select @v, @y, @z", args); + k.IsEqualTo((int)AnEnum.B); + } + + public static void TestDateTime(DbConnection connection) + { + DateTime? now = DateTime.UtcNow; + try { connection.Execute("DROP TABLE Persons"); } catch { /* don't care */ } + connection.Execute(@"CREATE TABLE Persons (id int not null, dob datetime null)"); + connection.Execute(@"INSERT Persons (id, dob) values (@id, @dob)", + new { id = 7, dob = (DateTime?)null }); + connection.Execute(@"INSERT Persons (id, dob) values (@id, @dob)", + new { id = 42, dob = now }); + + var row = connection.QueryFirstOrDefault( + "SELECT id, dob, dob as dob2 FROM Persons WHERE id=@id", new { id = 7 }); + row.IsNotNull(); + row.Id.IsEqualTo(7); + row.DoB.IsNull(); + row.DoB2.IsNull(); + + row = connection.QueryFirstOrDefault( + "SELECT id, dob FROM Persons WHERE id=@id", new { id = 42 }); + row.IsNotNull(); + row.Id.IsEqualTo(42); + row.DoB.Equals(now); + row.DoB2.Equals(now); + } + + private class NullableDatePerson + { + public int Id { get; set; } + public DateTime? DoB { get; set; } + public DateTime? DoB2 { get; set; } + } + } +} diff --git a/Dapper.Tests/SqlServerTypes/Loader.cs b/Dapper.Tests/Helpers/SqlServerTypesLoader.cs similarity index 88% rename from Dapper.Tests/SqlServerTypes/Loader.cs rename to Dapper.Tests/Helpers/SqlServerTypesLoader.cs index c35fb89b..f0135d92 100644 --- a/Dapper.Tests/SqlServerTypes/Loader.cs +++ b/Dapper.Tests/Helpers/SqlServerTypesLoader.cs @@ -2,15 +2,15 @@ using System.IO; using System.Runtime.InteropServices; -namespace SqlServerTypes +namespace Dapper.Tests { /// /// Utility methods related to CLR Types for SQL Server /// - internal class Utilities + internal static class SqlServerTypesLoader { - private static object _nativeLoadLock = new object(); - private static bool _nativeAssembliesLoaded = false; + private static readonly object _nativeLoadLock = new object(); + private static bool _nativeAssembliesLoaded; [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibrary(string libname); @@ -54,4 +54,4 @@ private static void LoadNativeAssembly(string nativeBinaryPath, string assemblyN } } } -} \ No newline at end of file +} diff --git a/Dapper.Tests/Helpers/TransactedConnection.cs b/Dapper.Tests/Helpers/TransactedConnection.cs new file mode 100644 index 00000000..7dd78574 --- /dev/null +++ b/Dapper.Tests/Helpers/TransactedConnection.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dapper.Tests +{ + public class TransactedConnection : IDbConnection + { + private readonly IDbConnection _conn; + private readonly IDbTransaction _tran; + + public TransactedConnection(IDbConnection conn, IDbTransaction tran) + { + _conn = conn; + _tran = tran; + } + + public string ConnectionString + { + get { return _conn.ConnectionString; } + set { _conn.ConnectionString = value; } + } + + public int ConnectionTimeout => _conn.ConnectionTimeout; + public string Database => _conn.Database; + public ConnectionState State => _conn.State; + + public IDbTransaction BeginTransaction(IsolationLevel il) + { + throw new NotImplementedException(); + } + + public IDbTransaction BeginTransaction() => _tran; + + public void ChangeDatabase(string databaseName) => _conn.ChangeDatabase(databaseName); + + public void Close() => _conn.Close(); + + public IDbCommand CreateCommand() + { + // The command inherits the "current" transaction. + var command = _conn.CreateCommand(); + command.Transaction = _tran; + return command; + } + + public void Dispose() => _conn.Dispose(); + + public void Open() => _conn.Open(); + } +} diff --git a/Dapper.Tests/XunitSkippable.cs b/Dapper.Tests/Helpers/XunitSkippable.cs similarity index 86% rename from Dapper.Tests/XunitSkippable.cs rename to Dapper.Tests/Helpers/XunitSkippable.cs index 331b3b74..e89e3047 100644 --- a/Dapper.Tests/XunitSkippable.cs +++ b/Dapper.Tests/Helpers/XunitSkippable.cs @@ -1,27 +1,26 @@ using System; -#if XUNIT2 // Not in .Net 4.0 (xUnit v1)... using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Xunit.Abstractions; using Xunit.Sdk; -#endif namespace Dapper.Tests { public class SkipTestException : Exception { - public SkipTestException(string reason) : base(reason) { } + public SkipTestException(string reason) : base(reason) + { + } } -#if XUNIT2 // Most of the below is a direct copy & port from the wonderful examples by Brad Wilson at // https://github.com/xunit/samples.xunit/tree/master/DynamicSkipExample public class SkippableFactDiscoverer : IXunitTestCaseDiscoverer { - readonly IMessageSink _diagnosticMessageSink; + private readonly IMessageSink _diagnosticMessageSink; public SkippableFactDiscoverer(IMessageSink diagnosticMessageSink) { @@ -33,15 +32,18 @@ public IEnumerable Discover(ITestFrameworkDiscoveryOptions disco yield return new SkippableFactTestCase(_diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod); } } - + public class SkippableFactTestCase : XunitTestCase { [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippableFactTestCase() { } + public SkippableFactTestCase() + { + } public SkippableFactTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) - { } + { + } public override async Task RunAsync( IMessageSink diagnosticMessageSink, @@ -56,7 +58,7 @@ public override async Task RunAsync( skipMessageBus, constructorArguments, aggregator, - cancellationTokenSource); + cancellationTokenSource).ConfigureAwait(false); if (skipMessageBus.DynamicallySkippedTestCount > 0) { result.Failed -= skipMessageBus.DynamicallySkippedTestCount; @@ -69,7 +71,7 @@ public override async Task RunAsync( public class SkippableFactMessageBus : IMessageBus { - readonly IMessageBus _innerBus; + private readonly IMessageBus _innerBus; public SkippableFactMessageBus(IMessageBus innerBus) { _innerBus = innerBus; @@ -77,7 +79,9 @@ public SkippableFactMessageBus(IMessageBus innerBus) public int DynamicallySkippedTestCount { get; private set; } - public void Dispose() { } + public void Dispose() + { + } public bool QueueMessage(IMessageSinkMessage message) { @@ -94,5 +98,4 @@ public bool QueueMessage(IMessageSinkMessage message) return _innerBus.QueueMessage(message); } } -#endif } diff --git a/Dapper.Tests/License.txt b/Dapper.Tests/License.txt deleted file mode 100644 index 54c5bd15..00000000 --- a/Dapper.Tests/License.txt +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2010 Stack Overflow Internet Services, Inc - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/Dapper.Tests/Linq2Sql/Post.cs b/Dapper.Tests/Linq2Sql/Post.cs deleted file mode 100644 index 5c9ef42f..00000000 --- a/Dapper.Tests/Linq2Sql/Post.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Dapper.Tests.Linq2Sql -{ - public partial class Post - { - /* - public int Bloat { get; set; } - public int Bloat1 { get; set; } - public int Bloat2 { get; set; } - public int Bloat3 { get; set; } - public int Bloat4 { get; set; } - public int Bloat5 { get; set; } - public int Bloat6 { get; set; } - public int Bloat7 { get; set; } - public int Bloat8 { get; set; } - - - public int Bloat10 { get; set; } - public int Bloat11 { get; set; } - public int Bloat12 { get; set; } - public int Bloat13 { get; set; } - public int Bloat14 { get; set; } - public int Bloat15 { get; set; } - public int Bloat16 { get; set; } - public int Bloat17 { get; set; } - public int Bloat18 { get; set; } - */ - } -} diff --git a/Dapper.Tests/LiteralTests.cs b/Dapper.Tests/LiteralTests.cs new file mode 100644 index 00000000..ef9f83eb --- /dev/null +++ b/Dapper.Tests/LiteralTests.cs @@ -0,0 +1,100 @@ +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class LiteralTests : TestBase + { + [Fact] + public void LiteralReplacementEnumAndString() + { + var args = new { x = AnEnum.B, y = 123.45M, z = AnotherEnum.A }; + var row = connection.Query("select {=x} as x,{=y} as y,cast({=z} as tinyint) as z", args).Single(); + AnEnum x = (AnEnum)(int)row.x; + decimal y = row.y; + AnotherEnum z = (AnotherEnum)(byte)row.z; + x.Equals(AnEnum.B); + y.Equals(123.45M); + z.Equals(AnotherEnum.A); + } + + [Fact] + public void LiteralReplacementDynamicEnumAndString() + { + var args = new DynamicParameters(); + args.Add("x", AnEnum.B); + args.Add("y", 123.45M); + args.Add("z", AnotherEnum.A); + var row = connection.Query("select {=x} as x,{=y} as y,cast({=z} as tinyint) as z", args).Single(); + AnEnum x = (AnEnum)(int)row.x; + decimal y = row.y; + AnotherEnum z = (AnotherEnum)(byte)row.z; + x.Equals(AnEnum.B); + y.Equals(123.45M); + z.Equals(AnotherEnum.A); + } + + [Fact] + public void LiteralReplacementBoolean() + { + var row = connection.Query("select 42 where 1 = {=val}", new { val = true }).SingleOrDefault(); + row.IsNotNull(); + row.IsEqualTo(42); + row = connection.Query("select 42 where 1 = {=val}", new { val = false }).SingleOrDefault(); + row.IsNull(); + } + + [Fact] + public void LiteralReplacementWithIn() + { + var data = connection.Query("select @x where 1 in @ids and 1 ={=a}", + new { x = 1, ids = new[] { 1, 2, 3 }, a = 1 }).ToList(); + } + + private class MyRow + { + public int x { get; set; } + } + + [Fact] + public void LiteralIn() + { + connection.Execute("create table #literalin(id int not null);"); + connection.Execute("insert #literalin (id) values (@id)", new[] { + new { id = 1 }, + new { id = 2 }, + new { id = 3 }, + }); + var count = connection.Query("select count(1) from #literalin where id in {=ids}", + new { ids = new[] { 1, 3, 4 } }).Single(); + count.IsEqualTo(2); + } + + [Fact] + public void LiteralReplacement() + { + connection.Execute("create table #literal1 (id int not null, foo int not null)"); + connection.Execute("insert #literal1 (id,foo) values ({=id}, @foo)", new { id = 123, foo = 456 }); + var rows = new[] { new { id = 1, foo = 2 }, new { id = 3, foo = 4 } }; + connection.Execute("insert #literal1 (id,foo) values ({=id}, @foo)", rows); + var count = connection.Query("select count(1) from #literal1 where id={=foo}", new { foo = 123 }).Single(); + count.IsEqualTo(1); + int sum = connection.Query("select sum(id) + sum(foo) from #literal1").Single(); + sum.IsEqualTo(123 + 456 + 1 + 2 + 3 + 4); + } + + [Fact] + public void LiteralReplacementDynamic() + { + var args = new DynamicParameters(); + args.Add("id", 123); + connection.Execute("create table #literal2 (id int not null)"); + connection.Execute("insert #literal2 (id) values ({=id})", args); + + args = new DynamicParameters(); + args.Add("foo", 123); + var count = connection.Query("select count(1) from #literal2 where id={=foo}", args).Single(); + count.IsEqualTo(1); + } + } +} diff --git a/Dapper.Tests/MiscTests.cs b/Dapper.Tests/MiscTests.cs new file mode 100644 index 00000000..8465e447 --- /dev/null +++ b/Dapper.Tests/MiscTests.cs @@ -0,0 +1,1161 @@ +using Microsoft.CSharp.RuntimeBinder; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using System.Diagnostics; +using System.Linq; +using Xunit; + +#if COREFX +using System.Collections; +using System.Dynamic; +using System.Data.SqlTypes; +#else // net452 +using System.IO; +using System.Threading; +using System.Threading.Tasks; +#endif + +#if COREFX +namespace System +{ + public enum GenericUriParserOptions + { + Default + } + + public class GenericUriParser + { + private readonly GenericUriParserOptions options; + + public GenericUriParser(GenericUriParserOptions options) + { + this.options = options; + } + } +} +#endif + +namespace Dapper.Tests +{ + public class MiscTests : TestBase + { + [Fact] + public void TestNullableGuidSupport() + { + var guid = connection.Query("select null").First(); + guid.IsNull(); + + guid = Guid.NewGuid(); + var guid2 = connection.Query("select @guid", new { guid }).First(); + guid.IsEqualTo(guid2); + } + + [Fact] + public void TestNonNullableGuidSupport() + { + var guid = Guid.NewGuid(); + var guid2 = connection.Query("select @guid", new { guid }).First(); + Assert.IsTrue(guid == guid2); + } + + private struct Car + { + public enum TrapEnum : int + { + A = 1, + B = 2 + } +#pragma warning disable 0649 + public string Name; +#pragma warning restore 0649 + public int Age { get; set; } + public TrapEnum Trap { get; set; } + } + + private struct CarWithAllProps + { + public string Name { get; set; } + public int Age { get; set; } + + public Car.TrapEnum Trap { get; set; } + } + + [Fact] + public void TestStructs() + { + var car = connection.Query("select 'Ford' Name, 21 Age, 2 Trap").First(); + + car.Age.IsEqualTo(21); + car.Name.IsEqualTo("Ford"); + ((int)car.Trap).IsEqualTo(2); + } + + [Fact] + public void TestStructAsParam() + { + var car1 = new CarWithAllProps { Name = "Ford", Age = 21, Trap = Car.TrapEnum.B }; + // note Car has Name as a field; parameters only respect properties at the moment + var car2 = connection.Query("select @Name Name, @Age Age, @Trap Trap", car1).First(); + + car2.Name.IsEqualTo(car1.Name); + car2.Age.IsEqualTo(car1.Age); + car2.Trap.IsEqualTo(car1.Trap); + } + + [Fact] + public void SelectListInt() + { + connection.Query("select 1 union all select 2 union all select 3") + .IsSequenceEqualTo(new[] { 1, 2, 3 }); + } + + [Fact] + public void SelectBinary() + { + connection.Query("select cast(1 as varbinary(4))").First().SequenceEqual(new byte[] { 1 }); + } + + [Fact] + public void TestSchemaChanged() + { + connection.Execute("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); + try + { + var d = connection.Query("select * from #dog").Single(); + d.Name.IsEqualTo("Alf"); + d.Age.IsEqualTo(1); + connection.Execute("alter table #dog drop column Name"); + d = connection.Query("select * from #dog").Single(); + d.Name.IsNull(); + d.Age.IsEqualTo(1); + } + finally + { + connection.Execute("drop table #dog"); + } + } + + [Fact] + public void TestSchemaChangedViaFirstOrDefault() + { + connection.Execute("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); + try + { + var d = connection.QueryFirstOrDefault("select * from #dog"); + d.Name.IsEqualTo("Alf"); + d.Age.IsEqualTo(1); + connection.Execute("alter table #dog drop column Name"); + d = connection.QueryFirstOrDefault("select * from #dog"); + d.Name.IsNull(); + d.Age.IsEqualTo(1); + } + finally + { + connection.Execute("drop table #dog"); + } + } + + [Fact] + public void Test_Single_First_Default() + { + var sql = "select 0 where 1 = 0;"; // no rows + try { connection.QueryFirst(sql); Assert.Fail("QueryFirst, 0"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains no elements"); } + try { connection.QuerySingle(sql); Assert.Fail("QuerySingle, 0"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains no elements"); } + connection.QueryFirstOrDefault(sql).IsEqualTo(0); + connection.QuerySingleOrDefault(sql).IsEqualTo(0); + + sql = "select 1;"; // one row + connection.QueryFirst(sql).IsEqualTo(1); + connection.QuerySingle(sql).IsEqualTo(1); + connection.QueryFirstOrDefault(sql).IsEqualTo(1); + connection.QuerySingleOrDefault(sql).IsEqualTo(1); + + sql = "select 2 union select 3 order by 1;"; // two rows + connection.QueryFirst(sql).IsEqualTo(2); + try { connection.QuerySingle(sql); Assert.Fail("QuerySingle, 2"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains more than one element"); } + connection.QueryFirstOrDefault(sql).IsEqualTo(2); + try { connection.QuerySingleOrDefault(sql); Assert.Fail("QuerySingleOrDefault, 2"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains more than one element"); } + } + + [Fact] + public void TestStrings() + { + connection.Query(@"select 'a' a union select 'b'") + .IsSequenceEqualTo(new[] { "a", "b" }); + } + + // see https://stackoverflow.com/questions/16726709/string-format-with-sql-wildcard-causing-dapper-query-to-break + [Fact] + public void CheckComplexConcat() + { + const string end_wildcard = @" +SELECT * FROM #users16726709 +WHERE (first_name LIKE CONCAT(@search_term, '%') OR last_name LIKE CONCAT(@search_term, '%'));"; + + const string both_wildcards = @" +SELECT * FROM #users16726709 +WHERE (first_name LIKE CONCAT('%', @search_term, '%') OR last_name LIKE CONCAT('%', @search_term, '%'));"; + + const string formatted = @" +SELECT * FROM #users16726709 +WHERE (first_name LIKE {0} OR last_name LIKE {0});"; + + const string use_end_only = @"CONCAT(@search_term, '%')"; + const string use_both = @"CONCAT('%', @search_term, '%')"; + + // if true, slower query due to not being able to use indices, but will allow searching inside strings + const bool allow_start_wildcards = false; + + string query = string.Format(formatted, allow_start_wildcards ? use_both : use_end_only); + const string term = "F"; // the term the user searched for + + connection.Execute(@"create table #users16726709 (first_name varchar(200), last_name varchar(200)) +insert #users16726709 values ('Fred','Bloggs') insert #users16726709 values ('Tony','Farcus') insert #users16726709 values ('Albert','TenoF')"); + + // Using Dapper + connection.Query(end_wildcard, new { search_term = term }).Count().IsEqualTo(2); + connection.Query(both_wildcards, new { search_term = term }).Count().IsEqualTo(3); + connection.Query(query, new { search_term = term }).Count().IsEqualTo(2); + } + + [Fact] + public void TestExtraFields() + { + var guid = Guid.NewGuid(); + var dog = connection.Query("select '' as Extra, 1 as Age, 0.1 as Name1 , Id = @id", new { id = guid }); + + dog.Count().IsEqualTo(1); + dog.First().Age.IsEqualTo(1); + dog.First().Id.IsEqualTo(guid); + } + + [Fact] + public void TestStrongType() + { + var guid = Guid.NewGuid(); + var dog = connection.Query("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid }); + + dog.Count().IsEqualTo(1); + dog.First().Age.IsNull(); + dog.First().Id.IsEqualTo(guid); + } + + [Fact] + public void TestSimpleNull() + { + connection.Query("select null").First().IsNull(); + } + + [Fact] + public void TestExpando() + { + var rows = connection.Query("select 1 A, 2 B union all select 3, 4").ToList(); + + ((int)rows[0].A).IsEqualTo(1); + ((int)rows[0].B).IsEqualTo(2); + ((int)rows[1].A).IsEqualTo(3); + ((int)rows[1].B).IsEqualTo(4); + } + + [Fact] + public void TestStringList() + { + connection.Query("select * from (select 'a' as x union all select 'b' union all select 'c') as T where x in @strings", new { strings = new[] { "a", "b", "c" } }) + .IsSequenceEqualTo(new[] { "a", "b", "c" }); + + connection.Query("select * from (select 'a' as x union all select 'b' union all select 'c') as T where x in @strings", new { strings = new string[0] }) + .IsSequenceEqualTo(new string[0]); + } + + [Fact] + public void TestExecuteCommand() + { + connection.Execute(@" + set nocount on + create table #t(i int) + set nocount off + insert #t + select @a a union all select @b + set nocount on + drop table #t", new { a = 1, b = 2 }).IsEqualTo(2); + } + + [Fact] + public void TestExecuteMultipleCommand() + { + connection.Execute("create table #t(i int)"); + try + { + int tally = connection.Execute(@"insert #t (i) values(@a)", new[] { new { a = 1 }, new { a = 2 }, new { a = 3 }, new { a = 4 } }); + int sum = connection.Query("select sum(i) from #t").First(); + tally.IsEqualTo(4); + sum.IsEqualTo(10); + } + finally + { + connection.Execute("drop table #t"); + } + } + + private class Student + { + public string Name { get; set; } + public int Age { get; set; } + } + + [Fact] + public void TestExecuteMultipleCommandStrongType() + { + connection.Execute("create table #t(Name nvarchar(max), Age int)"); + try + { + int tally = connection.Execute(@"insert #t (Name,Age) values(@Name, @Age)", new List + { + new Student{Age = 1, Name = "sam"}, + new Student{Age = 2, Name = "bob"} + }); + int sum = connection.Query("select sum(Age) from #t").First(); + tally.IsEqualTo(2); + sum.IsEqualTo(3); + } + finally + { + connection.Execute("drop table #t"); + } + } + + [Fact] + public void TestExecuteMultipleCommandObjectArray() + { + connection.Execute("create table #t(i int)"); + int tally = connection.Execute(@"insert #t (i) values(@a)", new object[] { new { a = 1 }, new { a = 2 }, new { a = 3 }, new { a = 4 } }); + int sum = connection.Query("select sum(i) from #t drop table #t").First(); + tally.IsEqualTo(4); + sum.IsEqualTo(10); + } + + private class TestObj + { + public int _internal; + internal int Internal + { + set { _internal = value; } + } + + public int _priv; + private int Priv + { + set { _priv = value; } + } + + private int PrivGet => _priv; + } + + [Fact] + public void TestSetInternal() + { + connection.Query("select 10 as [Internal]").First()._internal.IsEqualTo(10); + } + + [Fact] + public void TestSetPrivate() + { + connection.Query("select 10 as [Priv]").First()._priv.IsEqualTo(10); + } + + [Fact] + public void TestExpandWithNullableFields() + { + var row = connection.Query("select null A, 2 B").Single(); + ((int?)row.A).IsNull(); + ((int?)row.B).IsEqualTo(2); + } + + [Fact] + public void TestEnumeration() + { + var en = connection.Query("select 1 as one union all select 2 as one", buffered: false); + var i = en.GetEnumerator(); + i.MoveNext(); + + bool gotException = false; + try + { + var x = connection.Query("select 1 as one", buffered: false).First(); + } + catch (Exception) + { + gotException = true; + } + + while (i.MoveNext()) + { + } + + // should not exception, since enumerated + en = connection.Query("select 1 as one", buffered: false); + + gotException.IsTrue(); + } + + [Fact] + public void TestEnumerationDynamic() + { + var en = connection.Query("select 1 as one union all select 2 as one", buffered: false); + var i = en.GetEnumerator(); + i.MoveNext(); + + bool gotException = false; + try + { + var x = connection.Query("select 1 as one", buffered: false).First(); + } + catch (Exception) + { + gotException = true; + } + + while (i.MoveNext()) + { + } + + // should not exception, since enumertated + en = connection.Query("select 1 as one", buffered: false); + + gotException.IsTrue(); + } + + [Fact] + public void TestNakedBigInt() + { + const long foo = 12345; + var result = connection.Query("select @foo", new { foo }).Single(); + foo.IsEqualTo(result); + } + + [Fact] + public void TestBigIntMember() + { + const long foo = 12345; + var result = connection.Query(@" +declare @bar table(Value bigint) +insert @bar values (@foo) +select * from @bar", new { foo }).Single(); + result.Value.IsEqualTo(foo); + } + + private class WithBigInt + { + public long Value { get; set; } + } + + [Fact] + public void TestFieldsAndPrivates() + { + var data = connection.Query( + @"select a=1,b=2,c=3,d=4,f='5'").Single(); + data.a.IsEqualTo(1); + data.GetB().IsEqualTo(2); + data.c.IsEqualTo(3); + data.GetD().IsEqualTo(4); + data.e.IsEqualTo(5); + } + + private class TestFieldCaseAndPrivatesEntity + { +#pragma warning disable IDE1006 // Naming Styles + public int a { get; set; } + private int b { get; set; } + public int GetB() { return b; } + public int c = 0; + private int d = 0; + public int GetD() { return d; } + public int e { get; set; } + private string f + { + get { return e.ToString(); } + set { e = int.Parse(value); } + } +#pragma warning restore IDE1006 // Naming Styles + } + + private class InheritanceTest1 + { + public string Base1 { get; set; } + public string Base2 { get; private set; } + } + + private class InheritanceTest2 : InheritanceTest1 + { + public string Derived1 { get; set; } + public string Derived2 { get; private set; } + } + + [Fact] + public void TestInheritance() + { + // Test that inheritance works. + var list = connection.Query("select 'One' as Derived1, 'Two' as Derived2, 'Three' as Base1, 'Four' as Base2"); + list.First().Derived1.IsEqualTo("One"); + list.First().Derived2.IsEqualTo("Two"); + list.First().Base1.IsEqualTo("Three"); + list.First().Base2.IsEqualTo("Four"); + } + +#if !COREFX + [Fact] + public void ExecuteReader() + { + var dt = new DataTable(); + dt.Load(connection.ExecuteReader("select 3 as [three], 4 as [four]")); + dt.Columns.Count.IsEqualTo(2); + dt.Columns[0].ColumnName.IsEqualTo("three"); + dt.Columns[1].ColumnName.IsEqualTo("four"); + dt.Rows.Count.IsEqualTo(1); + ((int)dt.Rows[0][0]).IsEqualTo(3); + ((int)dt.Rows[0][1]).IsEqualTo(4); + } +#endif + + [Fact] + public void TestDbString() + { + var obj = connection.Query("select datalength(@a) as a, datalength(@b) as b, datalength(@c) as c, datalength(@d) as d, datalength(@e) as e, datalength(@f) as f", + new + { + a = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = true }, + b = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = false }, + c = new DbString { Value = "abcde", IsFixedLength = false, Length = 10, IsAnsi = true }, + d = new DbString { Value = "abcde", IsFixedLength = false, Length = 10, IsAnsi = false }, + e = new DbString { Value = "abcde", IsAnsi = true }, + f = new DbString { Value = "abcde", IsAnsi = false }, + }).First(); + ((int)obj.a).IsEqualTo(10); + ((int)obj.b).IsEqualTo(20); + ((int)obj.c).IsEqualTo(5); + ((int)obj.d).IsEqualTo(10); + ((int)obj.e).IsEqualTo(5); + ((int)obj.f).IsEqualTo(10); + } + + [Fact] + public void TestDefaultDbStringDbType() + { + var origDefaultStringDbType = DbString.IsAnsiDefault; + try + { + DbString.IsAnsiDefault = true; + var a = new DbString { Value = "abcde" }; + var b = new DbString { Value = "abcde", IsAnsi = false }; + a.IsAnsi.IsTrue(); + b.IsAnsi.IsFalse(); + } + finally + { + DbString.IsAnsiDefault = origDefaultStringDbType; + } + } + + [Fact] + public void TestFastExpandoSupportsIDictionary() + { + var row = connection.Query("select 1 A, 'two' B").First() as IDictionary; + row["A"].IsEqualTo(1); + row["B"].IsEqualTo("two"); + } + + [Fact] + public void TestDapperSetsPrivates() + { + connection.Query("select 'one' ShadowInDB").First().Shadow.IsEqualTo(1); + + connection.QueryFirstOrDefault("select 'one' ShadowInDB").Shadow.IsEqualTo(1); + } + + private class PrivateDan + { + public int Shadow { get; set; } + private string ShadowInDB + { + set { Shadow = value == "one" ? 1 : 0; } + } + } + + [Fact] + public void TestUnexpectedDataMessage() + { + string msg = null; + try + { + connection.Query("select count(1) where 1 = @Foo", new WithBizarreData { Foo = new GenericUriParser(GenericUriParserOptions.Default), Bar = 23 }).First(); + } + catch (Exception ex) + { + msg = ex.Message; + } + msg.IsEqualTo("The member Foo of type System.GenericUriParser cannot be used as a parameter value"); + } + + [Fact] + public void TestUnexpectedButFilteredDataMessage() + { + int i = connection.Query("select @Bar", new WithBizarreData { Foo = new GenericUriParser(GenericUriParserOptions.Default), Bar = 23 }).Single(); + + i.IsEqualTo(23); + } + + private class WithBizarreData + { + public GenericUriParser Foo { get; set; } + public int Bar { get; set; } + } + + private class WithCharValue + { + public char Value { get; set; } + public char? ValueNullable { get; set; } + } + + [Fact] + public void TestCharInputAndOutput() + { + const char test = '〠'; + char c = connection.Query("select @c", new { c = test }).Single(); + + c.IsEqualTo(test); + + var obj = connection.Query("select @Value as Value", new WithCharValue { Value = c }).Single(); + + obj.Value.IsEqualTo(test); + } + + [Fact] + public void TestNullableCharInputAndOutputNonNull() + { + char? test = '〠'; + char? c = connection.Query("select @c", new { c = test }).Single(); + + c.IsEqualTo(test); + + var obj = connection.Query("select @ValueNullable as ValueNullable", new WithCharValue { ValueNullable = c }).Single(); + + obj.ValueNullable.IsEqualTo(test); + } + + [Fact] + public void TestNullableCharInputAndOutputNull() + { + char? test = null; + char? c = connection.Query("select @c", new { c = test }).Single(); + + c.IsEqualTo(test); + + var obj = connection.Query("select @ValueNullable as ValueNullable", new WithCharValue { ValueNullable = c }).Single(); + + obj.ValueNullable.IsEqualTo(test); + } + + [Fact] + public void WorkDespiteHavingWrongStructColumnTypes() + { + var hazInt = connection.Query("select cast(1 as bigint) Value").Single(); + hazInt.Value.Equals(1); + } + + private struct CanHazInt + { + public int Value { get; set; } + } + + [Fact] + public void TestInt16Usage() + { + connection.Query("select cast(42 as smallint)").Single().IsEqualTo((short)42); + connection.Query("select cast(42 as smallint)").Single().IsEqualTo((short?)42); + connection.Query("select cast(null as smallint)").Single().IsEqualTo((short?)null); + + connection.Query("select cast(42 as smallint)").Single().IsEqualTo((ShortEnum)42); + connection.Query("select cast(42 as smallint)").Single().IsEqualTo((ShortEnum?)42); + connection.Query("select cast(null as smallint)").Single().IsEqualTo((ShortEnum?)null); + + var row = + connection.Query( + "select cast(1 as smallint) as NonNullableInt16, cast(2 as smallint) as NullableInt16, cast(3 as smallint) as NonNullableInt16Enum, cast(4 as smallint) as NullableInt16Enum") + .Single(); + row.NonNullableInt16.IsEqualTo((short)1); + row.NullableInt16.IsEqualTo((short)2); + row.NonNullableInt16Enum.IsEqualTo(ShortEnum.Three); + row.NullableInt16Enum.IsEqualTo(ShortEnum.Four); + + row = + connection.Query( + "select cast(5 as smallint) as NonNullableInt16, cast(null as smallint) as NullableInt16, cast(6 as smallint) as NonNullableInt16Enum, cast(null as smallint) as NullableInt16Enum") + .Single(); + row.NonNullableInt16.IsEqualTo((short)5); + row.NullableInt16.IsEqualTo((short?)null); + row.NonNullableInt16Enum.IsEqualTo(ShortEnum.Six); + row.NullableInt16Enum.IsEqualTo((ShortEnum?)null); + } + + [Fact] + public void TestInt32Usage() + { + connection.Query("select cast(42 as int)").Single().IsEqualTo((int)42); + connection.Query("select cast(42 as int)").Single().IsEqualTo((int?)42); + connection.Query("select cast(null as int)").Single().IsEqualTo((int?)null); + + connection.Query("select cast(42 as int)").Single().IsEqualTo((IntEnum)42); + connection.Query("select cast(42 as int)").Single().IsEqualTo((IntEnum?)42); + connection.Query("select cast(null as int)").Single().IsEqualTo((IntEnum?)null); + + var row = + connection.Query( + "select cast(1 as int) as NonNullableInt32, cast(2 as int) as NullableInt32, cast(3 as int) as NonNullableInt32Enum, cast(4 as int) as NullableInt32Enum") + .Single(); + row.NonNullableInt32.IsEqualTo((int)1); + row.NullableInt32.IsEqualTo((int)2); + row.NonNullableInt32Enum.IsEqualTo(IntEnum.Three); + row.NullableInt32Enum.IsEqualTo(IntEnum.Four); + + row = + connection.Query( + "select cast(5 as int) as NonNullableInt32, cast(null as int) as NullableInt32, cast(6 as int) as NonNullableInt32Enum, cast(null as int) as NullableInt32Enum") + .Single(); + row.NonNullableInt32.IsEqualTo((int)5); + row.NullableInt32.IsEqualTo((int?)null); + row.NonNullableInt32Enum.IsEqualTo(IntEnum.Six); + row.NullableInt32Enum.IsEqualTo((IntEnum?)null); + } + + public class WithInt16Values + { + public short NonNullableInt16 { get; set; } + public short? NullableInt16 { get; set; } + public ShortEnum NonNullableInt16Enum { get; set; } + public ShortEnum? NullableInt16Enum { get; set; } + } + + public class WithInt32Values + { + public int NonNullableInt32 { get; set; } + public int? NullableInt32 { get; set; } + public IntEnum NonNullableInt32Enum { get; set; } + public IntEnum? NullableInt32Enum { get; set; } + } + + public enum IntEnum : int + { + Zero = 0, One = 1, Two = 2, Three = 3, Four = 4, Five = 5, Six = 6 + } + + [Fact] + public void Issue_40_AutomaticBoolConversion() + { + var user = connection.Query("select UserId=1,Email='abc',Password='changeme',Active=cast(1 as tinyint)").Single(); + user.Active.IsTrue(); + user.UserID.IsEqualTo(1); + user.Email.IsEqualTo("abc"); + user.Password.IsEqualTo("changeme"); + } + + public class Issue40_User + { + public Issue40_User() + { + Email = Password = string.Empty; + } + + public int UserID { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public bool Active { get; set; } + } + + [Fact] + public void ExecuteFromClosed() + { + using (var conn = GetClosedConnection()) + { + conn.Execute("-- nop"); + conn.State.IsEqualTo(ConnectionState.Closed); + } + } + + [Fact] + public void ExecuteInvalidFromClosed() + { + using (var conn = GetClosedConnection()) + { + try + { + conn.Execute("nop"); + false.IsEqualTo(true); // shouldn't have got here + } + catch + { + conn.State.IsEqualTo(ConnectionState.Closed); + } + } + } + + [Fact] + public void QueryFromClosed() + { + using (var conn = GetClosedConnection()) + { + var i = conn.Query("select 1").Single(); + conn.State.IsEqualTo(ConnectionState.Closed); + i.IsEqualTo(1); + } + } + + [Fact] + public void QueryInvalidFromClosed() + { + using (var conn = GetClosedConnection()) + { + try + { + conn.Query("select gibberish").Single(); + false.IsEqualTo(true); // shouldn't have got here + } + catch + { + conn.State.IsEqualTo(ConnectionState.Closed); + } + } + } + + [Fact] + public void TestDynamicMutation() + { + var obj = connection.Query("select 1 as [a], 2 as [b], 3 as [c]").Single(); + ((int)obj.a).IsEqualTo(1); + IDictionary dict = obj; + Assert.Equals(3, dict.Count); + Assert.IsTrue(dict.Remove("a")); + Assert.IsFalse(dict.Remove("d")); + Assert.Equals(2, dict.Count); + dict.Add("d", 4); + Assert.Equals(3, dict.Count); + Assert.Equals("b,c,d", string.Join(",", dict.Keys.OrderBy(x => x))); + Assert.Equals("2,3,4", string.Join(",", dict.OrderBy(x => x.Key).Select(x => x.Value))); + + Assert.Equals(2, (int)obj.b); + Assert.Equals(3, (int)obj.c); + Assert.Equals(4, (int)obj.d); + try + { + ((int)obj.a).IsEqualTo(1); + throw new InvalidOperationException("should have thrown"); + } + catch (RuntimeBinderException) + { + // pass + } + } + + [Fact] + public void TestIssue131() + { + var results = connection.Query( + "SELECT 1 Id, 'Mr' Title, 'John' Surname, 4 AddressCount", + (person, addressCount) => person, + splitOn: "AddressCount" + ).FirstOrDefault(); + + var asDict = (IDictionary)results; + + asDict.ContainsKey("Id").IsEqualTo(true); + asDict.ContainsKey("Title").IsEqualTo(true); + asDict.ContainsKey("Surname").IsEqualTo(true); + asDict.ContainsKey("AddressCount").IsEqualTo(false); + } + + // see https://stackoverflow.com/questions/13127886/dapper-returns-null-for-singleordefaultdatediff + [Fact] + public void TestNullFromInt_NoRows() + { + var result = connection.Query( // case with rows + "select DATEDIFF(day, GETUTCDATE(), @date)", new { date = DateTime.UtcNow.AddDays(20) }) + .SingleOrDefault(); + result.IsEqualTo(20); + + result = connection.Query( // case without rows + "select DATEDIFF(day, GETUTCDATE(), @date) where 1 = 0", new { date = DateTime.UtcNow.AddDays(20) }) + .SingleOrDefault(); + result.IsEqualTo(0); // zero rows; default of int over zero rows is zero + } + + [Fact] + public void TestDapperTableMetadataRetrieval() + { + // Test for a bug found in CS 51509960 where the following sequence would result in an InvalidOperationException being + // thrown due to an attempt to access a disposed of DataReader: + // + // - Perform a dynamic query that yields no results + // - Add data to the source of that query + // - Perform a the same query again + connection.Execute("CREATE TABLE #sut (value varchar(10) NOT NULL PRIMARY KEY)"); + connection.Query("SELECT value FROM #sut").IsSequenceEqualTo(Enumerable.Empty()); + + connection.Execute("INSERT INTO #sut (value) VALUES ('test')").IsEqualTo(1); + var result = connection.Query("SELECT value FROM #sut"); + + var first = result.First(); + ((string)first.value).IsEqualTo("test"); + } + + [Fact] + public void DbStringAnsi() + { + var a = connection.Query("select datalength(@x)", + new { x = new DbString { Value = "abc", IsAnsi = true } }).Single(); + var b = connection.Query("select datalength(@x)", + new { x = new DbString { Value = "abc", IsAnsi = false } }).Single(); + a.IsEqualTo(3); + b.IsEqualTo(6); + } + + private class HasInt32 + { + public int Value { get; set; } + } + + // https://stackoverflow.com/q/23696254/23354 + [Fact] + public void DownwardIntegerConversion() + { + const string sql = "select cast(42 as bigint) as Value"; + int i = connection.Query(sql).Single().Value; + Assert.IsEqualTo(42, i); + + i = connection.Query(sql).Single(); + Assert.IsEqualTo(42, i); + } + + [Fact] + public void TypeBasedViaDynamic() + { + Type type = Common.GetSomeType(); + + dynamic template = Activator.CreateInstance(type); + dynamic actual = CheetViaDynamic(template, "select @A as [A], @B as [B]", new { A = 123, B = "abc" }); + ((object)actual).GetType().IsEqualTo(type); + int a = actual.A; + string b = actual.B; + a.IsEqualTo(123); + b.IsEqualTo("abc"); + } + + [Fact] + public void TypeBasedViaType() + { + Type type = Common.GetSomeType(); + + dynamic actual = connection.Query(type, "select @A as [A], @B as [B]", new { A = 123, B = "abc" }).FirstOrDefault(); + ((object)actual).GetType().IsEqualTo(type); + int a = actual.A; + string b = actual.B; + a.IsEqualTo(123); + b.IsEqualTo("abc"); + } + + private T CheetViaDynamic(T template, string query, object args) + { + return connection.Query(query, args).SingleOrDefault(); + } + + [Fact] + public void Issue22_ExecuteScalar() + { + int i = connection.ExecuteScalar("select 123"); + i.IsEqualTo(123); + + i = connection.ExecuteScalar("select cast(123 as bigint)"); + i.IsEqualTo(123); + + long j = connection.ExecuteScalar("select 123"); + j.IsEqualTo(123L); + + j = connection.ExecuteScalar("select cast(123 as bigint)"); + j.IsEqualTo(123L); + + int? k = connection.ExecuteScalar("select @i", new { i = default(int?) }); + k.IsNull(); + } + + [Fact] + public void Issue142_FailsNamedStatus() + { + var row1 = connection.Query("select @Status as [Status]", new { Status = StatusType.Started }).Single(); + row1.Status.IsEqualTo(StatusType.Started); + + var row2 = connection.Query("select @Status as [Status]", new { Status = Status.Started }).Single(); + row2.Status.IsEqualTo(Status.Started); + } + + public class Issue142_Status + { + public StatusType Status { get; set; } + } + + public class Issue142_StatusType + { + public Status Status { get; set; } + } + + public enum StatusType : byte + { + NotStarted = 1, Started = 2, Finished = 3 + } + + public enum Status : byte + { + NotStarted = 1, Started = 2, Finished = 3 + } + + [Fact] + public void Issue178_SqlServer() + { + const string sql = @"select count(*) from Issue178"; + try { connection.Execute("drop table Issue178"); } + catch { /* don't care */ } + try { connection.Execute("create table Issue178(id int not null)"); } + catch { /* don't care */ } + // raw ADO.net + var sqlCmd = new SqlCommand(sql, connection); + using (IDataReader reader1 = sqlCmd.ExecuteReader()) + { + Assert.IsTrue(reader1.Read()); + reader1.GetInt32(0).IsEqualTo(0); + Assert.IsFalse(reader1.Read()); + Assert.IsFalse(reader1.NextResult()); + } + + // dapper + using (var reader2 = connection.ExecuteReader(sql)) + { + Assert.IsTrue(reader2.Read()); + reader2.GetInt32(0).IsEqualTo(0); + Assert.IsFalse(reader2.Read()); + Assert.IsFalse(reader2.NextResult()); + } + } + + [Fact] + public void QueryBasicWithoutQuery() + { + int? i = connection.Query("print 'not a query'").FirstOrDefault(); + i.IsNull(); + } + + [Fact] + public void QueryComplexWithoutQuery() + { + var obj = connection.Query("print 'not a query'").FirstOrDefault(); + obj.IsNull(); + } + + [FactLongRunning] + public void Issue263_Timeout() + { + var watch = Stopwatch.StartNew(); + var i = connection.Query("waitfor delay '00:01:00'; select 42;", commandTimeout: 300, buffered: false).Single(); + watch.Stop(); + i.IsEqualTo(42); + var minutes = watch.ElapsedMilliseconds / 1000 / 60; + Assert.IsTrue(minutes >= 0.95 && minutes <= 1.05); + } + + [Fact] + public void SO30435185_InvalidTypeOwner() + { + try + { + const string sql = @" INSERT INTO #XXX + (XXXId, AnotherId, ThirdId, Value, Comment) + VALUES + (@XXXId, @AnotherId, @ThirdId, @Value, @Comment); select @@rowcount as [Foo]"; + + var command = new + { + MyModels = new[] + { + new {XXXId = 1, AnotherId = 2, ThirdId = 3, Value = "abc", Comment = "def" } + } + }; + var parameters = command + .MyModels + .Select(model => new + { + XXXId = model.XXXId, + AnotherId = model.AnotherId, + ThirdId = model.ThirdId, + Value = model.Value, + Comment = model.Comment + }) + .ToArray(); + + var rowcount = (int)connection.Query(sql, parameters).Single().Foo; + rowcount.IsEqualTo(1); + + Assert.Fail(); + } + catch (InvalidOperationException ex) + { + ex.Message.IsEqualTo("An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context"); + } + } + + [Fact] + public async void SO35470588_WrongValuePidValue() + { + // nuke, rebuild, and populate the table + try { connection.Execute("drop table TPTable"); } catch { /* don't care */ } + connection.Execute(@" +create table TPTable (Pid int not null primary key identity(1,1), Value int not null); +insert TPTable (Value) values (2), (568)"); + + // fetch the data using the query in the question, then force to a dictionary + var rows = (await connection.QueryAsync("select * from TPTable").ConfigureAwait(false)) + .ToDictionary(x => x.Pid); + + // check the number of rows + rows.Count.IsEqualTo(2); + + // check row 1 + var row = rows[1]; + row.Pid.IsEqualTo(1); + row.Value.IsEqualTo(2); + + // check row 2 + row = rows[2]; + row.Pid.IsEqualTo(2); + row.Value.IsEqualTo(568); + } + + public class TPTable + { + public int Pid { get; set; } + public int Value { get; set; } + } + + [Fact] + public void GetOnlyProperties() + { + var obj = connection.QuerySingle("select 42 as [Id], 'def' as [Name];"); + obj.Id.IsEqualTo(42); + obj.Name.IsEqualTo("def"); + } + + private class HazGetOnly + { + public int Id { get; } + public string Name { get; } = "abc"; + } + } +} diff --git a/Dapper.Tests/Tests.MultiMap.cs b/Dapper.Tests/MultiMapTests.cs similarity index 76% rename from Dapper.Tests/Tests.MultiMap.cs rename to Dapper.Tests/MultiMapTests.cs index 05b8b1dc..47db9c4c 100644 --- a/Dapper.Tests/Tests.MultiMap.cs +++ b/Dapper.Tests/MultiMapTests.cs @@ -1,5 +1,4 @@ -using Dapper; -using System; +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -7,7 +6,7 @@ namespace Dapper.Tests { - public partial class TestSuite + public class MultiMapTests : TestBase { [Fact] public void ParentChildIdentityAssociations() @@ -30,12 +29,13 @@ public void ParentChildIdentityAssociations() parents[3].Children.Select(c => c.Id).SequenceEqual(new[] { 5 }).IsTrue(); } - class Parent + private class Parent { public int Id { get; set; } public readonly List Children = new List(); } - class Child + + private class Child { public int Id { get; set; } } @@ -43,7 +43,7 @@ class Child [Fact] public void TestMultiMap() { - var createSql = @" + const string createSql = @" create table #Users (Id int, Name varchar(20)) create table #Posts (Id int, OwnerId int, Content varchar(20)) @@ -57,7 +57,7 @@ public void TestMultiMap() connection.Execute(createSql); try { - var sql = + const string sql = @"select * from #Posts p left join #Users u on u.Id = p.OwnerId Order by p.Id"; @@ -78,10 +78,67 @@ public void TestMultiMap() } } + [Fact] + public void TestSchemaChangedMultiMap() + { + connection.Execute("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); + try + { + var tuple = connection.Query>("select * from #dog d1 join #dog d2 on 1=1", Tuple.Create, splitOn: "Age").Single(); + + tuple.Item1.Name.IsEqualTo("Alf"); + tuple.Item1.Age.IsEqualTo(1); + tuple.Item2.Name.IsEqualTo("Alf"); + tuple.Item2.Age.IsEqualTo(1); + + connection.Execute("alter table #dog drop column Name"); + tuple = connection.Query>("select * from #dog d1 join #dog d2 on 1=1", Tuple.Create, splitOn: "Age").Single(); + + tuple.Item1.Name.IsNull(); + tuple.Item1.Age.IsEqualTo(1); + tuple.Item2.Name.IsNull(); + tuple.Item2.Age.IsEqualTo(1); + } + finally + { + connection.Execute("drop table #dog"); + } + } + + [Fact] + public void TestReadMultipleIntegersWithSplitOnAny() + { + connection.Query>( + "select 1,2,3 union all select 4,5,6", Tuple.Create, splitOn: "*") + .IsSequenceEqualTo(new[] { Tuple.Create(1, 2, 3), Tuple.Create(4, 5, 6) }); + } + + private class Multi1 + { + public int Id { get; set; } + } + + private class Multi2 + { + public int Id { get; set; } + } + + [Fact] + public void QueryMultimapFromClosed() + { + using (var conn = GetClosedConnection()) + { + conn.State.IsEqualTo(ConnectionState.Closed); + var i = conn.Query("select 2 as [Id], 3 as [Id]", (x, y) => x.Id + y.Id).Single(); + conn.State.IsEqualTo(ConnectionState.Closed); + i.IsEqualTo(5); + } + } + [Fact] public void TestMultiMapThreeTypesWithGridReader() { - var createSql = @" + const string createSql = @" create table #Users (Id int, Name varchar(20)) create table #Posts (Id int, OwnerId int, Content varchar(20)) create table #Comments (Id int, PostId int, CommentData varchar(20)) @@ -113,7 +170,6 @@ public void TestMultiMapThreeTypesWithGridReader() post2.Comment.Id.IsEqualTo(1); post2.Owner.Id.IsEqualTo(99); - } finally { @@ -121,24 +177,6 @@ public void TestMultiMapThreeTypesWithGridReader() } } - class User - { - public int Id { get; set; } - public string Name { get; set; } - } - class Post - { - public int Id { get; set; } - public User Owner { get; set; } - public string Content { get; set; } - public Comment Comment { get; set; } - } - class Comment - { - public int Id { get; set; } - public string CommentData { get; set; } - } - [Fact] public void TestMultiMapperIsNotConfusedWithUnorderedCols() { @@ -150,25 +188,10 @@ public void TestMultiMapperIsNotConfusedWithUnorderedCols() result.Item2.Name.IsEqualTo("a"); } - class Foo1 - { -#pragma warning disable 0649 - public int Id; -#pragma warning restore 0649 - public int BarId { get; set; } - } - class Bar1 - { -#pragma warning disable 0649 - public int BarId; -#pragma warning restore 0649 - public string Name { get; set; } - } - [Fact] public void TestMultiMapDynamic() { - var createSql = @" + const string createSql = @" create table #Users (Id int, Name varchar(20)) create table #Posts (Id int, OwnerId int, Content varchar(20)) @@ -181,7 +204,7 @@ public void TestMultiMapDynamic() "; connection.Execute(createSql); - var sql = + const string sql = @"select * from #Posts p left join #Users u on u.Id = p.OwnerId Order by p.Id"; @@ -201,9 +224,9 @@ public void TestMultiMapDynamic() } [Fact] - public void TestMultiMapWithSplit() // http://stackoverflow.com/q/6056778/23354 + public void TestMultiMapWithSplit() // https://stackoverflow.com/q/6056778/23354 { - var sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name"; + const string sql = @"select 1 as id, 'abc' as name, 2 as id, 'def' as name"; var product = connection.Query(sql, (prod, cat) => { prod.Category = cat; @@ -217,9 +240,9 @@ public void TestMultiMapWithSplit() // http://stackoverflow.com/q/6056778/23354 } [Fact] - public void TestMultiMapWithSplitWithNullValue() // http://stackoverflow.com/q/10744728/449906 + public void TestMultiMapWithSplitWithNullValue() // https://stackoverflow.com/q/10744728/449906 { - var sql = @"select 1 as id, 'abc' as name, NULL as description, 'def' as name"; + const string sql = @"select 1 as id, 'abc' as name, NULL as description, 'def' as name"; var product = connection.Query(sql, (prod, cat) => { prod.Category = cat; @@ -232,9 +255,9 @@ public void TestMultiMapWithSplitWithNullValue() // http://stackoverflow.com/q/1 } [Fact] - public void TestMultiMapWithSplitWithNullValueAndSpoofColumn() // http://stackoverflow.com/q/10744728/449906 + public void TestMultiMapWithSplitWithNullValueAndSpoofColumn() // https://stackoverflow.com/q/10744728/449906 { - var sql = @"select 1 as id, 'abc' as name, 1 as spoof, NULL as description, 'def' as name"; + const string sql = @"select 1 as id, 'abc' as name, 1 as spoof, NULL as description, 'def' as name"; var product = connection.Query(sql, (prod, cat) => { prod.Category = cat; @@ -249,23 +272,10 @@ public void TestMultiMapWithSplitWithNullValueAndSpoofColumn() // http://stackov product.Category.Description.IsNull(); } - class Product - { - public int Id { get; set; } - public string Name { get; set; } - public Category Category { get; set; } - } - class Category - { - public int Id { get; set; } - public string Name { get; set; } - public string Description { get; set; } - } - [Fact] public void TestMultiMappingVariations() { - var sql = @"select 1 as Id, 'a' as Content, 2 as Id, 'b' as Content, 3 as Id, 'c' as Content, 4 as Id, 'd' as Content, 5 as Id, 'e' as Content"; + const string sql = @"select 1 as Id, 'a' as Content, 2 as Id, 'b' as Content, 3 as Id, 'c' as Content, 4 as Id, 'd' as Content, 5 as Id, 'e' as Content"; var order = connection.Query(sql, (o, owner, creator) => { o.Owner = owner; o.Creator = creator; return o; }).First(); @@ -307,18 +317,19 @@ public void TestMultiMappingVariations() Assert.IsEqualTo(order.E.Content, "e"); } - class UserWithConstructor + private class UserWithConstructor { public UserWithConstructor(int id, string name) { Ident = id; FullName = name; } + public int Ident { get; set; } public string FullName { get; set; } } - class PostWithConstructor + private class PostWithConstructor { public PostWithConstructor(int id, int ownerid, string content) { @@ -335,7 +346,7 @@ public PostWithConstructor(int id, int ownerid, string content) [Fact] public void TestMultiMapWithConstructor() { - var createSql = @" + const string createSql = @" create table #Users (Id int, Name varchar(20)) create table #Posts (Id int, OwnerId int, Content varchar(20)) @@ -348,7 +359,7 @@ public void TestMultiMapWithConstructor() connection.Execute(createSql); try { - string sql = @"select * from #Posts p + const string sql = @"select * from #Posts p left join #Users u on u.Id = p.OwnerId Order by p.Id"; PostWithConstructor[] data = connection.Query(sql, (post, user) => { post.Owner = user; return post; }).ToArray(); @@ -367,26 +378,11 @@ public void TestMultiMapWithConstructor() } } - class ReviewBoard - { - public int Id { get; set; } - public string Name { get; set; } - public User User1 { get; set; } - public User User2 { get; set; } - public User User3 { get; set; } - public User User4 { get; set; } - public User User5 { get; set; } - public User User6 { get; set; } - public User User7 { get; set; } - public User User8 { get; set; } - public User User9 { get; set; } - } - [Fact] public void TestMultiMapArbitraryMaps() { // please excuse the trite example, but it is easier to follow than a more real-world one - var createSql = @" + const string createSql = @" create table #ReviewBoards (Id int, Name varchar(20), User1Id int, User2Id int, User3Id int, User4Id int, User5Id int, User6Id int, User7Id int, User8Id int, User9Id int) create table #Users (Id int, Name varchar(20)) @@ -405,7 +401,7 @@ public void TestMultiMapArbitraryMaps() connection.Execute(createSql); try { - var sql = @" + const string sql = @" select rb.Id, rb.Name, u1.*, u2.*, u3.*, u4.*, u5.*, u6.*, u7.*, u8.*, u9.* @@ -471,7 +467,7 @@ public void TestMultiMapArbitraryMaps() [Fact] public void TestMultiMapGridReader() { - var createSql = @" + const string createSql = @" create table #Users (Id int, Name varchar(20)) create table #Posts (Id int, OwnerId int, Content varchar(20)) @@ -484,7 +480,7 @@ public void TestMultiMapGridReader() "; connection.Execute(createSql); - var sql = + const string sql = @"select p.*, u.Id, u.Name + '0' Name from #Posts p left join #Users u on u.Id = p.OwnerId Order by p.Id @@ -515,7 +511,7 @@ Order by p.Id [Fact] public void TestFlexibleMultiMapping() { - var sql = + const string sql = @"select 1 as PersonId, 'bob' as Name, 2 as AddressId, 'abc street' as Name, 1 as PersonId, @@ -536,7 +532,7 @@ public void TestFlexibleMultiMapping() [Fact] public void TestMultiMappingWithSplitOnSpaceBetweenCommas() { - var sql = @"select + const string sql = @"select 1 as PersonId, 'bob' as Name, 2 as AddressId, 'abc street' as Name, 1 as PersonId, 3 as Id, 'fred' as Name @@ -553,21 +549,7 @@ public void TestMultiMappingWithSplitOnSpaceBetweenCommas() personWithAddress.Item3.Name.IsEqualTo("fred"); } - class Person - { - public int PersonId { get; set; } - public string Name { get; set; } - public string Occupation { get; private set; } - public int NumberOfLegs = 2; - public Address Address { get; set; } - } - class Address - { - public int AddressId { get; set; } - public string Name { get; set; } - public int PersonId { get; set; } - } - class Extra + private class Extra { public int Id { get; set; } public string Name { get; set; } @@ -576,7 +558,7 @@ class Extra [Fact] public void TestMultiMappingWithNonReturnedProperty() { - var sql = @"select + const string sql = @"select 1 as PostId, 'Title' as Title, 2 as BlogId, 'Blog' as Title"; var postWithBlog = connection.Query(sql, @@ -592,17 +574,85 @@ public void TestMultiMappingWithNonReturnedProperty() postWithBlog.Blog.Title.IsEqualTo("Blog"); } - class Post_DupeProp + private class Post_DupeProp { public int PostId { get; set; } public string Title { get; set; } public int BlogId { get; set; } public Blog_DupeProp Blog { get; set; } } - class Blog_DupeProp + + private class Blog_DupeProp { public int BlogId { get; set; } public string Title { get; set; } } + + // see https://stackoverflow.com/questions/16955357/issue-about-dapper + [Fact] + public void TestSplitWithMissingMembers() + { + var result = connection.Query( + @"select 123 as ID, 'abc' as Title, + cast('01 Feb 2013' as datetime) as CreateDate, + 'ghi' as Name, 'def' as Phone", + (T, P) => { T.Author = P; return T; }, + null, null, true, "ID,Name").Single(); + + result.ID.Equals(123); + result.Title.Equals("abc"); + result.CreateDate.Equals(new DateTime(2013, 2, 1)); + result.Name.IsNull(); + result.Content.IsNull(); + + result.Author.Phone.Equals("def"); + result.Author.Name.Equals("ghi"); + result.Author.ID.Equals(0); + result.Author.Address.IsNull(); + } + + public class Profile + { + public int ID { get; set; } + public string Name { get; set; } + public string Phone { get; set; } + public string Address { get; set; } + //public ExtraInfo Extra { get; set; } + } + + public class Topic + { + public int ID { get; set; } + public string Title { get; set; } + public DateTime CreateDate { get; set; } + public string Content { get; set; } + public int UID { get; set; } + public int TestColum { get; set; } + public string Name { get; set; } + public Profile Author { get; set; } + //public Attachment Attach { get; set; } + } + + [Fact] + public void TestInvalidSplitCausesNiceError() + { + try + { + connection.Query("select 1 A, 2 B, 3 C", (x, y) => x); + } + catch (ArgumentException) + { + // expecting an app exception due to multi mapping being bodged + } + + try + { + connection.Query("select 1 A, 2 B, 3 C", (x, y) => x); + } + catch (ArgumentException) + { + // expecting an app exception due to multi mapping being bodged + } + } } } diff --git a/Dapper.Tests/Tests.Nulls.cs b/Dapper.Tests/NullTests.cs similarity index 93% rename from Dapper.Tests/Tests.Nulls.cs rename to Dapper.Tests/NullTests.cs index 606e4c1e..9199cba0 100644 --- a/Dapper.Tests/Tests.Nulls.cs +++ b/Dapper.Tests/NullTests.cs @@ -2,18 +2,20 @@ using System.Linq; namespace Dapper.Tests { - public partial class TestSuite + public class NullTests : TestBase { [Fact] public void TestNullableDefault() { TestNullable(false); } + [Fact] public void TestNullableApplyNulls() { TestNullable(true); } + private void TestNullable(bool applyNulls) { bool oldSetting = SqlMapper.Settings.ApplyNullValues; @@ -62,7 +64,7 @@ insert @data (Id, A, B, C, D, E) values } } - class NullTestClass + private class NullTestClass { public int Id { get; set; } public int A { get; set; } @@ -70,7 +72,7 @@ class NullTestClass public string C { get; set; } public AnEnum D { get; set; } public AnEnum? E { get; set; } - + public NullTestClass() { A = 2; diff --git a/Dapper.Tests/Tests.Parameters.cs b/Dapper.Tests/ParameterTests.cs similarity index 70% rename from Dapper.Tests/Tests.Parameters.cs rename to Dapper.Tests/ParameterTests.cs index fff94e35..5864f175 100644 --- a/Dapper.Tests/Tests.Parameters.cs +++ b/Dapper.Tests/ParameterTests.cs @@ -1,13 +1,15 @@ -using Dapper; -using System; +using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Dynamic; using System.Linq; using Xunit; +using System.Globalization; +using System.Text.RegularExpressions; #if ENTITY_FRAMEWORK using System.Data.Entity.Spatial; @@ -16,9 +18,9 @@ namespace Dapper.Tests { - public partial class TestSuite + public class ParameterTests : TestBase { - public class DbParams : Dapper.SqlMapper.IDynamicParameters, IEnumerable + public class DbParams : SqlMapper.IDynamicParameters, IEnumerable { private readonly List parameters = new List(); public IEnumerator GetEnumerator() { return parameters.GetEnumerator(); } @@ -27,29 +29,28 @@ public void Add(IDbDataParameter value) { parameters.Add(value); } - void Dapper.SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, - Dapper.SqlMapper.Identity identity) + + void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity) { foreach (IDbDataParameter parameter in parameters) command.Parameters.Add(parameter); - } } - class IntDynamicParam : Dapper.SqlMapper.IDynamicParameters + private class IntDynamicParam : SqlMapper.IDynamicParameters { - IEnumerable numbers; + private readonly IEnumerable numbers; public IntDynamicParam(IEnumerable numbers) { this.numbers = numbers; } - public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity) + public void AddParameters(IDbCommand command, SqlMapper.Identity identity) { var sqlCommand = (SqlCommand)command; sqlCommand.CommandType = CommandType.StoredProcedure; - List number_list = new List(); + var number_list = new List(); // Create an SqlMetaData object that describes our table type. Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) }; @@ -57,7 +58,7 @@ public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity foreach (int n in numbers) { // Create a new record, using the metadata array above. - Microsoft.SqlServer.Server.SqlDataRecord rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition); + var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition); rec.SetInt32(0, n); // Set the value. number_list.Add(rec); // Add it to the list. } @@ -67,13 +68,12 @@ public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity p.Direction = ParameterDirection.Input; p.TypeName = "int_list_type"; p.Value = number_list; - } } - class IntCustomParam : Dapper.SqlMapper.ICustomQueryParameter + private class IntCustomParam : SqlMapper.ICustomQueryParameter { - IEnumerable numbers; + private readonly IEnumerable numbers; public IntCustomParam(IEnumerable numbers) { this.numbers = numbers; @@ -84,7 +84,7 @@ public void AddParameter(IDbCommand command, string name) var sqlCommand = (SqlCommand)command; sqlCommand.CommandType = CommandType.StoredProcedure; - List number_list = new List(); + var number_list = new List(); // Create an SqlMetaData object that describes our table type. Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) }; @@ -92,7 +92,7 @@ public void AddParameter(IDbCommand command, string name) foreach (int n in numbers) { // Create a new record, using the metadata array above. - Microsoft.SqlServer.Server.SqlDataRecord rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition); + var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition); rec.SetInt32(0, n); // Set the value. number_list.Add(rec); // Add it to the list. } @@ -105,6 +105,117 @@ public void AddParameter(IDbCommand command, string name) } } + /* TODO: + * + public void TestMagicParam() + { + // magic params allow you to pass in single params without using an anon class + // this test fails for now, but I would like to support a single param by parsing the sql with regex and remapping. + + var first = connection.Query("select @a as a", 1).First(); + Assert.IsEqualTo(first.a, 1); + } + * */ + + [Fact] + public void TestDoubleParam() + { + connection.Query("select @d", new { d = 0.1d }).First() + .IsEqualTo(0.1d); + } + + [Fact] + public void TestBoolParam() + { + connection.Query("select @b", new { b = false }).First() + .IsFalse(); + } + + // http://code.google.com/p/dapper-dot-net/issues/detail?id=70 + // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time + + [Fact] + public void TestTimeSpanParam() + { + connection.Query("select @ts", new { ts = TimeSpan.FromMinutes(42) }).First() + .IsEqualTo(TimeSpan.FromMinutes(42)); + } + + [Fact] + public void PassInIntArray() + { + connection.Query("select * from (select 1 as Id union all select 2 union all select 3) as X where Id in @Ids", new { Ids = new int[] { 1, 2, 3 }.AsEnumerable() }) + .IsSequenceEqualTo(new[] { 1, 2, 3 }); + } + + [Fact] + public void PassInEmptyIntArray() + { + connection.Query("select * from (select 1 as Id union all select 2 union all select 3) as X where Id in @Ids", new { Ids = new int[0] }) + .IsSequenceEqualTo(new int[0]); + } + + [Fact] + public void TestExecuteCommandWithHybridParameters() + { + var p = new DynamicParameters(new { a = 1, b = 2 }); + p.Add("c", dbType: DbType.Int32, direction: ParameterDirection.Output); + connection.Execute(@"set @c = @a + @b", p); + p.Get("@c").IsEqualTo(3); + } + + [Fact] + public void GuidIn_SO_24177902() + { + // invent and populate + Guid a = Guid.NewGuid(), b = Guid.NewGuid(), c = Guid.NewGuid(), d = Guid.NewGuid(); + connection.Execute("create table #foo (i int, g uniqueidentifier)"); + connection.Execute("insert #foo(i,g) values(@i,@g)", + new[] { new { i = 1, g = a }, new { i = 2, g = b }, + new { i = 3, g = c },new { i = 4, g = d }}); + + // check that rows 2&3 yield guids b&c + var guids = connection.Query("select g from #foo where i in (2,3)").ToArray(); + guids.Length.Equals(2); + guids.Contains(a).Equals(false); + guids.Contains(b).Equals(true); + guids.Contains(c).Equals(true); + guids.Contains(d).Equals(false); + + // in query on the guids + var rows = connection.Query("select * from #foo where g in @guids order by i", new { guids }) + .Select(row => new { i = (int)row.i, g = (Guid)row.g }).ToArray(); + rows.Length.Equals(2); + rows[0].i.Equals(2); + rows[0].g.Equals(b); + rows[1].i.Equals(3); + rows[1].g.Equals(c); + } + + [FactUnlessCaseSensitiveDatabase] + public void TestParameterInclusionNotSensitiveToCurrentCulture() + { + // note this might fail if your database server is case-sensitive + CultureInfo current = ActiveCulture; + try + { + ActiveCulture = new CultureInfo("tr-TR"); + + connection.Query("select @pid", new { PId = 1 }).Single(); + } + finally + { + ActiveCulture = current; + } + } + + [Fact] + public void TestMassiveStrings() + { + var str = new string('X', 20000); + connection.Query("select @a", new { a = str }).First() + .IsEqualTo(str); + } #if !COREFX [Fact] @@ -120,7 +231,6 @@ public void TestTVPWithAnonymousObject() nums[1].IsEqualTo(2); nums[2].IsEqualTo(3); nums.Count.IsEqualTo(3); - } finally { @@ -149,7 +259,6 @@ public void TestTVP() nums[1].IsEqualTo(2); nums[2].IsEqualTo(3); nums.Count.IsEqualTo(3); - } finally { @@ -164,22 +273,22 @@ public void TestTVP() } } - class DynamicParameterWithIntTVP : Dapper.DynamicParameters, Dapper.SqlMapper.IDynamicParameters + private class DynamicParameterWithIntTVP : DynamicParameters, SqlMapper.IDynamicParameters { - IEnumerable numbers; + private readonly IEnumerable numbers; public DynamicParameterWithIntTVP(IEnumerable numbers) { this.numbers = numbers; } - public new void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity) + public new void AddParameters(IDbCommand command, SqlMapper.Identity identity) { base.AddParameters(command, identity); var sqlCommand = (SqlCommand)command; sqlCommand.CommandType = CommandType.StoredProcedure; - List number_list = new List(); + var number_list = new List(); // Create an SqlMetaData object that describes our table type. Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) }; @@ -187,7 +296,7 @@ public DynamicParameterWithIntTVP(IEnumerable numbers) foreach (int n in numbers) { // Create a new record, using the metadata array above. - Microsoft.SqlServer.Server.SqlDataRecord rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition); + var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition); rec.SetInt32(0, n); // Set the value. number_list.Add(rec); // Add it to the list. } @@ -197,10 +306,9 @@ public DynamicParameterWithIntTVP(IEnumerable numbers) p.Direction = ParameterDirection.Input; p.TypeName = "int_list_type"; p.Value = number_list; - } } - + [Fact] public void TestTVPWithAdditionalParams() { @@ -221,7 +329,6 @@ public void TestTVPWithAdditionalParams() Assert.IsEqualTo("stringParam", result.stringParam); Assert.IsEqualTo(new DateTime(2012, 1, 1), result.dateParam); } - } finally { @@ -240,11 +347,11 @@ public void TestTVPWithAdditionalParams() public void DataTableParameters() { try { connection.Execute("drop proc #DataTableParameters"); } - catch { } + catch { /* don't care */ } try { connection.Execute("drop table #DataTableParameters"); } - catch { } + catch { /* don't care */ } try { connection.Execute("drop type MyTVPType"); } - catch { } + catch { /* don't care */ } connection.Execute("create type MyTVPType as table (id int)"); connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids"); @@ -266,36 +373,38 @@ public void DataTableParameters() ex.Message.Equals("The table type parameter 'ids' must have a valid type name."); } } - + [Fact] public void SO29533765_DataTableParametersViaDynamicParameters() { - try { connection.Execute("drop proc #DataTableParameters"); } catch { } - try { connection.Execute("drop table #DataTableParameters"); } catch { } - try { connection.Execute("drop type MyTVPType"); } catch { } + try { connection.Execute("drop proc #DataTableParameters"); } catch { /* don't care */ } + try { connection.Execute("drop table #DataTableParameters"); } catch { /* don't care */ } + try { connection.Execute("drop type MyTVPType"); } catch { /* don't care */ } connection.Execute("create type MyTVPType as table (id int)"); connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids"); var table = new DataTable { TableName="MyTVPType", Columns = { { "id", typeof(int) } }, Rows = { { 1 }, { 2 }, { 3 } } }; table.SetTypeName(table.TableName); // per SO29533765 - IDictionary args = new Dictionary(); - args.Add("ids", table); + IDictionary args = new Dictionary + { + ["ids"] = table + }; int count = connection.Query("#DataTableParameters", args, commandType: CommandType.StoredProcedure).First(); count.IsEqualTo(3); count = connection.Query("select count(1) from @ids", args).First(); count.IsEqualTo(3); } - + [Fact] public void SO26468710_InWithTVPs() { // this is just to make it re-runnable; normally you only do this once try { connection.Execute("drop type MyIdList"); } - catch { } + catch { /* don't care */ } connection.Execute("create type MyIdList as table(id int);"); - DataTable ids = new DataTable + var ids = new DataTable { Columns = { { "id", typeof(int) } }, Rows = { { 1 }, { 3 }, { 5 } } @@ -307,16 +416,16 @@ public void SO26468710_InWithTVPs() select * from @tmp t inner join @ids i on i.id = t.id", new { ids }).Sum(); sum.IsEqualTo(9); } - + [Fact] public void DataTableParametersWithExtendedProperty() { try { connection.Execute("drop proc #DataTableParameters"); } - catch { } + catch { /* don't care */ } try { connection.Execute("drop table #DataTableParameters"); } - catch { } + catch { /* don't care */ } try { connection.Execute("drop type MyTVPType"); } - catch { } + catch { /* don't care */ } connection.Execute("create type MyTVPType as table (id int)"); connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids"); @@ -338,9 +447,7 @@ public void DataTableParametersWithExtendedProperty() ex.Message.Equals("The table type parameter 'ids' must have a valid type name."); } } -#endif -#if !COREFX [Fact] public void SupportInit() { @@ -348,26 +455,85 @@ public void SupportInit() obj.Value.Equals("abc"); obj.Flags.Equals(31); } + + public class WithInit : ISupportInitialize + { + public string Value { get; set; } + public int Flags { get; set; } + + void ISupportInitialize.BeginInit() => Flags++; + + void ISupportInitialize.EndInit() => Flags += 30; + } + + [Fact] + public void SO29596645_TvpProperty() + { + try { connection.Execute("CREATE TYPE SO29596645_ReminderRuleType AS TABLE (id int NOT NULL)"); } + catch { /* don't care */ } + connection.Execute(@"create proc #SO29596645_Proc (@Id int, @Rules SO29596645_ReminderRuleType READONLY) + as begin select @Id + ISNULL((select sum(id) from @Rules), 0); end"); + var obj = new SO29596645_OrganisationDTO(); + int val = connection.Query("#SO29596645_Proc", obj.Rules, commandType: CommandType.StoredProcedure).Single(); + + // 4 + 9 + 7 = 20 + val.IsEqualTo(20); + } + + private class SO29596645_RuleTableValuedParameters : SqlMapper.IDynamicParameters + { + private string parameterName; + + public SO29596645_RuleTableValuedParameters(string parameterName) + { + this.parameterName = parameterName; + } + + public void AddParameters(IDbCommand command, SqlMapper.Identity identity) + { + Console.WriteLine("> AddParameters"); + var lazy = (SqlCommand)command; + lazy.Parameters.AddWithValue("Id", 7); + var table = new DataTable + { + Columns = { { "Id", typeof(int) } }, + Rows = { { 4 }, { 9 } } + }; + lazy.Parameters.AddWithValue("Rules", table); + Console.WriteLine("< AddParameters"); + } + } + + private class SO29596645_OrganisationDTO + { + public SO29596645_RuleTableValuedParameters Rules { get; private set; } + + public SO29596645_OrganisationDTO() + { + Rules = new SO29596645_RuleTableValuedParameters("@Rules"); + } + } #endif #if ENTITY_FRAMEWORK - class HazGeo + private class HazGeo { public int Id { get; set; } public DbGeography Geo { get; set; } public DbGeometry Geometry { get; set; } } - class HazSqlGeo + + private class HazSqlGeo { public int Id { get; set; } public SqlGeography Geo { get; set; } public SqlGeometry Geometry { get; set; } } - + [Fact] public void DBGeography_SO24405645_SO24402424() { - Dapper.EntityFramework.Handlers.Register(); + EntityFramework.Handlers.Register(); connection.Execute("create table #Geo (id int, geo geography, geometry geometry)"); @@ -384,11 +550,11 @@ public void DBGeography_SO24405645_SO24402424() row.Geo.IsNotNull(); row.Geometry.IsNotNull(); } - + [Fact] public void SqlGeography_SO25538154() { - Dapper.SqlMapper.ResetTypeHandlers(); + SqlMapper.ResetTypeHandlers(); connection.Execute("create table #SqlGeo (id int, geo geography, geometry geometry)"); var obj = new HazSqlGeo @@ -404,11 +570,11 @@ public void SqlGeography_SO25538154() row.Geo.IsNotNull(); row.Geometry.IsNotNull(); } - + [Fact] public void NullableSqlGeometry() { - Dapper.SqlMapper.ResetTypeHandlers(); + SqlMapper.ResetTypeHandlers(); connection.Execute("create table #SqlNullableGeo (id int, geometry geometry null)"); var obj = new HazSqlGeo @@ -422,7 +588,7 @@ public void NullableSqlGeometry() row.Id.IsEqualTo(1); row.Geometry.IsNull(); } - + [Fact] public void SqlHierarchyId_SO18888911() { @@ -443,36 +609,35 @@ public class HazSqlHierarchy #endif -#if OLEDB - - // see http://stackoverflow.com/q/18847510/23354 [Fact] - public void TestOleDbParameters() + public void TestCustomParameters() { - using (var conn = ConnectViaOledb()) - { - var row = conn.Query("select Id = ?, Age = ?", - new { foo = 12, bar = 23 } // these names DO NOT MATTER!!! - ).Single(); - int age = row.Age; - int id = row.Id; - age.IsEqualTo(23); - id.IsEqualTo(12); - } + var args = new DbParams { + new SqlParameter("foo", 123), + new SqlParameter("bar", "abc") + }; + var result = connection.Query("select Foo=@foo, Bar=@bar", args).Single(); + int foo = result.Foo; + string bar = result.Bar; + foo.IsEqualTo(123); + bar.IsEqualTo("abc"); } - System.Data.OleDb.OleDbConnection ConnectViaOledb() + [Fact] + public void TestDynamicParamNullSupport() { - var conn = new System.Data.OleDb.OleDbConnection(OleDbConnectionString); - conn.Open(); - return conn; + var p = new DynamicParameters(); + + p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); + connection.Execute("select @b = null", p); + + p.Get("@b").IsNull(); } -#endif [Fact] public void TestAppendingAnonClasses() { - DynamicParameters p = new DynamicParameters(); + var p = new DynamicParameters(); p.AddDynamicParams(new { A = 1, B = 2 }); p.AddDynamicParams(new { C = 3, D = 4 }); @@ -489,11 +654,11 @@ public void TestAppendingADictionary() { var dictionary = new Dictionary { - {"A", 1}, - {"B", "two"} + ["A"] = 1, + ["B"] = "two" }; - DynamicParameters p = new DynamicParameters(); + var p = new DynamicParameters(); p.AddDynamicParams(dictionary); var result = connection.Query("select @A a, @B b", p).Single(); @@ -509,7 +674,7 @@ public void TestAppendingAnExpandoObject() expando.A = 1; expando.B = "two"; - DynamicParameters p = new DynamicParameters(); + var p = new DynamicParameters(); p.AddDynamicParams(expando); var result = connection.Query("select @A a, @B b", p).Single(); @@ -521,7 +686,7 @@ public void TestAppendingAnExpandoObject() [Fact] public void TestAppendingAList() { - DynamicParameters p = new DynamicParameters(); + var p = new DynamicParameters(); var list = new int[] { 1, 2, 3 }; p.AddDynamicParams(new { list }); @@ -535,9 +700,9 @@ public void TestAppendingAList() [Fact] public void TestAppendingAListAsDictionary() { - DynamicParameters p = new DynamicParameters(); + var p = new DynamicParameters(); var list = new int[] { 1, 2, 3 }; - var args = new Dictionary { { "ids", list } }; + var args = new Dictionary { ["ids"] = list }; p.AddDynamicParams(args); var result = connection.Query("select * from (select 1 A union all select 2 union all select 3) X where A in @ids", p).ToList(); @@ -807,7 +972,7 @@ public void SO25069578_DynamicParams_Procs() parameters.Add("foo", "bar"); // parameters = new DynamicParameters(parameters); try { connection.Execute("drop proc SO25069578"); } - catch { } + catch { /* don't care */ } connection.Execute("create proc SO25069578 @foo nvarchar(max) as select @foo as [X]"); var tran = connection.BeginTransaction(); // gist used transaction; behaves the same either way, though var row = connection.Query("SO25069578", parameters, @@ -816,10 +981,15 @@ public void SO25069578_DynamicParams_Procs() row.X.IsEqualTo("bar"); } + public class HazX + { + public string X { get; set; } + } + [Fact] public void SO25297173_DynamicIn() { - var query = @" + const string query = @" declare @table table(value int not null); insert @table values(1); insert @table values(2); @@ -830,7 +1000,7 @@ public void SO25297173_DynamicIn() insert @table values(7); SELECT value FROM @table WHERE value IN @myIds"; var queryParams = new Dictionary { - { "myIds", new [] { 5, 6 } } + ["myIds"] = new [] { 5, 6 } }; var dynamicParams = new DynamicParameters(queryParams); @@ -855,7 +1025,7 @@ public void AllowIDictionaryParameters() { var parameters = new Dictionary { - { "param1", 0 } + ["param1"] = 0 }; connection.Query("SELECT @param1", parameters); @@ -872,6 +1042,7 @@ select @A end"); var item = connection.Query("#TestProcWithIndexer", new ParameterWithIndexer(), commandType: CommandType.StoredProcedure).Single(); } + public class ParameterWithIndexer { public int A { get; set; } @@ -890,14 +1061,24 @@ public void TestMultipleParametersWithIndexer() order.A.IsEqualTo(1); order.B.IsEqualTo(2); } + public class MultipleParametersWithIndexer : MultipleParametersWithIndexerDeclaringType { public int A { get; set; } } + public class MultipleParametersWithIndexerDeclaringType { - public object this[object field] { get { return null; } set { } } - public object this[object field, int index] { get { return null; } set { } } + public object this[object field] + { + get { return null; } + set { } + } + public object this[object field, int index] + { + get { return null; } + set { } + } public int B { get; set; } } @@ -915,6 +1096,7 @@ public void Issue182_BindDynamicObjectParametersAndColumns() fromDb.Name.IsEqualTo("T Rex"); ((long)fromDb.Foo).IsEqualTo(123L); } + public class Dyno { public dynamic Id { get; set; } @@ -998,18 +1180,20 @@ public void RunAllStringSplitTestsDisabled() { RunAllStringSplitTests(-1, 1500); } + [FactRequiredCompatibilityLevel(FactRequiredCompatibilityLevelAttribute.SqlServer2016)] public void RunAllStringSplitTestsEnabled() { RunAllStringSplitTests(10, 4500); } + private void RunAllStringSplitTests(int stringSplit, int max = 150) { int oldVal = SqlMapper.Settings.InListStringSplitCount; try { SqlMapper.Settings.InListStringSplitCount = stringSplit; - try { connection.Execute("drop table #splits"); } catch { } + try { connection.Execute("drop table #splits"); } catch { /* don't care */ } int count = connection.QuerySingle("create table #splits (i int not null);" + string.Concat(Enumerable.Range(-max, max * 3).Select(i => $"insert #splits (i) values ({i});")) + "select count(1) from #splits"); @@ -1035,7 +1219,8 @@ private void RunAllStringSplitTests(int stringSplit, int max = 150) SqlMapper.Settings.InListStringSplitCount = oldVal; } } - static void Incr(ref int i) + + private static void Incr(ref int i) { if (i <= 15) i++; else if (i <= 80) i += 5; @@ -1050,15 +1235,97 @@ public void Issue601_InternationalParameterNamesWork() // regular parameter var result = connection.QuerySingle("select @æøå٦", new { æøå٦ = 42 }); result.IsEqualTo(42); + } + + [Fact] + public void TestListExpansionPadding_Enabled() => TestListExpansionPadding(true); + + [Fact] + public void TestListExpansionPadding_Disabled() => TestListExpansionPadding(false); + + private void TestListExpansionPadding(bool enabled) + { + bool oldVal = SqlMapper.Settings.PadListExpansions; + try + { + SqlMapper.Settings.PadListExpansions = enabled; + connection.ExecuteScalar(@" +create table #ListExpansion(id int not null identity(1,1), value int null); +insert #ListExpansion (value) values (null); +declare @loop int = 0; +while (@loop < 12) +begin -- double it + insert #ListExpansion (value) select value from #ListExpansion; + set @loop = @loop + 1; +end + +select count(1) as [Count] from #ListExpansion").IsEqualTo(4096); + + var list = new List(); + int nextId = 1, batchCount; + var rand = new Random(12345); + const int SQL_SERVER_MAX_PARAMS = 2095; + TestListForExpansion(list, enabled); // test while empty + while (list.Count < SQL_SERVER_MAX_PARAMS) + { + try + { + if (list.Count <= 20) batchCount = 1; + else if (list.Count <= 200) batchCount = rand.Next(1, 40); + else batchCount = rand.Next(1, 100); -#if OLEDB - // pseudo-positional - using (var connection = ConnectViaOledb()) + for (int j = 0; j < batchCount && list.Count < SQL_SERVER_MAX_PARAMS; j++) + list.Add(nextId++); + + TestListForExpansion(list, enabled); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failure with {list.Count} items: {ex.Message}", ex); + } + } + } + finally { - int value = connection.QuerySingle("select ?æøå٦?", new { æøå٦ = 42 }); + SqlMapper.Settings.PadListExpansions = oldVal; } -#endif } + private void TestListForExpansion(List list, bool enabled) + { + var row = connection.QuerySingle(@" +declare @hits int, @misses int, @count int; +select @count = count(1) from #ListExpansion; +select @hits = count(1) from #ListExpansion where id in @ids ; +select @misses = count(1) from #ListExpansion where not id in @ids ; +declare @query nvarchar(max) = N' in @ids '; -- ok, I confess to being pleased with this hack ;p +select @hits as [Hits], (@count - @misses) as [Misses], @query as [Query]; +", new { ids = list }); + int hits = row.Hits, misses = row.Misses; + string query = row.Query; + int argCount = Regex.Matches(query, "@ids[0-9]").Count; + int expectedCount = GetExpectedListExpansionCount(list.Count, enabled); + hits.IsEqualTo(list.Count); + misses.IsEqualTo(list.Count); + argCount.IsEqualTo(expectedCount); + } + + private static int GetExpectedListExpansionCount(int count, bool enabled) + { + if (!enabled) return count; + + if (count <= 5 || count > 2070) return count; + + int padFactor; + if (count <= 150) padFactor = 10; + else if (count <= 750) padFactor = 50; + else if (count <= 2000) padFactor = 100; + else if (count <= 2070) padFactor = 10; + else padFactor = 200; + + int blocks = count / padFactor, delta = count % padFactor; + if (delta != 0) blocks++; + return blocks * padFactor; + } } } diff --git a/Dapper.Tests/ProcedureTests.cs b/Dapper.Tests/ProcedureTests.cs new file mode 100644 index 00000000..b485d46b --- /dev/null +++ b/Dapper.Tests/ProcedureTests.cs @@ -0,0 +1,187 @@ +using System; +using System.Data; +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class ProcedureTests : TestBase + { + [Fact] + public void TestProcWithOutParameter() + { + connection.Execute( + @"CREATE PROCEDURE #TestProcWithOutParameter + @ID int output, + @Foo varchar(100), + @Bar int + AS + SET @ID = @Bar + LEN(@Foo)"); + var obj = new + { + ID = 0, + Foo = "abc", + Bar = 4 + }; + var args = new DynamicParameters(obj); + args.Add("ID", 0, direction: ParameterDirection.Output); + connection.Execute("#TestProcWithOutParameter", args, commandType: CommandType.StoredProcedure); + args.Get("ID").IsEqualTo(7); + } + + [Fact] + public void TestProcWithOutAndReturnParameter() + { + connection.Execute( + @"CREATE PROCEDURE #TestProcWithOutAndReturnParameter + @ID int output, + @Foo varchar(100), + @Bar int + AS + SET @ID = @Bar + LEN(@Foo) + RETURN 42"); + var obj = new + { + ID = 0, + Foo = "abc", + Bar = 4 + }; + var args = new DynamicParameters(obj); + args.Add("ID", 0, direction: ParameterDirection.Output); + args.Add("result", 0, direction: ParameterDirection.ReturnValue); + connection.Execute("#TestProcWithOutAndReturnParameter", args, commandType: CommandType.StoredProcedure); + args.Get("ID").IsEqualTo(7); + args.Get("result").IsEqualTo(42); + } + + [Fact] + public void TestIssue17648290() + { + var p = new DynamicParameters(); + const int code = 1, getMessageControlId = 2; + p.Add("@Code", code); + p.Add("@MessageControlID", getMessageControlId); + p.Add("@SuccessCode", dbType: DbType.Int32, direction: ParameterDirection.Output); + p.Add("@ErrorDescription", dbType: DbType.String, direction: ParameterDirection.Output, size: 255); + connection.Execute(@"CREATE PROCEDURE #up_MessageProcessed_get + @Code varchar(10), + @MessageControlID varchar(22), + @SuccessCode int OUTPUT, + @ErrorDescription varchar(255) OUTPUT + AS + + BEGIN + + Select 2 as MessageProcessID, 38349348 as StartNum, 3874900 as EndNum, GETDATE() as StartDate, GETDATE() as EndDate + SET @SuccessCode = 0 + SET @ErrorDescription = 'Completed successfully' + END"); + var result = connection.Query(sql: "#up_MessageProcessed_get", param: p, commandType: CommandType.StoredProcedure); + var row = result.Single(); + ((int)row.MessageProcessID).IsEqualTo(2); + ((int)row.StartNum).IsEqualTo(38349348); + ((int)row.EndNum).IsEqualTo(3874900); + DateTime startDate = row.StartDate, endDate = row.EndDate; + p.Get("SuccessCode").IsEqualTo(0); + p.Get("ErrorDescription").IsEqualTo("Completed successfully"); + } + + [Fact] + public void SO24605346_ProcsAndStrings() + { + connection.Execute(@"create proc #GetPracticeRebateOrderByInvoiceNumber @TaxInvoiceNumber nvarchar(20) as + select @TaxInvoiceNumber as [fTaxInvoiceNumber]"); + const string InvoiceNumber = "INV0000000028PPN"; + var result = connection.Query("#GetPracticeRebateOrderByInvoiceNumber", new + { + TaxInvoiceNumber = InvoiceNumber + }, commandType: CommandType.StoredProcedure).FirstOrDefault(); + + result.TaxInvoiceNumber.IsEqualTo("INV0000000028PPN"); + } + + private class PracticeRebateOrders + { + public string fTaxInvoiceNumber; +#if !COREFX + [System.Xml.Serialization.XmlElement(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)] +#endif + public string TaxInvoiceNumber + { + get { return fTaxInvoiceNumber; } + set { fTaxInvoiceNumber = value; } + } + } + + [Fact] + public void Issue327_ReadEmptyProcedureResults() + { + // Actually testing for not erroring here on the mapping having no rows to map on in Read(); + connection.Execute(@" + CREATE PROCEDURE #TestEmptyResults + AS + SELECT Top 0 1 Id, 'Bob' Name; + SELECT Top 0 'Billy Goat' Creature, 'Unicorn' SpiritAnimal, 'Rainbow' Location;"); + var query = connection.QueryMultiple("#TestEmptyResults", commandType: CommandType.StoredProcedure); + var result1 = query.Read(); + var result2 = query.Read(); + result1.Any().IsFalse(); + result2.Any().IsFalse(); + } + + private class Issue327_Person + { + public int Id { get; set; } + public string Name { get; set; } + } + + private class Issue327_Magic + { + public string Creature { get; set; } + public string SpiritAnimal { get; set; } + public string Location { get; set; } + } + + [Fact] + public void TestProcSupport() + { + var p = new DynamicParameters(); + p.Add("a", 11); + p.Add("b", dbType: DbType.Int32, direction: ParameterDirection.Output); + p.Add("c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); + + connection.Execute(@"create proc #TestProc + @a int, + @b int output +as +begin + set @b = 999 + select 1111 + return @a +end"); + connection.Query("#TestProc", p, commandType: CommandType.StoredProcedure).First().IsEqualTo(1111); + + p.Get("c").IsEqualTo(11); + p.Get("b").IsEqualTo(999); + } + + // https://stackoverflow.com/q/8593871 + [Fact] + public void TestListOfAnsiStrings() + { + var results = connection.Query("select * from (select 'a' str union select 'b' union select 'c') X where str in @strings", + new + { + strings = new[] { + new DbString { IsAnsi = true, Value = "a" }, + new DbString { IsAnsi = true, Value = "b" } + } + }).ToList(); + + results.Count.IsEqualTo(2); + results.Sort(); + results[0].IsEqualTo("a"); + results[1].IsEqualTo("b"); + } + } +} diff --git a/Dapper.Tests/Properties/AssemblyInfo.cs b/Dapper.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 3ef64df6..00000000 --- a/Dapper.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Smackdown")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Smackdown")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("77246f63-77a4-4d9f-a4d6-62282d67c8be")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Dapper.Tests/Properties/DataSources/SqlMapper.EntityFramework.tempdbEntities1.datasource b/Dapper.Tests/Properties/DataSources/SqlMapper.EntityFramework.tempdbEntities1.datasource deleted file mode 100644 index ace42764..00000000 --- a/Dapper.Tests/Properties/DataSources/SqlMapper.EntityFramework.tempdbEntities1.datasource +++ /dev/null @@ -1,10 +0,0 @@ - - - - SqlMapper.EntityFramework.tempdbEntities1, EntityFramework.Model.Designer.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null - \ No newline at end of file diff --git a/Dapper.Tests/Properties/Settings.Designer.cs b/Dapper.Tests/Properties/Settings.Designer.cs deleted file mode 100644 index 503beac9..00000000 --- a/Dapper.Tests/Properties/Settings.Designer.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if !COREFX -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Dapper.Tests.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)] - [global::System.Configuration.DefaultSettingValueAttribute("Data Source=.;Initial Catalog=tempdb;Integrated Security=True")] - public string tempdbConnectionString { - get { - return ((string)(this["tempdbConnectionString"])); - } - } - } -} -#endif \ No newline at end of file diff --git a/Dapper.Tests/Properties/Settings.settings b/Dapper.Tests/Properties/Settings.settings deleted file mode 100644 index e1bd2689..00000000 --- a/Dapper.Tests/Properties/Settings.settings +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - <?xml version="1.0" encoding="utf-16"?> -<SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <ConnectionString>Data Source=.;Initial Catalog=tempdb;Integrated Security=True</ConnectionString> - <ProviderName>System.Data.SqlClient</ProviderName> -</SerializableConnectionString> - Data Source=.;Initial Catalog=tempdb;Integrated Security=True - - - \ No newline at end of file diff --git a/Dapper.Tests/Properties/launchSettings.json b/Dapper.Tests/Properties/launchSettings.json deleted file mode 100644 index 4dbd118f..00000000 --- a/Dapper.Tests/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "test": { - "commandName": "test", - "sdkVersion": "dnx-coreclr-win-x86.1.0.0-rc1-update1" - } - } -} \ No newline at end of file diff --git a/Dapper.Tests/Providers/EntityFrameworkTests.cs b/Dapper.Tests/Providers/EntityFrameworkTests.cs new file mode 100644 index 00000000..0c93ffc2 --- /dev/null +++ b/Dapper.Tests/Providers/EntityFrameworkTests.cs @@ -0,0 +1,38 @@ +#if ENTITY_FRAMEWORK +using System.Data.Entity.Spatial; +using Xunit; + +namespace Dapper.Tests.Providers +{ + public class EntityFrameworkTests : TestBase + { + public EntityFrameworkTests() + { + EntityFramework.Handlers.Register(); + } + + [Fact] + public void Issue570_DbGeo_HasValues() + { + EntityFramework.Handlers.Register(); + const string redmond = "POINT (122.1215 47.6740)"; + DbGeography point = DbGeography.PointFromText(redmond, DbGeography.DefaultCoordinateSystemId); + DbGeography orig = point.Buffer(20); + + var fromDb = connection.QuerySingle("declare @geos table(geo geography); insert @geos(geo) values(@val); select * from @geos", + new { val = orig }); + + fromDb.Area.IsNotNull(); + fromDb.Area.IsEqualTo(orig.Area); + } + + [Fact] + public void Issue22_ExecuteScalar_EntityFramework() + { + var geo = DbGeography.LineFromText("LINESTRING(-122.360 47.656, -122.343 47.656 )", 4326); + var geo2 = connection.ExecuteScalar("select @geo", new { geo }); + geo2.IsNotNull(); + } + } +} +#endif diff --git a/Dapper.Tests/Providers/FirebirdTests.cs b/Dapper.Tests/Providers/FirebirdTests.cs new file mode 100644 index 00000000..9499c8e0 --- /dev/null +++ b/Dapper.Tests/Providers/FirebirdTests.cs @@ -0,0 +1,49 @@ +#if FIREBIRD +using FirebirdSql.Data.FirebirdClient; +using System.Data; +using System.Linq; +using Xunit; + +namespace Dapper.Tests.Providers +{ + public class FirebirdTests : TestBase + { + [Fact(Skip = "Bug in Firebird; a PR to fix it has been submitted")] + public void Issue178_Firebird() + { + const string cs = @"initial catalog=localhost:database;user id=SYSDBA;password=masterkey"; + + using (var connection = new FbConnection(cs)) + { + connection.Open(); + const string sql = @"select count(*) from Issue178"; + try { connection.Execute("drop table Issue178"); } + catch { /* don't care */ } + connection.Execute("create table Issue178(id int not null)"); + connection.Execute("insert into Issue178(id) values(42)"); + // raw ADO.net + using (var sqlCmd = new FbCommand(sql, connection)) + using (IDataReader reader1 = sqlCmd.ExecuteReader()) + { + Assert.IsTrue(reader1.Read()); + reader1.GetInt32(0).IsEqualTo(1); + Assert.IsFalse(reader1.Read()); + Assert.IsFalse(reader1.NextResult()); + } + + // dapper + using (var reader2 = connection.ExecuteReader(sql)) + { + Assert.IsTrue(reader2.Read()); + reader2.GetInt32(0).IsEqualTo(1); + Assert.IsFalse(reader2.Read()); + Assert.IsFalse(reader2.NextResult()); + } + + var count = connection.Query(sql).Single(); + count.IsEqualTo(1); + } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/Providers/Linq2SqlTests.cs b/Dapper.Tests/Providers/Linq2SqlTests.cs new file mode 100644 index 00000000..e9ca10f9 --- /dev/null +++ b/Dapper.Tests/Providers/Linq2SqlTests.cs @@ -0,0 +1,43 @@ +#if LINQ2SQL +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Dapper.Tests +{ + public class Linq2SqlTests : TestBase + { + [Fact] + public void TestLinqBinaryToClass() + { + byte[] orig = new byte[20]; + new Random(123456).NextBytes(orig); + var input = new System.Data.Linq.Binary(orig); + + var output = connection.Query("select @input as [Value]", new { input }).First().Value; + + output.ToArray().IsSequenceEqualTo(orig); + } + + [Fact] + public void TestLinqBinaryRaw() + { + byte[] orig = new byte[20]; + new Random(123456).NextBytes(orig); + var input = new System.Data.Linq.Binary(orig); + + var output = connection.Query("select @input as [Value]", new { input }).First(); + + output.ToArray().IsSequenceEqualTo(orig); + } + + private class WithBinary + { + public System.Data.Linq.Binary Value { get; set; } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/Providers/MySQLTests.cs b/Dapper.Tests/Providers/MySQLTests.cs new file mode 100644 index 00000000..57a4003d --- /dev/null +++ b/Dapper.Tests/Providers/MySQLTests.cs @@ -0,0 +1,187 @@ +#if MYSQL +using System; +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class MySQLTests : TestBase + { + private static MySql.Data.MySqlClient.MySqlConnection GetMySqlConnection(bool open = true, + bool convertZeroDatetime = false, bool allowZeroDatetime = false) + { + string cs = IsAppVeyor + ? "Server=localhost;Database=test;Uid=root;Pwd=Password12!;" + : "Server=localhost;Database=tests;Uid=test;Pwd=pass;"; + var csb = new MySql.Data.MySqlClient.MySqlConnectionStringBuilder(cs); + csb.AllowZeroDateTime = allowZeroDatetime; + csb.ConvertZeroDateTime = convertZeroDatetime; + var conn = new MySql.Data.MySqlClient.MySqlConnection(csb.ConnectionString); + if (open) conn.Open(); + return conn; + } + + [FactMySql] + public void DapperEnumValue_Mysql() + { + using (var connection = GetMySqlConnection()) + { + Common.DapperEnumValue(connection); + } + } + + [FactMySql] + public void Issue552_SignedUnsignedBooleans() + { + using (var conn = GetMySqlConnection(true, false, false)) + { + conn.Execute(@" +CREATE TEMPORARY TABLE IF NOT EXISTS `bar` ( + `id` INT NOT NULL, + `bool_val` BOOL NULL, + PRIMARY KEY (`id`)); + + truncate table bar; + insert bar (id, bool_val) values (1, null); + insert bar (id, bool_val) values (2, 0); + insert bar (id, bool_val) values (3, 1); + insert bar (id, bool_val) values (4, null); + insert bar (id, bool_val) values (5, 1); + insert bar (id, bool_val) values (6, 0); + insert bar (id, bool_val) values (7, null); + insert bar (id, bool_val) values (8, 1);"); + + var rows = conn.Query("select * from bar;").ToDictionary(x => x.Id); + + rows[1].Bool_Val.IsNull(); + rows[2].Bool_Val.IsEqualTo(false); + rows[3].Bool_Val.IsEqualTo(true); + rows[4].Bool_Val.IsNull(); + rows[5].Bool_Val.IsEqualTo(true); + rows[6].Bool_Val.IsEqualTo(false); + rows[7].Bool_Val.IsNull(); + rows[8].Bool_Val.IsEqualTo(true); + } + } + + private class MySqlHasBool + { + public int Id { get; set; } + public bool? Bool_Val { get; set; } + } + + [FactMySql] + public void Issue295_NullableDateTime_MySql_Default() + { + using (var conn = GetMySqlConnection(true, false, false)) + { + Common.TestDateTime(connection); + } + } + + [FactMySql] + public void Issue295_NullableDateTime_MySql_ConvertZeroDatetime() + { + using (var conn = GetMySqlConnection(true, true, false)) + { + Common.TestDateTime(connection); + } + } + + [FactMySql] + public void Issue295_NullableDateTime_MySql_AllowZeroDatetime() + { + using (var conn = GetMySqlConnection(true, false, true)) + { + Common.TestDateTime(connection); + } + } + + [FactMySql] + public void Issue295_NullableDateTime_MySql_ConvertAllowZeroDatetime() + { + using (var conn = GetMySqlConnection(true, true, true)) + { + Common.TestDateTime(connection); + } + } + + [FactMySql] + public void Issue426_SO34439033_DateTimeGainsTicks() + { + using (var conn = GetMySqlConnection(true, true, true)) + { + try { conn.Execute("drop table Issue426_Test"); } catch { /* don't care */ } + try { conn.Execute("create table Issue426_Test (Id int not null, Time time not null)"); } catch { /* don't care */ } + const long ticks = 553440000000; + const int Id = 426; + + var localObj = new Issue426_Test + { + Id = Id, + Time = TimeSpan.FromTicks(ticks) // from code example + }; + conn.Execute("replace into Issue426_Test values (@Id,@Time)", localObj); + + var dbObj = conn.Query("select * from Issue426_Test where Id = @id", new { id = Id }).Single(); + dbObj.Id.IsEqualTo(Id); + dbObj.Time.Value.Ticks.IsEqualTo(ticks); + } + } + + [FactMySql] + public void SO36303462_Tinyint_Bools() + { + using (var conn = GetMySqlConnection(true, true, true)) + { + try { conn.Execute("drop table SO36303462_Test"); } catch { /* don't care */ } + conn.Execute("create table SO36303462_Test (Id int not null, IsBold tinyint not null);"); + conn.Execute("insert SO36303462_Test (Id, IsBold) values (1,1);"); + conn.Execute("insert SO36303462_Test (Id, IsBold) values (2,0);"); + conn.Execute("insert SO36303462_Test (Id, IsBold) values (3,1);"); + + var rows = conn.Query("select * from SO36303462_Test").ToDictionary(x => x.Id); + rows.Count.IsEqualTo(3); + rows[1].IsBold.IsTrue(); + rows[2].IsBold.IsFalse(); + rows[3].IsBold.IsTrue(); + } + } + + private class SO36303462 + { + public int Id { get; set; } + public bool IsBold { get; set; } + } + + public class Issue426_Test + { + public long Id { get; set; } + public TimeSpan? Time { get; set; } + } + + public class FactMySqlAttribute : FactAttribute + { + public override string Skip + { + get { return unavailable ?? base.Skip; } + set { base.Skip = value; } + } + + private static readonly string unavailable; + + static FactMySqlAttribute() + { + try + { + using (GetMySqlConnection(true)) { } + } + catch (Exception ex) + { + unavailable = $"MySql is unavailable: {ex.Message}"; + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/Providers/OLDEBTests.cs b/Dapper.Tests/Providers/OLDEBTests.cs new file mode 100644 index 00000000..27011a70 --- /dev/null +++ b/Dapper.Tests/Providers/OLDEBTests.cs @@ -0,0 +1,283 @@ +#if OLEDB +using System; +using System.Data.OleDb; +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class OLDEBTests : TestBase + { + public static string OleDbConnectionString => + IsAppVeyor + ? @"Provider=SQLOLEDB;Data Source=(local)\SQL2014;Initial Catalog=tempdb;User Id=sa;Password=Password12!" + : "Provider=SQLOLEDB;Data Source=.;Initial Catalog=tempdb;Integrated Security=SSPI"; + + public OleDbConnection GetOleDbConnection() + { + var conn = new OleDbConnection(OleDbConnectionString); + conn.Open(); + return conn; + } + + // see https://stackoverflow.com/q/18847510/23354 + [Fact] + public void TestOleDbParameters() + { + using (var conn = GetOleDbConnection()) + { + var row = conn.Query("select Id = ?, Age = ?", + new { foo = 12, bar = 23 } // these names DO NOT MATTER!!! + ).Single(); + int age = row.Age; + int id = row.Id; + age.IsEqualTo(23); + id.IsEqualTo(12); + } + } + + [Fact] + public void PseudoPositionalParameters_Simple() + { + using (var connection = GetOleDbConnection()) + { + int value = connection.Query("select ?x? + ?y_2? + ?z?", new { x = 1, y_2 = 3, z = 5, z2 = 24 }).Single(); + value.IsEqualTo(9); + } + } + + [Fact] + public void Issue601_InternationalParameterNamesWork_OleDb() + { + // pseudo-positional + using (var connection = GetOleDbConnection()) + { + int value = connection.QuerySingle("select ?æøå٦?", new { æøå٦ = 42 }); + } + } + + [Fact] + public void PseudoPositionalParameters_Dynamic() + { + using (var connection = GetOleDbConnection()) + { + var args = new DynamicParameters(); + args.Add("x", 1); + args.Add("y_2", 3); + args.Add("z", 5); + args.Add("z2", 24); + int value = connection.Query("select ?x? + ?y_2? + ?z?", args).Single(); + value.IsEqualTo(9); + } + } + + [Fact] + public void PseudoPositionalParameters_ReusedParameter() + { + using (var connection = GetOleDbConnection()) + { + try + { + int value = connection.Query("select ?x? + ?y_2? + ?x?", new { x = 1, y_2 = 3 }).Single(); + Assert.Fail(); + } + catch (InvalidOperationException ex) + { + ex.Message.IsEqualTo("When passing parameters by position, each parameter can only be referenced once"); + } + } + } + + [Fact] + public void Issue569_SO38527197_PseudoPositionalParameters_In() + { + using (var connection = GetOleDbConnection()) + { + int[] ids = { 1, 2, 5, 7 }; + var list = connection.Query("select * from string_split('1,2,3,4,5',',') where value in ?ids?", new { ids }).AsList(); + list.Sort(); + string.Join(",", list).IsEqualTo("1,2,5"); + } + } + + [Fact] + public void PseudoPositional_CanUseVariable() + { + using (var connection = GetOleDbConnection()) + { + const int id = 42; + var row = connection.QuerySingle("declare @id int = ?id?; select @id as [A], @id as [B];", new { id }); + int a = (int)row.A; + int b = (int)row.B; + a.IsEqualTo(42); + b.IsEqualTo(42); + } + } + + [Fact] + public void PseudoPositional_CannotUseParameterMultipleTimes() + { + using (var connection = GetOleDbConnection()) + { + try + { + const int id = 42; + var row = connection.QuerySingle("select ?id? as [A], ?id? as [B];", new { id }); + Assert.Fail(); + } + catch (InvalidOperationException ex) when (ex.Message == "When passing parameters by position, each parameter can only be referenced once") + { + // that's a win + } + } + } + + [Fact] + public void PseudoPositionalParameters_ExecSingle() + { + using (var connection = GetOleDbConnection()) + { + var data = new { x = 6 }; + connection.Execute("create table #named_single(val int not null)"); + int count = connection.Execute("insert #named_single (val) values (?x?)", data); + int sum = (int)connection.ExecuteScalar("select sum(val) from #named_single"); + count.IsEqualTo(1); + sum.IsEqualTo(6); + } + } + + [Fact] + public void PseudoPositionalParameters_ExecMulti() + { + using (var connection = GetOleDbConnection()) + { + var data = new[] + { + new { x = 1, y = 1 }, + new { x = 3, y = 1 }, + new { x = 6, y = 1 }, + }; + connection.Execute("create table #named_multi(val int not null)"); + int count = connection.Execute("insert #named_multi (val) values (?x?)", data); + int sum = (int)connection.ExecuteScalar("select sum(val) from #named_multi"); + count.IsEqualTo(3); + sum.IsEqualTo(10); + } + } + + [Fact] + public void Issue457_NullParameterValues() + { + const string sql = @" +DECLARE @since DATETIME, @customerCode nvarchar(10) +SET @since = ? -- ODBC parameter +SET @customerCode = ? -- ODBC parameter + +SELECT @since as [Since], @customerCode as [Code]"; + + using (var connection = GetOleDbConnection()) + { + DateTime? since = null; // DateTime.Now.Date; + const string code = null; // "abc"; + var row = connection.QuerySingle(sql, new + { + since, + customerCode = code + }); + var a = (DateTime?)row.Since; + var b = (string)row.Code; + + a.IsEqualTo(since); + b.IsEqualTo(code); + } + } + + [Fact] + public void Issue457_NullParameterValues_Named() + { + const string sql = @" +DECLARE @since DATETIME, @customerCode nvarchar(10) +SET @since = ?since? -- ODBC parameter +SET @customerCode = ?customerCode? -- ODBC parameter + +SELECT @since as [Since], @customerCode as [Code]"; + + using (var connection = GetOleDbConnection()) + { + DateTime? since = null; // DateTime.Now.Date; + const string code = null; // "abc"; + var row = connection.QuerySingle(sql, new + { + since, + customerCode = code + }); + var a = (DateTime?)row.Since; + var b = (string)row.Code; + + a.IsEqualTo(since); + b.IsEqualTo(code); + } + } + + [Fact] + public async void Issue457_NullParameterValues_MultiAsync() + { + const string sql = @" +DECLARE @since DATETIME, @customerCode nvarchar(10) +SET @since = ? -- ODBC parameter +SET @customerCode = ? -- ODBC parameter + +SELECT @since as [Since], @customerCode as [Code]"; + + using (var connection = GetOleDbConnection()) + { + DateTime? since = null; // DateTime.Now.Date; + const string code = null; // "abc"; + using (var multi = await connection.QueryMultipleAsync(sql, new + { + since, + customerCode = code + }).ConfigureAwait(false)) + { + var row = await multi.ReadSingleAsync().ConfigureAwait(false); + var a = (DateTime?)row.Since; + var b = (string)row.Code; + + a.IsEqualTo(since); + b.IsEqualTo(code); + } + } + } + + [Fact] + public async void Issue457_NullParameterValues_MultiAsync_Named() + { + const string sql = @" +DECLARE @since DATETIME, @customerCode nvarchar(10) +SET @since = ?since? -- ODBC parameter +SET @customerCode = ?customerCode? -- ODBC parameter + +SELECT @since as [Since], @customerCode as [Code]"; + + using (var connection = GetOleDbConnection()) + { + DateTime? since = null; // DateTime.Now.Date; + const string code = null; // "abc"; + using (var multi = await connection.QueryMultipleAsync(sql, new + { + since, + customerCode = code + }).ConfigureAwait(false)) + { + var row = await multi.ReadSingleAsync().ConfigureAwait(false); + var a = (DateTime?)row.Since; + var b = (string)row.Code; + + a.IsEqualTo(since); + b.IsEqualTo(code); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/Providers/PostgresqlTests.cs b/Dapper.Tests/Providers/PostgresqlTests.cs new file mode 100644 index 00000000..57aaab3d --- /dev/null +++ b/Dapper.Tests/Providers/PostgresqlTests.cs @@ -0,0 +1,84 @@ +#if POSTGRESQL +using System; +using System.Data; +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class PostcresqlTests : TestBase + { + private static Npgsql.NpgsqlConnection GetOpenNpgsqlConnection() + { + string cs = IsAppVeyor + ? "Server=localhost;Port=5432;User Id=postgres;Password=Password12!;Database=test" + : "Server=localhost;Port=5432;User Id=dappertest;Password=dapperpass;Database=dappertest"; // ;Encoding = UNICODE + var conn = new Npgsql.NpgsqlConnection(cs); + conn.Open(); + return conn; + } + + private class Cat + { + public int Id { get; set; } + public string Breed { get; set; } + public string Name { get; set; } + } + + private readonly Cat[] Cats = + { + new Cat() { Breed = "Abyssinian", Name="KACTUS"}, + new Cat() { Breed = "Aegean cat", Name="KADAFFI"}, + new Cat() { Breed = "American Bobtail", Name="KANJI"}, + new Cat() { Breed = "Balinese", Name="MACARONI"}, + new Cat() { Breed = "Bombay", Name="MACAULAY"}, + new Cat() { Breed = "Burmese", Name="MACBETH"}, + new Cat() { Breed = "Chartreux", Name="MACGYVER"}, + new Cat() { Breed = "German Rex", Name="MACKENZIE"}, + new Cat() { Breed = "Javanese", Name="MADISON"}, + new Cat() { Breed = "Persian", Name="MAGNA"} + }; + + [FactPostgresql] + public void TestPostgresqlArrayParameters() + { + using (var conn = GetOpenNpgsqlConnection()) + { + IDbTransaction transaction = conn.BeginTransaction(); + conn.Execute("create table tcat ( id serial not null, breed character varying(20) not null, name character varying (20) not null);"); + conn.Execute("insert into tcat(breed, name) values(:breed, :name) ", Cats); + + var r = conn.Query("select * from tcat where id=any(:catids)", new { catids = new[] { 1, 3, 5 } }); + r.Count().IsEqualTo(3); + r.Count(c => c.Id == 1).IsEqualTo(1); + r.Count(c => c.Id == 3).IsEqualTo(1); + r.Count(c => c.Id == 5).IsEqualTo(1); + transaction.Rollback(); + } + } + + public class FactPostgresqlAttribute : FactAttribute + { + public override string Skip + { + get { return unavailable ?? base.Skip; } + set { base.Skip = value; } + } + + private static readonly string unavailable; + + static FactPostgresqlAttribute() + { + try + { + using (GetOpenNpgsqlConnection()) { } + } + catch (Exception ex) + { + unavailable = $"Postgresql is unavailable: {ex.Message}"; + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/Providers/SQLCETests.cs b/Dapper.Tests/Providers/SQLCETests.cs new file mode 100644 index 00000000..75c385a0 --- /dev/null +++ b/Dapper.Tests/Providers/SQLCETests.cs @@ -0,0 +1,56 @@ +#if SQL_CE +using System.Data.SqlServerCe; +using System.IO; +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class SQLCETests : TestBase + { + [Fact] + public void MultiRSSqlCE() + { + if (File.Exists("Test.DB.sdf")) + File.Delete("Test.DB.sdf"); + + const string cnnStr = "Data Source = Test.DB.sdf;"; + var engine = new SqlCeEngine(cnnStr); + engine.CreateDatabase(); + + using (var cnn = new SqlCeConnection(cnnStr)) + { + cnn.Open(); + + cnn.Execute("create table Posts (ID int, Title nvarchar(50), Body nvarchar(50), AuthorID int)"); + cnn.Execute("create table Authors (ID int, Name nvarchar(50))"); + + cnn.Execute("insert Posts values (1,'title','body',1)"); + cnn.Execute("insert Posts values(2,'title2','body2',null)"); + cnn.Execute("insert Authors values(1,'sam')"); + + var data = cnn.Query(@"select * from Posts p left join Authors a on a.ID = p.AuthorID", (post, author) => { post.Author = author; return post; }).ToList(); + var firstPost = data.First(); + firstPost.Title.IsEqualTo("title"); + firstPost.Author.Name.IsEqualTo("sam"); + data[1].Author.IsNull(); + } + } + + public class PostCE + { + public int ID { get; set; } + public string Title { get; set; } + public string Body { get; set; } + + public AuthorCE Author { get; set; } + } + + public class AuthorCE + { + public int ID { get; set; } + public string Name { get; set; } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/Providers/SqliteTests.cs b/Dapper.Tests/Providers/SqliteTests.cs new file mode 100644 index 00000000..700e2762 --- /dev/null +++ b/Dapper.Tests/Providers/SqliteTests.cs @@ -0,0 +1,122 @@ +#if SQLITE +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Data.SQLite; +using System.Data; +using Xunit; + +namespace Dapper.Tests +{ + public class SqliteTests : TestBase + { + protected static SQLiteConnection GetSQLiteConnection(bool open = true) + { + var connection = new SQLiteConnection("Data Source=:memory:"); + if (open) connection.Open(); + return connection; + } + + [FactSqlite] + public void DapperEnumValue_Sqlite() + { + using (var connection = GetSQLiteConnection()) + { + Common.DapperEnumValue(connection); + } + } + + [FactSqlite] + public void Issue466_SqliteHatesOptimizations() + { + using (var connection = GetSQLiteConnection()) + { + SqlMapper.ResetTypeHandlers(); + var row = connection.Query("select 42 as Id").First(); + row.Id.IsEqualTo(42); + row = connection.Query("select 42 as Id").First(); + row.Id.IsEqualTo(42); + + SqlMapper.ResetTypeHandlers(); + row = connection.QueryFirst("select 42 as Id"); + row.Id.IsEqualTo(42); + row = connection.QueryFirst("select 42 as Id"); + row.Id.IsEqualTo(42); + } + } + + [FactSqlite] + public async Task Issue466_SqliteHatesOptimizations_Async() + { + using (var connection = GetSQLiteConnection()) + { + SqlMapper.ResetTypeHandlers(); + var row = (await connection.QueryAsync("select 42 as Id").ConfigureAwait(false)).First(); + row.Id.IsEqualTo(42); + row = (await connection.QueryAsync("select 42 as Id").ConfigureAwait(false)).First(); + row.Id.IsEqualTo(42); + + SqlMapper.ResetTypeHandlers(); + row = await connection.QueryFirstAsync("select 42 as Id").ConfigureAwait(false); + row.Id.IsEqualTo(42); + row = await connection.QueryFirstAsync("select 42 as Id").ConfigureAwait(false); + row.Id.IsEqualTo(42); + } + } + + [FactSqlite] + public void Isse467_SqliteLikesParametersWithPrefix() + { + Isse467_SqliteParameterNaming(true); + } + + [FactSqlite] + public void Isse467_SqliteLikesParametersWithoutPrefix() + { // see issue 375 / 467; note: fixed from RC2 onwards + Isse467_SqliteParameterNaming(false); + } + + private void Isse467_SqliteParameterNaming(bool prefix) + { + using (var connection = GetSQLiteConnection()) + { + var cmd = connection.CreateCommand(); + cmd.CommandText = "select @foo"; +#if NET45 + const DbType type = DbType.Int32; +#else + const SqliteType type = SqliteType.Integer; +#endif + cmd.Parameters.Add(prefix ? "@foo" : "foo", type).Value = 42; + var i = Convert.ToInt32(cmd.ExecuteScalar()); + i.IsEqualTo(42); + } + } + + public class FactSqliteAttribute : FactAttribute + { + public override string Skip + { + get { return unavailable ?? base.Skip; } + set { base.Skip = value; } + } + + private static readonly string unavailable; + + static FactSqliteAttribute() + { + try + { + using (GetSQLiteConnection()) + { + } + } + catch (Exception ex) + { + unavailable = $"Sqlite is unavailable: {ex.Message}"; + } + } + } + } +} +#endif \ No newline at end of file diff --git a/Dapper.Tests/QueryMultipleTests.cs b/Dapper.Tests/QueryMultipleTests.cs new file mode 100644 index 00000000..a827d7c2 --- /dev/null +++ b/Dapper.Tests/QueryMultipleTests.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Xunit; + +namespace Dapper.Tests +{ + public class QueryMultipleTests : TestBase + { + [Fact] + public void TestQueryMultipleBuffered() + { + using (var grid = connection.QueryMultiple("select 1; select 2; select @x; select 4", new { x = 3 })) + { + var a = grid.Read(); + var b = grid.Read(); + var c = grid.Read(); + var d = grid.Read(); + + a.Single().Equals(1); + b.Single().Equals(2); + c.Single().Equals(3); + d.Single().Equals(4); + } + } + + [Fact] + public void TestQueryMultipleNonBufferedIncorrectOrder() + { + using (var grid = connection.QueryMultiple("select 1; select 2; select @x; select 4", new { x = 3 })) + { + var a = grid.Read(false); + try + { + var b = grid.Read(false); + throw new InvalidOperationException(); // should have thrown + } + catch (InvalidOperationException) + { + // that's expected + } + } + } + + [Fact] + public void TestQueryMultipleNonBufferedCorrectOrder() + { + using (var grid = connection.QueryMultiple("select 1; select 2; select @x; select 4", new { x = 3 })) + { + var a = grid.Read(false).Single(); + var b = grid.Read(false).Single(); + var c = grid.Read(false).Single(); + var d = grid.Read(false).Single(); + + a.Equals(1); + b.Equals(2); + c.Equals(3); + d.Equals(4); + } + } + + [Fact] + public void TestMultiReaderBasic() + { + const string sql = @"select 1 as Id union all select 2 as Id select 'abc' as name select 1 as Id union all select 2 as Id"; + int i, j; + string s; + using (var multi = connection.QueryMultiple(sql)) + { + i = multi.Read().First(); + s = multi.Read().Single(); + j = multi.Read().Sum(); + } + Assert.IsEqualTo(i, 1); + Assert.IsEqualTo(s, "abc"); + Assert.IsEqualTo(j, 3); + } + + [Fact] + public void TestReadDynamicWithGridReader() + { + const string createSql = @" + create table #Users (Id int, Name varchar(20)) + create table #Posts (Id int, OwnerId int, Content varchar(20)) + + insert #Users values(99, 'Sam') + insert #Users values(2, 'I am') + + insert #Posts values(1, 99, 'Sams Post1') + insert #Posts values(2, 99, 'Sams Post2') + insert #Posts values(3, null, 'no ones post')"; + try + { + connection.Execute(createSql); + + const string sql = @"SELECT * FROM #Users ORDER BY Id + SELECT * FROM #Posts ORDER BY Id DESC"; + + var grid = connection.QueryMultiple(sql); + + var users = grid.Read().ToList(); + var posts = grid.Read().ToList(); + + users.Count.IsEqualTo(2); + posts.Count.IsEqualTo(3); + + ((int)users.First().Id).IsEqualTo(2); + ((int)posts.First().Id).IsEqualTo(3); + } + finally + { + connection.Execute("drop table #Users drop table #Posts"); + } + } + + [Fact] + public void Issue268_ReturnQueryMultiple() + { + connection.Execute(@"create proc #TestProc268 (@a int, @b int, @c int)as +begin +select @a; + +select @b + +return @c; +end"); + var p = new DynamicParameters(new { a = 1, b = 2, c = 3 }); + p.Add("RetVal", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); + + using (var reader = connection.QueryMultiple("#TestProc268", p, commandType: CommandType.StoredProcedure)) + { + reader.Read(); + } + var retVal = p.Get("RetVal"); + retVal.IsEqualTo(3); + } + + [Fact] + public void Issue524_QueryMultiple_Cast() + { + // aka: Read should work even if the data is a + // using regular API + connection.Query("select cast(42 as bigint)").Single().IsEqualTo(42); + connection.QuerySingle("select cast(42 as bigint)").IsEqualTo(42); + + // using multi-reader API + using(var reader = connection.QueryMultiple("select cast(42 as bigint); select cast(42 as bigint)")) + { + reader.Read().Single().IsEqualTo(42); + reader.ReadSingle().IsEqualTo(42); + } + } + + [Fact] + public void QueryMultipleFromClosed() + { + using (var conn = GetClosedConnection()) + { + using (var multi = conn.QueryMultiple("select 1; select 'abc';")) + { + multi.Read().Single().IsEqualTo(1); + multi.Read().Single().IsEqualTo("abc"); + } + conn.State.IsEqualTo(ConnectionState.Closed); + } + } + + [Fact] + public void QueryMultiple2FromClosed() + { + using (var conn = GetClosedConnection()) + { + conn.State.IsEqualTo(ConnectionState.Closed); + using (var multi = conn.QueryMultiple("select 1 select 2 select 3")) + { + multi.Read().Single().IsEqualTo(1); + multi.Read().Single().IsEqualTo(2); + // not reading 3 is intentional here + } + conn.State.IsEqualTo(ConnectionState.Closed); + } + } + + [Fact] + public void SO35554284_QueryMultipleUntilConsumed() + { + using (var reader = connection.QueryMultiple("select 1 as Id; select 2 as Id; select 3 as Id;")) + { + var items = new List(); + while (!reader.IsConsumed) + { + items.AddRange(reader.Read()); + } + items.Count.IsEqualTo(3); + items[0].Id.IsEqualTo(1); + items[1].Id.IsEqualTo(2); + items[2].Id.IsEqualTo(3); + } + } + + [Fact] + public void QueryMultipleInvalidFromClosed() + { + using (var conn = GetClosedConnection()) + { + try + { + conn.QueryMultiple("select gibberish"); + false.IsEqualTo(true); // shouldn't have got here + } + catch + { + conn.State.IsEqualTo(ConnectionState.Closed); + } + } + } + + [Fact] + public void TestMultiSelectWithSomeEmptyGridsUnbuffered() => TestMultiSelectWithSomeEmptyGrids(false); + + [Fact] + public void TestMultiSelectWithSomeEmptyGridsBuffered() => TestMultiSelectWithSomeEmptyGrids(true); + + private void TestMultiSelectWithSomeEmptyGrids(bool buffered) + { + using (var reader = connection.QueryMultiple("select 1; select 2 where 1 = 0; select 3 where 1 = 0; select 4;")) + { + var one = reader.Read(buffered: buffered).ToArray(); + var two = reader.Read(buffered: buffered).ToArray(); + var three = reader.Read(buffered: buffered).ToArray(); + var four = reader.Read(buffered: buffered).ToArray(); + try + { // only returned four grids; expect a fifth read to fail + reader.Read(buffered: buffered); + throw new InvalidOperationException("this should not have worked!"); + } + catch (ObjectDisposedException ex) + { // expected; success + ex.Message.IsEqualTo("The reader has been disposed; this can happen after all data has been consumed\r\nObject name: 'Dapper.SqlMapper+GridReader'."); + } + + one.Length.IsEqualTo(1); + one[0].IsEqualTo(1); + two.Length.IsEqualTo(0); + three.Length.IsEqualTo(0); + four.Length.IsEqualTo(1); + four[0].IsEqualTo(4); + } + } + + [Fact] + public void TypeBasedViaTypeMulti() + { + Type type = Common.GetSomeType(); + + dynamic first, second; + using (var multi = connection.QueryMultiple("select @A as [A], @B as [B]; select @C as [A], @D as [B]", + new { A = 123, B = "abc", C = 456, D = "def" })) + { + first = multi.Read(type).Single(); + second = multi.Read(type).Single(); + } + ((object)first).GetType().IsEqualTo(type); + int a = first.A; + string b = first.B; + a.IsEqualTo(123); + b.IsEqualTo("abc"); + + ((object)second).GetType().IsEqualTo(type); + a = second.A; + b = second.B; + a.IsEqualTo(456); + b.IsEqualTo("def"); + } + } +} diff --git a/Dapper.Tests/SharedTypes/Address.cs b/Dapper.Tests/SharedTypes/Address.cs new file mode 100644 index 00000000..3aaa5a56 --- /dev/null +++ b/Dapper.Tests/SharedTypes/Address.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dapper.Tests +{ + public class Address + { + public int AddressId { get; set; } + public string Name { get; set; } + public int PersonId { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/Bar1.cs b/Dapper.Tests/SharedTypes/Bar1.cs new file mode 100644 index 00000000..306d6b96 --- /dev/null +++ b/Dapper.Tests/SharedTypes/Bar1.cs @@ -0,0 +1,8 @@ +namespace Dapper.Tests +{ + public class Bar1 + { + public int BarId; + public string Name { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/Category.cs b/Dapper.Tests/SharedTypes/Category.cs new file mode 100644 index 00000000..e51ef200 --- /dev/null +++ b/Dapper.Tests/SharedTypes/Category.cs @@ -0,0 +1,9 @@ +namespace Dapper.Tests +{ + public class Category + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/Comment.cs b/Dapper.Tests/SharedTypes/Comment.cs new file mode 100644 index 00000000..568b0a01 --- /dev/null +++ b/Dapper.Tests/SharedTypes/Comment.cs @@ -0,0 +1,8 @@ +namespace Dapper.Tests +{ + public class Comment + { + public int Id { get; set; } + public string CommentData { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/Dog.cs b/Dapper.Tests/SharedTypes/Dog.cs new file mode 100644 index 00000000..9a521292 --- /dev/null +++ b/Dapper.Tests/SharedTypes/Dog.cs @@ -0,0 +1,14 @@ +using System; + +namespace Dapper.Tests +{ + public class Dog + { + public int? Age { get; set; } + public Guid Id { get; set; } + public string Name { get; set; } + public float? Weight { get; set; } + + public int IgnoredProperty => 1; + } +} diff --git a/Dapper.Tests/SharedTypes/Enums.cs b/Dapper.Tests/SharedTypes/Enums.cs new file mode 100644 index 00000000..15acaa2d --- /dev/null +++ b/Dapper.Tests/SharedTypes/Enums.cs @@ -0,0 +1,14 @@ +namespace Dapper.Tests +{ + internal enum AnEnum + { + A = 2, + B = 1 + } + + internal enum AnotherEnum : byte + { + A = 2, + B = 1 + } +} diff --git a/Dapper.Tests/SharedTypes/Foo1.cs b/Dapper.Tests/SharedTypes/Foo1.cs new file mode 100644 index 00000000..00d3151f --- /dev/null +++ b/Dapper.Tests/SharedTypes/Foo1.cs @@ -0,0 +1,8 @@ +namespace Dapper.Tests +{ + public class Foo1 + { + public int Id; + public int BarId { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/HazNameId.cs b/Dapper.Tests/SharedTypes/HazNameId.cs new file mode 100644 index 00000000..7bc44a5a --- /dev/null +++ b/Dapper.Tests/SharedTypes/HazNameId.cs @@ -0,0 +1,8 @@ +namespace Dapper.Tests +{ + public class HazNameId + { + public string Name { get; set; } + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/Dapper.Tests/SharedTypes/Person.cs b/Dapper.Tests/SharedTypes/Person.cs new file mode 100644 index 00000000..269eff0a --- /dev/null +++ b/Dapper.Tests/SharedTypes/Person.cs @@ -0,0 +1,11 @@ +namespace Dapper.Tests +{ + public class Person + { + public int PersonId { get; set; } + public string Name { get; set; } + public string Occupation { get; private set; } + public int NumberOfLegs = 2; + public Address Address { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/Post.cs b/Dapper.Tests/SharedTypes/Post.cs new file mode 100644 index 00000000..9c017d6d --- /dev/null +++ b/Dapper.Tests/SharedTypes/Post.cs @@ -0,0 +1,10 @@ +namespace Dapper.Tests +{ + public class Post + { + public int Id { get; set; } + public User Owner { get; set; } + public string Content { get; set; } + public Comment Comment { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/Product.cs b/Dapper.Tests/SharedTypes/Product.cs new file mode 100644 index 00000000..0e07da3a --- /dev/null +++ b/Dapper.Tests/SharedTypes/Product.cs @@ -0,0 +1,9 @@ +namespace Dapper.Tests +{ + public class Product + { + public int Id { get; set; } + public string Name { get; set; } + public Category Category { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/ReviewBoard.cs b/Dapper.Tests/SharedTypes/ReviewBoard.cs new file mode 100644 index 00000000..06a5473a --- /dev/null +++ b/Dapper.Tests/SharedTypes/ReviewBoard.cs @@ -0,0 +1,17 @@ +namespace Dapper.Tests +{ + public class ReviewBoard + { + public int Id { get; set; } + public string Name { get; set; } + public User User1 { get; set; } + public User User2 { get; set; } + public User User3 { get; set; } + public User User4 { get; set; } + public User User5 { get; set; } + public User User6 { get; set; } + public User User7 { get; set; } + public User User8 { get; set; } + public User User9 { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/ShortEnum.cs b/Dapper.Tests/SharedTypes/ShortEnum.cs new file mode 100644 index 00000000..33254f30 --- /dev/null +++ b/Dapper.Tests/SharedTypes/ShortEnum.cs @@ -0,0 +1,13 @@ +namespace Dapper.Tests +{ + public enum ShortEnum : short + { + Zero = 0, + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + Six = 6 + } +} diff --git a/Dapper.Tests/SharedTypes/SomeType.cs b/Dapper.Tests/SharedTypes/SomeType.cs new file mode 100644 index 00000000..b86f8371 --- /dev/null +++ b/Dapper.Tests/SharedTypes/SomeType.cs @@ -0,0 +1,8 @@ +namespace Dapper.Tests +{ + public class SomeType + { + public int A { get; set; } + public string B { get; set; } + } +} diff --git a/Dapper.Tests/SharedTypes/User.cs b/Dapper.Tests/SharedTypes/User.cs new file mode 100644 index 00000000..84d8d852 --- /dev/null +++ b/Dapper.Tests/SharedTypes/User.cs @@ -0,0 +1,8 @@ +namespace Dapper.Tests +{ + public class User + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/Dapper.Tests/SqlServerTypes/readme.htm b/Dapper.Tests/SqlServerTypes/readme.htm deleted file mode 100644 index 1f4b2247..00000000 --- a/Dapper.Tests/SqlServerTypes/readme.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - - Microsoft.SqlServer.Types - - - -
-

Action required to load native assemblies

-

- To deploy an application that uses spatial data types to a machine that does not have 'System CLR Types for SQL Server' installed you also need to deploy the native assembly SqlServerSpatial110.dll. Both x86 (32 bit) and x64 (64 bit) versions of this assembly have been added to your project under the SqlServerTypes\x86 and SqlServerTypes\x64 subdirectories. The native assembly msvcr100.dll is also included in case the C++ runtime is not installed. -

-

- You need to add code to load the correct one of these assemblies at runtime (depending on the current architecture). -

-

ASP.NET applications

-

- For ASP.NET applications, add the following line of code to the Application_Start method in Global.asax.cs: -

    SqlServerTypes.Utilities.LoadNativeAssemblies(Server.MapPath("~/bin"));
-

-

Desktop applications

-

- For desktop applications, add the following line of code to run before any spatial operations are performed: -

    SqlServerTypes.Utilities.LoadNativeAssemblies(AppDomain.CurrentDomain.BaseDirectory);
-

-
- - \ No newline at end of file diff --git a/Dapper.Tests/TestBase.cs b/Dapper.Tests/TestBase.cs new file mode 100644 index 00000000..450fb815 --- /dev/null +++ b/Dapper.Tests/TestBase.cs @@ -0,0 +1,84 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using System.Globalization; +#if !COREFX +using System.Threading; +#endif + +namespace Dapper.Tests +{ + public abstract class TestBase : IDisposable + { + protected static readonly bool IsAppVeyor = Environment.GetEnvironmentVariable("Appveyor")?.ToUpperInvariant() == "TRUE"; + + public static string ConnectionString => + IsAppVeyor + ? @"Server=(local)\SQL2014;Database=tempdb;User ID=sa;Password=Password12!" + : "Data Source=.;Initial Catalog=tempdb;Integrated Security=True"; + + protected SqlConnection _connection; + protected SqlConnection connection => _connection ?? (_connection = GetOpenConnection()); + + public static SqlConnection GetOpenConnection(bool mars = false) + { + var cs = ConnectionString; + if (mars) + { + var scsb = new SqlConnectionStringBuilder(cs) + { + MultipleActiveResultSets = true + }; + cs = scsb.ConnectionString; + } + var connection = new SqlConnection(cs); + connection.Open(); + return connection; + } + + public SqlConnection GetClosedConnection() + { + var conn = new SqlConnection(ConnectionString); + if (conn.State != ConnectionState.Closed) throw new InvalidOperationException("should be closed!"); + return conn; + } + + protected static CultureInfo ActiveCulture + { +#if COREFX + get { return CultureInfo.CurrentCulture; } + set { CultureInfo.CurrentCulture = value; } +#else + get { return Thread.CurrentThread.CurrentCulture; } + set { Thread.CurrentThread.CurrentCulture = value; } +#endif + } + + static TestBase() + { + Console.WriteLine("Dapper: " + typeof(SqlMapper).AssemblyQualifiedName); + Console.WriteLine("Using Connectionstring: {0}", ConnectionString); +#if COREFX + Console.WriteLine("CoreCLR"); +#else + Console.WriteLine(".NET: " + Environment.Version); + Console.Write("Loading native assemblies for SQL types..."); + try + { + SqlServerTypesLoader.LoadNativeAssemblies(AppDomain.CurrentDomain.BaseDirectory); + Console.WriteLine("done."); + } + catch (Exception ex) + { + Console.WriteLine("failed."); + Console.Error.WriteLine(ex.Message); + } +#endif + } + + public void Dispose() + { + connection?.Dispose(); + } + } +} diff --git a/Dapper.Tests/Tests.QueryMultiple.cs b/Dapper.Tests/Tests.QueryMultiple.cs deleted file mode 100644 index 94ea711a..00000000 --- a/Dapper.Tests/Tests.QueryMultiple.cs +++ /dev/null @@ -1,155 +0,0 @@ -using Dapper; -using System; -using System.Data; -using System.Linq; -using Xunit; - -namespace Dapper.Tests -{ - public partial class TestSuite - { - [Fact] - public void TestQueryMultipleBuffered() - { - using (var grid = connection.QueryMultiple("select 1; select 2; select @x; select 4", new { x = 3 })) - { - var a = grid.Read(); - var b = grid.Read(); - var c = grid.Read(); - var d = grid.Read(); - - a.Single().Equals(1); - b.Single().Equals(2); - c.Single().Equals(3); - d.Single().Equals(4); - } - } - - [Fact] - public void TestQueryMultipleNonBufferedIncorrectOrder() - { - using (var grid = connection.QueryMultiple("select 1; select 2; select @x; select 4", new { x = 3 })) - { - var a = grid.Read(false); - try - { - var b = grid.Read(false); - throw new InvalidOperationException(); // should have thrown - } - catch (InvalidOperationException) - { - // that's expected - } - } - } - - [Fact] - public void TestQueryMultipleNonBufferedCorrectOrder() - { - using (var grid = connection.QueryMultiple("select 1; select 2; select @x; select 4", new { x = 3 })) - { - var a = grid.Read(false).Single(); - var b = grid.Read(false).Single(); - var c = grid.Read(false).Single(); - var d = grid.Read(false).Single(); - - a.Equals(1); - b.Equals(2); - c.Equals(3); - d.Equals(4); - } - } - - [Fact] - public void TestMultiReaderBasic() - { - var sql = @"select 1 as Id union all select 2 as Id select 'abc' as name select 1 as Id union all select 2 as Id"; - int i, j; - string s; - using (var multi = connection.QueryMultiple(sql)) - { - i = multi.Read().First(); - s = multi.Read().Single(); - j = multi.Read().Sum(); - } - Assert.IsEqualTo(i, 1); - Assert.IsEqualTo(s, "abc"); - Assert.IsEqualTo(j, 3); - } - - [Fact] - public void TestReadDynamicWithGridReader() - { - var createSql = @" - create table #Users (Id int, Name varchar(20)) - create table #Posts (Id int, OwnerId int, Content varchar(20)) - - insert #Users values(99, 'Sam') - insert #Users values(2, 'I am') - - insert #Posts values(1, 99, 'Sams Post1') - insert #Posts values(2, 99, 'Sams Post2') - insert #Posts values(3, null, 'no ones post')"; - try - { - connection.Execute(createSql); - - var sql = @"SELECT * FROM #Users ORDER BY Id - SELECT * FROM #Posts ORDER BY Id DESC"; - - var grid = connection.QueryMultiple(sql); - - var users = grid.Read().ToList(); - var posts = grid.Read().ToList(); - - users.Count.IsEqualTo(2); - posts.Count.IsEqualTo(3); - - ((int)users.First().Id).IsEqualTo(2); - ((int)posts.First().Id).IsEqualTo(3); - } - finally - { - connection.Execute("drop table #Users drop table #Posts"); - } - } - - [Fact] - public void Issue268_ReturnQueryMultiple() - { - connection.Execute(@"create proc #TestProc268 (@a int, @b int, @c int)as -begin -select @a; - -select @b - -return @c; -end"); - var p = new DynamicParameters(new { a = 1, b = 2, c = 3 }); - p.Add("RetVal", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); - - using (var reader = connection.QueryMultiple("#TestProc268", p, commandType: CommandType.StoredProcedure)) - { - reader.Read(); - } - var retVal = p.Get("RetVal"); - retVal.IsEqualTo(3); - } - - [Fact] - public void Issue524_QueryMultiple_Cast() - { // aka: Read should work even if the data is a - - // using regular API - connection.Query("select cast(42 as bigint)").Single().IsEqualTo(42); - connection.QuerySingle("select cast(42 as bigint)").IsEqualTo(42); - - // using multi-reader API - using(var reader = connection.QueryMultiple("select cast(42 as bigint); select cast(42 as bigint)")) - { - reader.Read().Single().IsEqualTo(42); - reader.ReadSingle().IsEqualTo(42); - } - } - } -} diff --git a/Dapper.Tests/Tests.cs b/Dapper.Tests/Tests.cs deleted file mode 100644 index eac725c4..00000000 --- a/Dapper.Tests/Tests.cs +++ /dev/null @@ -1,3819 +0,0 @@ -//#define POSTGRESQL // uncomment to run postgres tests - -#if SQLITE && (NET40 || NET45) -using SqliteConnection = System.Data.SQLite.SQLiteConnection; -#endif - -using System; -using System.Collections.Generic; -using System.Data.SqlClient; -using System.Linq; -using Dapper; -using System.IO; -using System.Data; -using System.Collections; -using System.Reflection; -using System.Dynamic; -using System.ComponentModel; -using Microsoft.CSharp.RuntimeBinder; -using System.Globalization; -using System.Threading; -using System.Data.SqlTypes; -using System.Diagnostics; -using Xunit; -using System.Data.Common; -using System.Text.RegularExpressions; -#if FIREBIRD -using FirebirdSql.Data.FirebirdClient; -#endif -#if ENTITY_FRAMEWORK -using System.Data.Entity.Spatial; -using Microsoft.SqlServer.Types; -#endif -#if SQL_CE -using System.Data.SqlServerCe; -#endif -using SqlServerTypes; -#if POSTGRESQL -using Npgsql; -#endif -#if SQLITE -#if NET40 || NET45 -using System.Data.SQLite; -#else -using Microsoft.Data.Sqlite; -#endif -#endif -#if ASYNC -using System.Threading.Tasks; -#endif - -#if COREFX -namespace System.ComponentModel { - public sealed class DescriptionAttribute : Attribute { - public DescriptionAttribute(string description) - { - Description = description; - } - public string Description {get;private set;} - } -} -namespace System -{ - public enum GenericUriParserOptions - { - Default - } - public class GenericUriParser - { - private GenericUriParserOptions options; - - public GenericUriParser(GenericUriParserOptions options) - { - this.options = options; - } - } -} -#endif - -namespace Dapper.Tests -{ - public partial class TestSuite : IDisposable - { - public static string ConnectionString => - IsAppVeyor - ? @"Server=(local)\SQL2014;Database=tempdb;User ID=sa;Password=Password12!" - : "Data Source=.;Initial Catalog=tempdb;Integrated Security=True"; - - public static string OleDbConnectionString => - IsAppVeyor - ? @"Provider=SQLOLEDB;Data Source=(local)\SQL2014;Initial Catalog=tempdb;User Id=sa;Password=Password12!" - : "Provider=SQLOLEDB;Data Source=.;Initial Catalog=tempdb;Integrated Security=SSPI"; - - public static SqlConnection GetOpenConnection(bool mars = false) - { - var cs = ConnectionString; - if (mars) - { - var scsb = new SqlConnectionStringBuilder(cs) - { - MultipleActiveResultSets = true - }; - cs = scsb.ConnectionString; - } - var connection = new SqlConnection(cs); - connection.Open(); - return connection; - } - - public static SqlConnection GetClosedConnection() - { - var conn = new SqlConnection(ConnectionString); - if (conn.State != ConnectionState.Closed) throw new InvalidOperationException("should be closed!"); - return conn; - } - - private SqlConnection _connection, _marsConnection; - - private SqlConnection connection => _connection ?? (_connection = GetOpenConnection()); - private SqlConnection marsConnection => _marsConnection ?? (_marsConnection = GetOpenConnection(true)); - - static TestSuite() - { -#if COREFX - Console.WriteLine("CoreCLR"); -#else - Console.WriteLine(".NET: " + Environment.Version); -#endif - Console.WriteLine("Dapper: " + typeof(SqlMapper).AssemblyQualifiedName); - Console.WriteLine("Using Connectionstring: {0}", ConnectionString); -#if !(COREFX || DNX) - Console.Write("Loading native assemblies for SQL types..."); - try { - Utilities.LoadNativeAssemblies(AppDomain.CurrentDomain.BaseDirectory); - Console.WriteLine("done."); - } catch(Exception ex) - { - Console.WriteLine("failed."); - Console.Error.WriteLine(ex.Message); - } -#endif - } - - public TestSuite() - { - } - - public void Dispose() - { - connection?.Dispose(); - } - - // http://stackoverflow.com/q/8593871 - - [Fact] - public void TestListOfAnsiStrings() - { - var results = connection.Query("select * from (select 'a' str union select 'b' union select 'c') X where str in @strings", - new { strings = new[] { - new DbString { IsAnsi = true, Value = "a" }, - new DbString { IsAnsi = true, Value = "b" } - } }).ToList(); - - results.Count.IsEqualTo(2); - results.Sort(); - results[0].IsEqualTo("a"); - results[1].IsEqualTo("b"); - } - - [Fact] - public void TestListExpansionPadding_Enabled() - { - TestListExpansionPadding(true); - } - [Fact] - public void TestListExpansionPadding_Disabled() - { - TestListExpansionPadding(false); - } - - private void TestListExpansionPadding(bool enabled) - { - bool oldVal = SqlMapper.Settings.PadListExpansions; - try - { - SqlMapper.Settings.PadListExpansions = enabled; - connection.ExecuteScalar(@" -create table #ListExpansion(id int not null identity(1,1), value int null); -insert #ListExpansion (value) values (null); -declare @loop int = 0; -while (@loop < 12) -begin -- double it - insert #ListExpansion (value) select value from #ListExpansion; - set @loop = @loop + 1; -end - -select count(1) as [Count] from #ListExpansion").IsEqualTo(4096); - - var list = new List(); - int nextId = 1, batchCount; - var rand = new Random(12345); - const int SQL_SERVER_MAX_PARAMS = 2095; - TestListForExpansion(list, enabled); // test while empty - while (list.Count < SQL_SERVER_MAX_PARAMS) - { - try - { - if (list.Count <= 20) batchCount = 1; - else if (list.Count <= 200) batchCount = rand.Next(1, 40); - else batchCount = rand.Next(1, 100); - - for (int j = 0; j < batchCount && list.Count < SQL_SERVER_MAX_PARAMS; j++) - list.Add(nextId++); - - TestListForExpansion(list, enabled); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failure with {list.Count} items: {ex.Message}", ex); - } - } - } - finally - { - SqlMapper.Settings.PadListExpansions = oldVal; - } - } - - private void TestListForExpansion(List list, bool enabled) - { - var row = connection.QuerySingle(@" -declare @hits int, @misses int, @count int; -select @count = count(1) from #ListExpansion; -select @hits = count(1) from #ListExpansion where id in @ids ; -select @misses = count(1) from #ListExpansion where not id in @ids ; -declare @query nvarchar(max) = N' in @ids '; -- ok, I confess to being pleased with this hack ;p -select @hits as [Hits], (@count - @misses) as [Misses], @query as [Query]; -", new { ids = list }); - int hits = row.Hits, misses = row.Misses; - string query = row.Query; - int argCount = Regex.Matches(query, "@ids[0-9]").Count; - int expectedCount = GetExpectedListExpansionCount(list.Count, enabled); - hits.IsEqualTo(list.Count); - misses.IsEqualTo(list.Count); - argCount.IsEqualTo(expectedCount); - } - - static int GetExpectedListExpansionCount(int count, bool enabled) - { - if (!enabled) return count; - - if (count <= 5 || count > 2070) return count; - - int padFactor; - if (count <= 150) padFactor = 10; - else if (count <= 750) padFactor = 50; - else if (count <= 2000) padFactor = 100; - else if (count <= 2070) padFactor = 10; - else padFactor = 200; - - int blocks = count / padFactor, delta = count % padFactor; - if (delta != 0) blocks++; - return blocks * padFactor; - } - - [Fact] - public void TestNullableGuidSupport() - { - var guid = connection.Query("select null").First(); - guid.IsNull(); - - guid = Guid.NewGuid(); - var guid2 = connection.Query("select @guid", new { guid }).First(); - guid.IsEqualTo(guid2); - } - - [Fact] - public void TestNonNullableGuidSupport() - { - var guid = Guid.NewGuid(); - var guid2 = connection.Query("select @guid", new { guid }).First(); - Assert.IsTrue(guid == guid2); - } - - struct Car - { - public enum TrapEnum : int - { - A = 1, - B = 2 - } -#pragma warning disable 0649 - public string Name; -#pragma warning restore 0649 - public int Age { get; set; } - public TrapEnum Trap { get; set; } - - } - - struct CarWithAllProps - { - public string Name { get; set; } - public int Age { get; set; } - - public Car.TrapEnum Trap { get; set; } - } - - [Fact] - public void TestStructs() - { - var car = connection.Query("select 'Ford' Name, 21 Age, 2 Trap").First(); - - car.Age.IsEqualTo(21); - car.Name.IsEqualTo("Ford"); - ((int)car.Trap).IsEqualTo(2); - } - - [Fact] - public void TestStructAsParam() - { - var car1 = new CarWithAllProps { Name = "Ford", Age = 21, Trap = Car.TrapEnum.B }; - // note Car has Name as a field; parameters only respect properties at the moment - var car2 = connection.Query("select @Name Name, @Age Age, @Trap Trap", car1).First(); - - car2.Name.IsEqualTo(car1.Name); - car2.Age.IsEqualTo(car1.Age); - car2.Trap.IsEqualTo(car1.Trap); - } - - [Fact] - public void SelectListInt() - { - connection.Query("select 1 union all select 2 union all select 3") - .IsSequenceEqualTo(new[] { 1, 2, 3 }); - } - - [Fact] - public void SelectBinary() - { - connection.Query("select cast(1 as varbinary(4))").First().SequenceEqual(new byte[] { 1 }); - } - - [Fact] - public void PassInIntArray() - { - connection.Query("select * from (select 1 as Id union all select 2 union all select 3) as X where Id in @Ids", new { Ids = new int[] { 1, 2, 3 }.AsEnumerable() }) - .IsSequenceEqualTo(new[] { 1, 2, 3 }); - } - - [Fact] - public void PassInEmptyIntArray() - { - connection.Query("select * from (select 1 as Id union all select 2 union all select 3) as X where Id in @Ids", new { Ids = new int[0] }) - .IsSequenceEqualTo(new int[0]); - } - - [Fact] - public void TestSchemaChanged() - { - connection.Execute("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); - try - { - var d = connection.Query("select * from #dog").Single(); - d.Name.IsEqualTo("Alf"); - d.Age.IsEqualTo(1); - connection.Execute("alter table #dog drop column Name"); - d = connection.Query("select * from #dog").Single(); - d.Name.IsNull(); - d.Age.IsEqualTo(1); - } - finally - { - connection.Execute("drop table #dog"); - } - } - - [Fact] - public void TestSchemaChangedViaFirstOrDefault() - { - connection.Execute("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); - try - { - var d = connection.QueryFirstOrDefault("select * from #dog"); - d.Name.IsEqualTo("Alf"); - d.Age.IsEqualTo(1); - connection.Execute("alter table #dog drop column Name"); - d = connection.QueryFirstOrDefault("select * from #dog"); - d.Name.IsNull(); - d.Age.IsEqualTo(1); - } - finally - { - connection.Execute("drop table #dog"); - } - } - - [Fact] - public void Test_Single_First_Default() - { - var sql = "select 0 where 1 = 0;"; // no rows - try { connection.QueryFirst(sql); Assert.Fail("QueryFirst, 0"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains no elements"); } - try { connection.QuerySingle(sql); Assert.Fail("QuerySingle, 0"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains no elements"); } - connection.QueryFirstOrDefault(sql).IsEqualTo(0); - connection.QuerySingleOrDefault(sql).IsEqualTo(0); - - sql = "select 1;"; // one row - connection.QueryFirst(sql).IsEqualTo(1); - connection.QuerySingle(sql).IsEqualTo(1); - connection.QueryFirstOrDefault(sql).IsEqualTo(1); - connection.QuerySingleOrDefault(sql).IsEqualTo(1); - - sql = "select 2 union select 3 order by 1;"; // two rows - connection.QueryFirst(sql).IsEqualTo(2); - try { connection.QuerySingle(sql); Assert.Fail("QuerySingle, 2"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains more than one element"); } - connection.QueryFirstOrDefault(sql).IsEqualTo(2); - try { connection.QuerySingleOrDefault(sql); Assert.Fail("QuerySingleOrDefault, 2"); } catch (InvalidOperationException ex) { ex.Message.IsEqualTo("Sequence contains more than one element"); } - } - - - [Fact] - public void TestSchemaChangedMultiMap() - { - connection.Execute("create table #dog(Age int, Name nvarchar(max)) insert #dog values(1, 'Alf')"); - try - { - var tuple = connection.Query>("select * from #dog d1 join #dog d2 on 1=1", Tuple.Create, splitOn: "Age").Single(); - - tuple.Item1.Name.IsEqualTo("Alf"); - tuple.Item1.Age.IsEqualTo(1); - tuple.Item2.Name.IsEqualTo("Alf"); - tuple.Item2.Age.IsEqualTo(1); - - connection.Execute("alter table #dog drop column Name"); - tuple = connection.Query>("select * from #dog d1 join #dog d2 on 1=1", Tuple.Create, splitOn: "Age").Single(); - - tuple.Item1.Name.IsNull(); - tuple.Item1.Age.IsEqualTo(1); - tuple.Item2.Name.IsNull(); - tuple.Item2.Age.IsEqualTo(1); - } - finally - { - connection.Execute("drop table #dog"); - } - } - - [Fact] - public void TestReadMultipleIntegersWithSplitOnAny() - { - connection.Query>( - "select 1,2,3 union all select 4,5,6", Tuple.Create, splitOn: "*") - .IsSequenceEqualTo(new[] { Tuple.Create(1, 2, 3), Tuple.Create(4, 5, 6) }); - } - - [Fact] - public void TestDoubleParam() - { - connection.Query("select @d", new { d = 0.1d }).First() - .IsEqualTo(0.1d); - } - - [Fact] - public void TestBoolParam() - { - connection.Query("select @b", new { b = false }).First() - .IsFalse(); - } - - // http://code.google.com/p/dapper-dot-net/issues/detail?id=70 - // https://connect.microsoft.com/VisualStudio/feedback/details/381934/sqlparameter-dbtype-dbtype-time-sets-the-parameter-to-sqldbtype-datetime-instead-of-sqldbtype-time - - [Fact] - public void TestTimeSpanParam() - { - connection.Query("select @ts", new { ts = TimeSpan.FromMinutes(42) }).First() - .IsEqualTo(TimeSpan.FromMinutes(42)); - } - - [Fact] - public void TestStrings() - { - connection.Query(@"select 'a' a union select 'b'") - .IsSequenceEqualTo(new[] { "a", "b" }); - } - - // see http://stackoverflow.com/questions/16726709/string-format-with-sql-wildcard-causing-dapper-query-to-break - [Fact] - public void CheckComplexConcat() - { - string end_wildcard = @" -SELECT * FROM #users16726709 -WHERE (first_name LIKE CONCAT(@search_term, '%') OR last_name LIKE CONCAT(@search_term, '%'));"; - - string both_wildcards = @" -SELECT * FROM #users16726709 -WHERE (first_name LIKE CONCAT('%', @search_term, '%') OR last_name LIKE CONCAT('%', @search_term, '%'));"; - - string formatted = @" -SELECT * FROM #users16726709 -WHERE (first_name LIKE {0} OR last_name LIKE {0});"; - - string use_end_only = @"CONCAT(@search_term, '%')"; - string use_both = @"CONCAT('%', @search_term, '%')"; - - // if true, slower query due to not being able to use indices, but will allow searching inside strings - bool allow_start_wildcards = false; - - string query = string.Format(formatted, allow_start_wildcards ? use_both : use_end_only); - string term = "F"; // the term the user searched for - - connection.Execute(@"create table #users16726709 (first_name varchar(200), last_name varchar(200)) -insert #users16726709 values ('Fred','Bloggs') insert #users16726709 values ('Tony','Farcus') insert #users16726709 values ('Albert','TenoF')"); - - // Using Dapper - connection.Query(end_wildcard, new { search_term = term }).Count().IsEqualTo(2); - connection.Query(both_wildcards, new { search_term = term }).Count().IsEqualTo(3); - connection.Query(query, new { search_term = term }).Count().IsEqualTo(2); - - } - - public class Dog - { - public int? Age { get; set; } - public Guid Id { get; set; } - public string Name { get; set; } - public float? Weight { get; set; } - - public int IgnoredProperty { get { return 1; } } - } - - [Fact] - public void TestExtraFields() - { - var guid = Guid.NewGuid(); - var dog = connection.Query("select '' as Extra, 1 as Age, 0.1 as Name1 , Id = @id", new { id = guid }); - - dog.Count() - .IsEqualTo(1); - - dog.First().Age - .IsEqualTo(1); - - dog.First().Id - .IsEqualTo(guid); - } - - [Fact] - public void TestStrongType() - { - var guid = Guid.NewGuid(); - var dog = connection.Query("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid }); - - dog.Count() - .IsEqualTo(1); - - dog.First().Age - .IsNull(); - - dog.First().Id - .IsEqualTo(guid); - } - - [Fact] - public void TestSimpleNull() - { - connection.Query("select null").First().IsNull(); - } - - [Fact] - public void TestExpando() - { - var rows = connection.Query("select 1 A, 2 B union all select 3, 4").ToList(); - - ((int)rows[0].A) - .IsEqualTo(1); - - ((int)rows[0].B) - .IsEqualTo(2); - - ((int)rows[1].A) - .IsEqualTo(3); - - ((int)rows[1].B) - .IsEqualTo(4); - } - - [Fact] - public void TestStringList() - { - connection.Query("select * from (select 'a' as x union all select 'b' union all select 'c') as T where x in @strings", new { strings = new[] { "a", "b", "c" } }) - .IsSequenceEqualTo(new[] { "a", "b", "c" }); - - connection.Query("select * from (select 'a' as x union all select 'b' union all select 'c') as T where x in @strings", new { strings = new string[0] }) - .IsSequenceEqualTo(new string[0]); - } - - [Fact] - public void TestExecuteCommand() - { - connection.Execute(@" - set nocount on - create table #t(i int) - set nocount off - insert #t - select @a a union all select @b - set nocount on - drop table #t", new { a = 1, b = 2 }).IsEqualTo(2); - } - - [Fact] - public void TestExecuteCommandWithHybridParameters() - { - var p = new DynamicParameters(new { a = 1, b = 2 }); - p.Add("c", dbType: DbType.Int32, direction: ParameterDirection.Output); - connection.Execute(@"set @c = @a + @b", p); - p.Get("@c").IsEqualTo(3); - } - - [Fact] - public void TestExecuteMultipleCommand() - { - connection.Execute("create table #t(i int)"); - try - { - int tally = connection.Execute(@"insert #t (i) values(@a)", new[] { new { a = 1 }, new { a = 2 }, new { a = 3 }, new { a = 4 } }); - int sum = connection.Query("select sum(i) from #t").First(); - tally.IsEqualTo(4); - sum.IsEqualTo(10); - } - finally - { - connection.Execute("drop table #t"); - } - } - - class Student - { - public string Name { get; set; } - public int Age { get; set; } - } - - [Fact] - public void TestExecuteMultipleCommandStrongType() - { - connection.Execute("create table #t(Name nvarchar(max), Age int)"); - try - { - int tally = connection.Execute(@"insert #t (Name,Age) values(@Name, @Age)", new List - { - new Student{Age = 1, Name = "sam"}, - new Student{Age = 2, Name = "bob"} - }); - int sum = connection.Query("select sum(Age) from #t").First(); - tally.IsEqualTo(2); - sum.IsEqualTo(3); - } - finally - { - connection.Execute("drop table #t"); - } - } - - [Fact] - public void TestExecuteMultipleCommandObjectArray() - { - connection.Execute("create table #t(i int)"); - int tally = connection.Execute(@"insert #t (i) values(@a)", new object[] { new { a = 1 }, new { a = 2 }, new { a = 3 }, new { a = 4 } }); - int sum = connection.Query("select sum(i) from #t drop table #t").First(); - tally.IsEqualTo(4); - sum.IsEqualTo(10); - } - - [Fact] - public void TestMassiveStrings() - { - var str = new string('X', 20000); - connection.Query("select @a", new { a = str }).First() - .IsEqualTo(str); - } - - class TestObj - { - public int _internal; - internal int Internal { set { _internal = value; } } - - public int _priv; - private int Priv { set { _priv = value; } } - - private int PrivGet { get { return _priv; } } - } - - [Fact] - public void TestSetInternal() - { - connection.Query("select 10 as [Internal]").First()._internal.IsEqualTo(10); - } - - [Fact] - public void TestSetPrivate() - { - connection.Query("select 10 as [Priv]").First()._priv.IsEqualTo(10); - } - - [Fact] - public void TestExpandWithNullableFields() - { - var row = connection.Query("select null A, 2 B").Single(); - - ((int?)row.A) - .IsNull(); - - ((int?)row.B) - .IsEqualTo(2); - } - - [Fact] - public void TestEnumeration() - { - var en = connection.Query("select 1 as one union all select 2 as one", buffered: false); - var i = en.GetEnumerator(); - i.MoveNext(); - - bool gotException = false; - try - { - var x = connection.Query("select 1 as one", buffered: false).First(); - } - catch (Exception) - { - gotException = true; - } - - while (i.MoveNext()) - { } - - // should not exception, since enumerated - en = connection.Query("select 1 as one", buffered: false); - - gotException.IsTrue(); - } - - [Fact] - public void TestEnumerationDynamic() - { - var en = connection.Query("select 1 as one union all select 2 as one", buffered: false); - var i = en.GetEnumerator(); - i.MoveNext(); - - bool gotException = false; - try - { - var x = connection.Query("select 1 as one", buffered: false).First(); - } - catch (Exception) - { - gotException = true; - } - - while (i.MoveNext()) - { } - - // should not exception, since enumertated - en = connection.Query("select 1 as one", buffered: false); - - gotException.IsTrue(); - } - - [Fact] - public void TestNakedBigInt() - { - long foo = 12345; - var result = connection.Query("select @foo", new { foo }).Single(); - foo.IsEqualTo(result); - } - - [Fact] - public void TestBigIntMember() - { - long foo = 12345; - var result = connection.Query(@" -declare @bar table(Value bigint) -insert @bar values (@foo) -select * from @bar", new { foo }).Single(); - result.Value.IsEqualTo(foo); - } - - class WithBigInt - { - public long Value { get; set; } - } - - [Fact] - public void TestFieldsAndPrivates() - { - var data = connection.Query( - @"select a=1,b=2,c=3,d=4,f='5'").Single(); - data.a.IsEqualTo(1); - data.GetB().IsEqualTo(2); - data.c.IsEqualTo(3); - data.GetD().IsEqualTo(4); - data.e.IsEqualTo(5); - } - - private class TestFieldCaseAndPrivatesEntity - { - public int a { get; set; } - private int b { get; set; } - public int GetB() { return b; } - public int c = 0; - private int d = 0; - public int GetD() { return d; } - public int e { get; set; } - private string f - { - get { return e.ToString(); } - set { e = int.Parse(value); } - } - } - - class InheritanceTest1 - { - public string Base1 { get; set; } - public string Base2 { get; private set; } - } - - class InheritanceTest2 : InheritanceTest1 - { - public string Derived1 { get; set; } - public string Derived2 { get; private set; } - } - - [Fact] - public void TestInheritance() - { - // Test that inheritance works. - var list = connection.Query("select 'One' as Derived1, 'Two' as Derived2, 'Three' as Base1, 'Four' as Base2"); - list.First().Derived1.IsEqualTo("One"); - list.First().Derived2.IsEqualTo("Two"); - list.First().Base1.IsEqualTo("Three"); - list.First().Base2.IsEqualTo("Four"); - } - -#if !COREFX - [Fact] - public void ExecuteReader() - { - var dt = new DataTable(); - dt.Load(connection.ExecuteReader("select 3 as [three], 4 as [four]")); - dt.Columns.Count.IsEqualTo(2); - dt.Columns[0].ColumnName.IsEqualTo("three"); - dt.Columns[1].ColumnName.IsEqualTo("four"); - dt.Rows.Count.IsEqualTo(1); - ((int)dt.Rows[0][0]).IsEqualTo(3); - ((int)dt.Rows[0][1]).IsEqualTo(4); - } -#endif - -#if SQL_CE - [Fact] - public void MultiRSSqlCE() - { - if (File.Exists("Test.DB.sdf")) - File.Delete("Test.DB.sdf"); - - var cnnStr = "Data Source = Test.DB.sdf;"; - var engine = new SqlCeEngine(cnnStr); - engine.CreateDatabase(); - - using (var cnn = new SqlCeConnection(cnnStr)) - { - cnn.Open(); - - cnn.Execute("create table Posts (ID int, Title nvarchar(50), Body nvarchar(50), AuthorID int)"); - cnn.Execute("create table Authors (ID int, Name nvarchar(50))"); - - cnn.Execute("insert Posts values (1,'title','body',1)"); - cnn.Execute("insert Posts values(2,'title2','body2',null)"); - cnn.Execute("insert Authors values(1,'sam')"); - - var data = cnn.Query(@"select * from Posts p left join Authors a on a.ID = p.AuthorID", (post, author) => { post.Author = author; return post; }).ToList(); - var firstPost = data.First(); - firstPost.Title.IsEqualTo("title"); - firstPost.Author.Name.IsEqualTo("sam"); - data[1].Author.IsNull(); - cnn.Close(); - } - } - - public class PostCE - { - public int ID { get; set; } - public string Title { get; set; } - public string Body { get; set; } - - public AuthorCE Author { get; set; } - } - - public class AuthorCE - { - public int ID { get; set; } - public string Name { get; set; } - } -#endif -#if LINQ2SQL - [Fact] - public void TestLinqBinaryToClass() - { - byte[] orig = new byte[20]; - new Random(123456).NextBytes(orig); - var input = new System.Data.Linq.Binary(orig); - - var output = connection.Query("select @input as [Value]", new { input }).First().Value; - - output.ToArray().IsSequenceEqualTo(orig); - } - - [Fact] - public void TestLinqBinaryRaw() - { - byte[] orig = new byte[20]; - new Random(123456).NextBytes(orig); - var input = new System.Data.Linq.Binary(orig); - - var output = connection.Query("select @input as [Value]", new { input }).First(); - - output.ToArray().IsSequenceEqualTo(orig); - } - - class WithBinary - { - public System.Data.Linq.Binary Value { get; set; } - } -#endif - - [Fact] - public void TestProcSupport() - { - var p = new DynamicParameters(); - p.Add("a", 11); - p.Add("b", dbType: DbType.Int32, direction: ParameterDirection.Output); - p.Add("c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); - - connection.Execute(@"create proc #TestProc - @a int, - @b int output -as -begin - set @b = 999 - select 1111 - return @a -end"); - connection.Query("#TestProc", p, commandType: CommandType.StoredProcedure).First().IsEqualTo(1111); - - p.Get("c").IsEqualTo(11); - p.Get("b").IsEqualTo(999); - - } - - [Fact] - public void TestDbString() - { - var obj = connection.Query("select datalength(@a) as a, datalength(@b) as b, datalength(@c) as c, datalength(@d) as d, datalength(@e) as e, datalength(@f) as f", - new - { - a = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = true }, - b = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = false }, - c = new DbString { Value = "abcde", IsFixedLength = false, Length = 10, IsAnsi = true }, - d = new DbString { Value = "abcde", IsFixedLength = false, Length = 10, IsAnsi = false }, - e = new DbString { Value = "abcde", IsAnsi = true }, - f = new DbString { Value = "abcde", IsAnsi = false }, - }).First(); - ((int)obj.a).IsEqualTo(10); - ((int)obj.b).IsEqualTo(20); - ((int)obj.c).IsEqualTo(5); - ((int)obj.d).IsEqualTo(10); - ((int)obj.e).IsEqualTo(5); - ((int)obj.f).IsEqualTo(10); - } - - [Fact] - public void TestDefaultDbStringDbType() - { - var origDefaultStringDbType = DbString.IsAnsiDefault; - try - { - DbString.IsAnsiDefault = true; - var a = new DbString { Value = "abcde" }; - var b = new DbString { Value = "abcde", IsAnsi = false }; - a.IsAnsi.IsTrue(); - b.IsAnsi.IsFalse(); - } - finally - { - DbString.IsAnsiDefault = origDefaultStringDbType; - } - } - - [Fact] - public void TestFastExpandoSupportsIDictionary() - { - var row = connection.Query("select 1 A, 'two' B").First() as IDictionary; - row["A"].IsEqualTo(1); - row["B"].IsEqualTo("two"); - } - - [Fact] - public void TestDapperSetsPrivates() - { - connection.Query("select 'one' ShadowInDB").First().Shadow.IsEqualTo(1); - - connection.QueryFirstOrDefault("select 'one' ShadowInDB").Shadow.IsEqualTo(1); - } - - class PrivateDan - { - public int Shadow { get; set; } - private string ShadowInDB - { - set - { - Shadow = value == "one" ? 1 : 0; - } - } - } - - - /* TODO: - * - public void TestMagicParam() - { - // magic params allow you to pass in single params without using an anon class - // this test fails for now, but I would like to support a single param by parsing the sql with regex and remapping. - - var first = connection.Query("select @a as a", 1).First(); - Assert.IsEqualTo(first.a, 1); - } - * */ - - [Fact] - public void TestUnexpectedDataMessage() - { - string msg = null; - try - { - connection.Query("select count(1) where 1 = @Foo", new WithBizarreData { Foo = new GenericUriParser(GenericUriParserOptions.Default), Bar = 23 }).First(); - - } - catch (Exception ex) - { - msg = ex.Message; - } - msg.IsEqualTo("The member Foo of type System.GenericUriParser cannot be used as a parameter value"); - } - - [Fact] - public void TestUnexpectedButFilteredDataMessage() - { - int i = connection.Query("select @Bar", new WithBizarreData { Foo = new GenericUriParser(GenericUriParserOptions.Default), Bar = 23 }).Single(); - - i.IsEqualTo(23); - } - - class WithBizarreData - { - public GenericUriParser Foo { get; set; } - public int Bar { get; set; } - } - - class WithCharValue - { - public char Value { get; set; } - public char? ValueNullable { get; set; } - } - - [Fact] - public void TestCharInputAndOutput() - { - const char test = '〠'; - char c = connection.Query("select @c", new { c = test }).Single(); - - c.IsEqualTo(test); - - var obj = connection.Query("select @Value as Value", new WithCharValue { Value = c }).Single(); - - obj.Value.IsEqualTo(test); - } - - [Fact] - public void TestNullableCharInputAndOutputNonNull() - { - char? test = '〠'; - char? c = connection.Query("select @c", new { c = test }).Single(); - - c.IsEqualTo(test); - - var obj = connection.Query("select @ValueNullable as ValueNullable", new WithCharValue { ValueNullable = c }).Single(); - - obj.ValueNullable.IsEqualTo(test); - } - - [Fact] - public void TestNullableCharInputAndOutputNull() - { - char? test = null; - char? c = connection.Query("select @c", new { c = test }).Single(); - - c.IsEqualTo(test); - - var obj = connection.Query("select @ValueNullable as ValueNullable", new WithCharValue { ValueNullable = c }).Single(); - - obj.ValueNullable.IsEqualTo(test); - } - - [Fact] - public void TestInvalidSplitCausesNiceError() - { - try - { - connection.Query("select 1 A, 2 B, 3 C", (x, y) => x); - } - catch (ArgumentException) - { - // expecting an app exception due to multi mapping being bodged - } - - try - { - connection.Query("select 1 A, 2 B, 3 C", (x, y) => x); - } - catch (ArgumentException) - { - // expecting an app exception due to multi mapping being bodged - } - } - - [Fact] - public void TestCustomParameters() - { - var args = new DbParams { - new SqlParameter("foo", 123), - new SqlParameter("bar", "abc") - }; - var result = connection.Query("select Foo=@foo, Bar=@bar", args).Single(); - int foo = result.Foo; - string bar = result.Bar; - foo.IsEqualTo(123); - bar.IsEqualTo("abc"); - } - - [Fact] - public void TestDynamicParamNullSupport() - { - var p = new DynamicParameters(); - - p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); - connection.Execute("select @b = null", p); - - p.Get("@b").IsNull(); - } - - class WithPrivateConstructor - { - public int Foo { get; set; } - private WithPrivateConstructor() { } - } - - [Fact] - public void TestWithNonPublicConstructor() - { - var output = connection.Query("select 1 as Foo").First(); - output.Foo.IsEqualTo(1); - } - - - [Fact] - public void WorkDespiteHavingWrongStructColumnTypes() - { - var hazInt = connection.Query("select cast(1 as bigint) Value").Single(); - hazInt.Value.Equals(1); - } - - [Fact] - public void TestProcWithOutParameter() - { - connection.Execute( - @"CREATE PROCEDURE #TestProcWithOutParameter - @ID int output, - @Foo varchar(100), - @Bar int - AS - SET @ID = @Bar + LEN(@Foo)"); - var obj = new - { - ID = 0, - Foo = "abc", - Bar = 4 - }; - var args = new DynamicParameters(obj); - args.Add("ID", 0, direction: ParameterDirection.Output); - connection.Execute("#TestProcWithOutParameter", args, commandType: CommandType.StoredProcedure); - args.Get("ID").IsEqualTo(7); - } - - [Fact] - public void TestProcWithOutAndReturnParameter() - { - connection.Execute( - @"CREATE PROCEDURE #TestProcWithOutAndReturnParameter - @ID int output, - @Foo varchar(100), - @Bar int - AS - SET @ID = @Bar + LEN(@Foo) - RETURN 42"); - var obj = new - { - ID = 0, - Foo = "abc", - Bar = 4 - }; - var args = new DynamicParameters(obj); - args.Add("ID", 0, direction: ParameterDirection.Output); - args.Add("result", 0, direction: ParameterDirection.ReturnValue); - connection.Execute("#TestProcWithOutAndReturnParameter", args, commandType: CommandType.StoredProcedure); - args.Get("ID").IsEqualTo(7); - args.Get("result").IsEqualTo(42); - } - struct CanHazInt - { - public int Value { get; set; } - } - - [Fact] - public void TestInt16Usage() - { - connection.Query("select cast(42 as smallint)").Single().IsEqualTo((short)42); - connection.Query("select cast(42 as smallint)").Single().IsEqualTo((short?)42); - connection.Query("select cast(null as smallint)").Single().IsEqualTo((short?)null); - - connection.Query("select cast(42 as smallint)").Single().IsEqualTo((ShortEnum)42); - connection.Query("select cast(42 as smallint)").Single().IsEqualTo((ShortEnum?)42); - connection.Query("select cast(null as smallint)").Single().IsEqualTo((ShortEnum?)null); - - var row = - connection.Query( - "select cast(1 as smallint) as NonNullableInt16, cast(2 as smallint) as NullableInt16, cast(3 as smallint) as NonNullableInt16Enum, cast(4 as smallint) as NullableInt16Enum") - .Single(); - row.NonNullableInt16.IsEqualTo((short)1); - row.NullableInt16.IsEqualTo((short)2); - row.NonNullableInt16Enum.IsEqualTo(ShortEnum.Three); - row.NullableInt16Enum.IsEqualTo(ShortEnum.Four); - - row = - connection.Query( - "select cast(5 as smallint) as NonNullableInt16, cast(null as smallint) as NullableInt16, cast(6 as smallint) as NonNullableInt16Enum, cast(null as smallint) as NullableInt16Enum") - .Single(); - row.NonNullableInt16.IsEqualTo((short)5); - row.NullableInt16.IsEqualTo((short?)null); - row.NonNullableInt16Enum.IsEqualTo(ShortEnum.Six); - row.NullableInt16Enum.IsEqualTo((ShortEnum?)null); - } - - [Fact] - public void TestInt32Usage() - { - connection.Query("select cast(42 as int)").Single().IsEqualTo((int)42); - connection.Query("select cast(42 as int)").Single().IsEqualTo((int?)42); - connection.Query("select cast(null as int)").Single().IsEqualTo((int?)null); - - connection.Query("select cast(42 as int)").Single().IsEqualTo((IntEnum)42); - connection.Query("select cast(42 as int)").Single().IsEqualTo((IntEnum?)42); - connection.Query("select cast(null as int)").Single().IsEqualTo((IntEnum?)null); - - var row = - connection.Query( - "select cast(1 as int) as NonNullableInt32, cast(2 as int) as NullableInt32, cast(3 as int) as NonNullableInt32Enum, cast(4 as int) as NullableInt32Enum") - .Single(); - row.NonNullableInt32.IsEqualTo((int)1); - row.NullableInt32.IsEqualTo((int)2); - row.NonNullableInt32Enum.IsEqualTo(IntEnum.Three); - row.NullableInt32Enum.IsEqualTo(IntEnum.Four); - - row = - connection.Query( - "select cast(5 as int) as NonNullableInt32, cast(null as int) as NullableInt32, cast(6 as int) as NonNullableInt32Enum, cast(null as int) as NullableInt32Enum") - .Single(); - row.NonNullableInt32.IsEqualTo((int)5); - row.NullableInt32.IsEqualTo((int?)null); - row.NonNullableInt32Enum.IsEqualTo(IntEnum.Six); - row.NullableInt32Enum.IsEqualTo((IntEnum?)null); - } - - public class WithInt16Values - { - public short NonNullableInt16 { get; set; } - public short? NullableInt16 { get; set; } - public ShortEnum NonNullableInt16Enum { get; set; } - public ShortEnum? NullableInt16Enum { get; set; } - } - public enum ShortEnum : short - { - Zero = 0, One = 1, Two = 2, Three = 3, Four = 4, Five = 5, Six = 6 - } - public class WithInt32Values - { - public int NonNullableInt32 { get; set; } - public int? NullableInt32 { get; set; } - public IntEnum NonNullableInt32Enum { get; set; } - public IntEnum? NullableInt32Enum { get; set; } - } - public enum IntEnum : int - { - Zero = 0, One = 1, Two = 2, Three = 3, Four = 4, Five = 5, Six = 6 - } - - [Fact] - public void TestTransactionCommit() - { - try - { - connection.Execute("create table #TransactionTest ([ID] int, [Value] varchar(32));"); - - using (var transaction = connection.BeginTransaction()) - { - connection.Execute("insert into #TransactionTest ([ID], [Value]) values (1, 'ABC');", transaction: transaction); - - transaction.Commit(); - } - - connection.Query("select count(*) from #TransactionTest;").Single().IsEqualTo(1); - } - finally - { - connection.Execute("drop table #TransactionTest;"); - } - } - - [Fact] - public void TestTransactionRollback() - { - connection.Execute("create table #TransactionTest ([ID] int, [Value] varchar(32));"); - - try - { - using (var transaction = connection.BeginTransaction()) - { - connection.Execute("insert into #TransactionTest ([ID], [Value]) values (1, 'ABC');", transaction: transaction); - - transaction.Rollback(); - } - - connection.Query("select count(*) from #TransactionTest;").Single().IsEqualTo(0); - } - finally - { - connection.Execute("drop table #TransactionTest;"); - } - } - - [Fact] - public void TestCommandWithInheritedTransaction() - { - connection.Execute("create table #TransactionTest ([ID] int, [Value] varchar(32));"); - - try - { - using (var transaction = connection.BeginTransaction()) - { - var transactedConnection = new TransactedConnection(connection, transaction); - - transactedConnection.Execute("insert into #TransactionTest ([ID], [Value]) values (1, 'ABC');"); - - transaction.Rollback(); - } - - connection.Query("select count(*) from #TransactionTest;").Single().IsEqualTo(0); - } - finally - { - connection.Execute("drop table #TransactionTest;"); - } - } - - [Fact] - public void TestReaderWhenResultsChange() - { - try - { - - connection.Execute("create table #ResultsChange (X int);create table #ResultsChange2 (Y int);insert #ResultsChange (X) values(1);insert #ResultsChange2 (Y) values(1);"); - - var obj1 = connection.Query("select * from #ResultsChange").Single(); - obj1.X.IsEqualTo(1); - obj1.Y.IsEqualTo(0); - obj1.Z.IsEqualTo(0); - - var obj2 = connection.Query("select * from #ResultsChange rc inner join #ResultsChange2 rc2 on rc2.Y=rc.X").Single(); - obj2.X.IsEqualTo(1); - obj2.Y.IsEqualTo(1); - obj2.Z.IsEqualTo(0); - - connection.Execute("alter table #ResultsChange add Z int null"); - connection.Execute("update #ResultsChange set Z = 2"); - - var obj3 = connection.Query("select * from #ResultsChange").Single(); - obj3.X.IsEqualTo(1); - obj3.Y.IsEqualTo(0); - obj3.Z.IsEqualTo(2); - - var obj4 = connection.Query("select * from #ResultsChange rc inner join #ResultsChange2 rc2 on rc2.Y=rc.X").Single(); - obj4.X.IsEqualTo(1); - obj4.Y.IsEqualTo(1); - obj4.Z.IsEqualTo(2); - } - finally - { - connection.Execute("drop table #ResultsChange;drop table #ResultsChange2;"); - } - } - class ResultsChangeType - { - public int X { get; set; } - public int Y { get; set; } - public int Z { get; set; } - } - - [Fact] - public void TestCustomTypeMap() - { - // default mapping - var item = connection.Query("Select 'AVal' as A, 'BVal' as B").Single(); - item.A.IsEqualTo("AVal"); - item.B.IsEqualTo("BVal"); - - // custom mapping - var map = new CustomPropertyTypeMap(typeof(TypeWithMapping), - (type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName)); - Dapper.SqlMapper.SetTypeMap(typeof(TypeWithMapping), map); - - item = connection.Query("Select 'AVal' as A, 'BVal' as B").Single(); - item.A.IsEqualTo("BVal"); - item.B.IsEqualTo("AVal"); - - // reset to default - Dapper.SqlMapper.SetTypeMap(typeof(TypeWithMapping), null); - item = connection.Query("Select 'AVal' as A, 'BVal' as B").Single(); - item.A.IsEqualTo("AVal"); - item.B.IsEqualTo("BVal"); - } - static string GetDescriptionFromAttribute(MemberInfo member) - { - if (member == null) return null; -#if COREFX - var data = member.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DescriptionAttribute)); - return (string) data?.ConstructorArguments.Single().Value; -#else - var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false); - return attrib == null ? null : attrib.Description; -#endif - } - public class TypeWithMapping - { - [Description("B")] - public string A { get; set; } - - [Description("A")] - public string B { get; set; } - } - - public class WrongTypes - { - public int A { get; set; } - public double B { get; set; } - public long C { get; set; } - public bool D { get; set; } - } - - [Fact] - public void TestWrongTypes_WithRightTypes() - { - var item = connection.Query("select 1 as A, cast(2.0 as float) as B, cast(3 as bigint) as C, cast(1 as bit) as D").Single(); - item.A.Equals(1); - item.B.Equals(2.0); - item.C.Equals(3L); - item.D.Equals(true); - } - - [Fact] - public void TestWrongTypes_WithWrongTypes() - { - var item = connection.Query("select cast(1.0 as float) as A, 2 as B, 3 as C, cast(1 as bigint) as D").Single(); - item.A.Equals(1); - item.B.Equals(2.0); - item.C.Equals(3L); - item.D.Equals(true); - } - - - [Fact] - public void Issue_40_AutomaticBoolConversion() - { - var user = connection.Query("select UserId=1,Email='abc',Password='changeme',Active=cast(1 as tinyint)").Single(); - user.Active.IsTrue(); - user.UserID.IsEqualTo(1); - user.Email.IsEqualTo("abc"); - user.Password.IsEqualTo("changeme"); - } - - public class Issue40_User - { - public Issue40_User() - { - Email = Password = string.Empty; - } - public int UserID { get; set; } - public string Email { get; set; } - public string Password { get; set; } - public bool Active { get; set; } - } - - [Fact] - public void ExecuteFromClosed() - { - using (var conn = GetClosedConnection()) - { - conn.Execute("-- nop"); - conn.State.IsEqualTo(ConnectionState.Closed); - } - } - class Multi1 - { - public int Id { get; set; } - } - class Multi2 - { - public int Id { get; set; } - } - - [Fact] - public void QueryMultimapFromClosed() - { - using (var conn = GetClosedConnection()) - { - conn.State.IsEqualTo(ConnectionState.Closed); - var i = conn.Query("select 2 as [Id], 3 as [Id]", (x, y) => x.Id + y.Id).Single(); - conn.State.IsEqualTo(ConnectionState.Closed); - i.IsEqualTo(5); - } - } - - [Fact] - public void QueryMultiple2FromClosed() - { - using (var conn = GetClosedConnection()) - { - conn.State.IsEqualTo(ConnectionState.Closed); - using (var multi = conn.QueryMultiple("select 1 select 2 select 3")) - { - multi.Read().Single().IsEqualTo(1); - multi.Read().Single().IsEqualTo(2); - // not reading 3 is intentional here - } - conn.State.IsEqualTo(ConnectionState.Closed); - } - } - - [Fact] - public void ExecuteInvalidFromClosed() - { - using (var conn = GetClosedConnection()) - { - try - { - conn.Execute("nop"); - false.IsEqualTo(true); // shouldn't have got here - } - catch - { - conn.State.IsEqualTo(ConnectionState.Closed); - } - } - } - - [Fact] - public void QueryFromClosed() - { - using (var conn = GetClosedConnection()) - { - var i = conn.Query("select 1").Single(); - conn.State.IsEqualTo(ConnectionState.Closed); - i.IsEqualTo(1); - } - } - - [Fact] - public void QueryInvalidFromClosed() - { - using (var conn = GetClosedConnection()) - { - try - { - conn.Query("select gibberish").Single(); - false.IsEqualTo(true); // shouldn't have got here - } - catch - { - conn.State.IsEqualTo(ConnectionState.Closed); - } - } - } - - [Fact] - public void QueryMultipleFromClosed() - { - using (var conn = GetClosedConnection()) - { - using (var multi = conn.QueryMultiple("select 1; select 'abc';")) - { - multi.Read().Single().IsEqualTo(1); - multi.Read().Single().IsEqualTo("abc"); - } - conn.State.IsEqualTo(ConnectionState.Closed); - } - } - - [Fact] - public void SO35554284_QueryMultipleUntilConsumed() - { - using (var reader = connection.QueryMultiple("select 1 as Id; select 2 as Id; select 3 as Id;")) - { - List items = new List(); - while (!reader.IsConsumed) - { - items.AddRange(reader.Read()); - } - items.Count.IsEqualTo(3); - items[0].Id.IsEqualTo(1); - items[1].Id.IsEqualTo(2); - items[2].Id.IsEqualTo(3); - } - } - - [Fact] - public void QueryMultipleInvalidFromClosed() - { - using (var conn = GetClosedConnection()) - { - try - { - conn.QueryMultiple("select gibberish"); - false.IsEqualTo(true); // shouldn't have got here - } - catch - { - conn.State.IsEqualTo(ConnectionState.Closed); - } - } - } - - [Fact] - public void TestMultiSelectWithSomeEmptyGridsUnbuffered() - { - TestMultiSelectWithSomeEmptyGrids(false); - } - [Fact] - public void TestMultiSelectWithSomeEmptyGridsBuffered() - { - TestMultiSelectWithSomeEmptyGrids(true); - } - private void TestMultiSelectWithSomeEmptyGrids(bool buffered) - { - using (var reader = connection.QueryMultiple("select 1; select 2 where 1 = 0; select 3 where 1 = 0; select 4;")) - { - var one = reader.Read(buffered: buffered).ToArray(); - var two = reader.Read(buffered: buffered).ToArray(); - var three = reader.Read(buffered: buffered).ToArray(); - var four = reader.Read(buffered: buffered).ToArray(); - try - { // only returned four grids; expect a fifth read to fail - reader.Read(buffered: buffered); - throw new InvalidOperationException("this should not have worked!"); - } - catch (ObjectDisposedException ex) - { // expected; success - ex.Message.IsEqualTo("The reader has been disposed; this can happen after all data has been consumed\r\nObject name: 'Dapper.SqlMapper+GridReader'."); - } - - one.Length.IsEqualTo(1); - one[0].IsEqualTo(1); - two.Length.IsEqualTo(0); - three.Length.IsEqualTo(0); - four.Length.IsEqualTo(1); - four[0].IsEqualTo(4); - } - } - - [Fact] - public void TestDynamicMutation() - { - var obj = connection.Query("select 1 as [a], 2 as [b], 3 as [c]").Single(); - ((int)obj.a).IsEqualTo(1); - IDictionary dict = obj; - Assert.Equals(3, dict.Count); - Assert.IsTrue(dict.Remove("a")); - Assert.IsFalse(dict.Remove("d")); - Assert.Equals(2, dict.Count); - dict.Add("d", 4); - Assert.Equals(3, dict.Count); - Assert.Equals("b,c,d", string.Join(",", dict.Keys.OrderBy(x => x))); - Assert.Equals("2,3,4", string.Join(",", dict.OrderBy(x => x.Key).Select(x => x.Value))); - - Assert.Equals(2, (int)obj.b); - Assert.Equals(3, (int)obj.c); - Assert.Equals(4, (int)obj.d); - try - { - ((int)obj.a).IsEqualTo(1); - throw new InvalidOperationException("should have thrown"); - } - catch (RuntimeBinderException) - { - // pass - } - } - - - [Fact] - public void TestIssue131() - { - var results = connection.Query( - "SELECT 1 Id, 'Mr' Title, 'John' Surname, 4 AddressCount", - (person, addressCount) => person, - splitOn: "AddressCount" - ).FirstOrDefault(); - - var asDict = (IDictionary)results; - - asDict.ContainsKey("Id").IsEqualTo(true); - asDict.ContainsKey("Title").IsEqualTo(true); - asDict.ContainsKey("Surname").IsEqualTo(true); - asDict.ContainsKey("AddressCount").IsEqualTo(false); - } - - // see http://stackoverflow.com/questions/16955357/issue-about-dapper - [Fact] - public void TestSplitWithMissingMembers() - { - var result = connection.Query( - @"select 123 as ID, 'abc' as Title, - cast('01 Feb 2013' as datetime) as CreateDate, - 'ghi' as Name, 'def' as Phone", - (T, P) => { T.Author = P; return T; }, - null, null, true, "ID,Name").Single(); - - result.ID.Equals(123); - result.Title.Equals("abc"); - result.CreateDate.Equals(new DateTime(2013, 2, 1)); - result.Name.IsNull(); - result.Content.IsNull(); - - result.Author.Phone.Equals("def"); - result.Author.Name.Equals("ghi"); - result.Author.ID.Equals(0); - result.Author.Address.IsNull(); - } - public class Profile - { - public int ID { get; set; } - public string Name { get; set; } - public string Phone { get; set; } - public string Address { get; set; } - //public ExtraInfo Extra { get; set; } - } - - public class Topic - { - public int ID { get; set; } - public string Title { get; set; } - public DateTime CreateDate { get; set; } - public string Content { get; set; } - public int UID { get; set; } - public int TestColum { get; set; } - public string Name { get; set; } - public Profile Author { get; set; } - //public Attachment Attach { get; set; } - } - - // see http://stackoverflow.com/questions/13127886/dapper-returns-null-for-singleordefaultdatediff - [Fact] - public void TestNullFromInt_NoRows() - { - var result = connection.Query( // case with rows - "select DATEDIFF(day, GETUTCDATE(), @date)", new { date = DateTime.UtcNow.AddDays(20) }) - .SingleOrDefault(); - result.IsEqualTo(20); - - result = connection.Query( // case without rows - "select DATEDIFF(day, GETUTCDATE(), @date) where 1 = 0", new { date = DateTime.UtcNow.AddDays(20) }) - .SingleOrDefault(); - result.IsEqualTo(0); // zero rows; default of int over zero rows is zero - - - } - - [Fact] - public void TestChangingDefaultStringTypeMappingToAnsiString() - { - var sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType"; - var param = new { testParam = "TestString" }; - - var result01 = connection.Query(sql, param).FirstOrDefault(); - result01.IsEqualTo("nvarchar"); - - Dapper.SqlMapper.PurgeQueryCache(); - - Dapper.SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString - var result02 = connection.Query(sql, param).FirstOrDefault(); - result02.IsEqualTo("varchar"); - - Dapper.SqlMapper.PurgeQueryCache(); - Dapper.SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String - } - - [Fact] - public void TestChangingDefaultStringTypeMappingToAnsiStringFirstOrDefault() - { - var sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType"; - var param = new { testParam = "TestString" }; - - var result01 = connection.QueryFirstOrDefault(sql, param); - result01.IsEqualTo("nvarchar"); - - Dapper.SqlMapper.PurgeQueryCache(); - - Dapper.SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString - var result02 = connection.QueryFirstOrDefault(sql, param); - result02.IsEqualTo("varchar"); - - Dapper.SqlMapper.PurgeQueryCache(); - Dapper.SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String - } - - class TransactedConnection : IDbConnection - { - IDbConnection _conn; - IDbTransaction _tran; - - public TransactedConnection(IDbConnection conn, IDbTransaction tran) - { - _conn = conn; - _tran = tran; - } - - public string ConnectionString { get { return _conn.ConnectionString; } set { _conn.ConnectionString = value; } } - public int ConnectionTimeout { get { return _conn.ConnectionTimeout; } } - public string Database { get { return _conn.Database; } } - public ConnectionState State { get { return _conn.State; } } - - public IDbTransaction BeginTransaction(IsolationLevel il) - { - throw new NotImplementedException(); - } - - public IDbTransaction BeginTransaction() - { - return _tran; - } - - public void ChangeDatabase(string databaseName) - { - _conn.ChangeDatabase(databaseName); - } - - public void Close() - { - _conn.Close(); - } - - public IDbCommand CreateCommand() - { - // The command inherits the "current" transaction. - var command = _conn.CreateCommand(); - command.Transaction = _tran; - return command; - } - - public void Dispose() - { - _conn.Dispose(); - } - - public void Open() - { - _conn.Open(); - } - } - - [Fact] - public void TestDapperTableMetadataRetrieval() - { - // Test for a bug found in CS 51509960 where the following sequence would result in an InvalidOperationException being - // thrown due to an attempt to access a disposed of DataReader: - // - // - Perform a dynamic query that yields no results - // - Add data to the source of that query - // - Perform a the same query again - connection.Execute("CREATE TABLE #sut (value varchar(10) NOT NULL PRIMARY KEY)"); - connection.Query("SELECT value FROM #sut").IsSequenceEqualTo(Enumerable.Empty()); - - connection.Execute("INSERT INTO #sut (value) VALUES ('test')").IsEqualTo(1); - var result = connection.Query("SELECT value FROM #sut"); - - var first = result.First(); - ((string)first.value).IsEqualTo("test"); - } - - [Fact] - public void TestIssue17648290() - { - var p = new DynamicParameters(); - int code = 1, getMessageControlId = 2; - p.Add("@Code", code); - p.Add("@MessageControlID", getMessageControlId); - p.Add("@SuccessCode", dbType: DbType.Int32, direction: ParameterDirection.Output); - p.Add("@ErrorDescription", dbType: DbType.String, direction: ParameterDirection.Output, size: 255); - connection.Execute(@"CREATE PROCEDURE #up_MessageProcessed_get - @Code varchar(10), - @MessageControlID varchar(22), - @SuccessCode int OUTPUT, - @ErrorDescription varchar(255) OUTPUT - AS - - BEGIN - - Select 2 as MessageProcessID, 38349348 as StartNum, 3874900 as EndNum, GETDATE() as StartDate, GETDATE() as EndDate - SET @SuccessCode = 0 - SET @ErrorDescription = 'Completed successfully' - END"); - var result = connection.Query(sql: "#up_MessageProcessed_get", param: p, commandType: CommandType.StoredProcedure); - var row = result.Single(); - ((int)row.MessageProcessID).IsEqualTo(2); - ((int)row.StartNum).IsEqualTo(38349348); - ((int)row.EndNum).IsEqualTo(3874900); - DateTime startDate = row.StartDate, endDate = row.EndDate; - p.Get("SuccessCode").IsEqualTo(0); - p.Get("ErrorDescription").IsEqualTo("Completed successfully"); - } - - [Fact] - public void TestDoubleDecimalConversions_SO18228523_RightWay() - { - var row = connection.Query( - "select cast(1 as float) as A, cast(2 as float) as B, cast(3 as decimal) as C, cast(4 as decimal) as D").Single(); - row.A.Equals(1.0); - row.B.Equals(2.0); - row.C.Equals(3.0M); - row.D.Equals(4.0M); - } - - [Fact] - public void TestDoubleDecimalConversions_SO18228523_WrongWay() - { - var row = connection.Query( - "select cast(1 as decimal) as A, cast(2 as decimal) as B, cast(3 as float) as C, cast(4 as float) as D").Single(); - row.A.Equals(1.0); - row.B.Equals(2.0); - row.C.Equals(3.0M); - row.D.Equals(4.0M); - } - - [Fact] - public void TestDoubleDecimalConversions_SO18228523_Nulls() - { - var row = connection.Query( - "select cast(null as decimal) as A, cast(null as decimal) as B, cast(null as float) as C, cast(null as float) as D").Single(); - row.A.Equals(0.0); - row.B.IsNull(); - row.C.Equals(0.0M); - row.D.IsNull(); - } - - private static CultureInfo ActiveCulture - { -#if COREFX - get { return CultureInfo.CurrentCulture; } - set { CultureInfo.CurrentCulture = value; } -#else - get { return Thread.CurrentThread.CurrentCulture; } - set { Thread.CurrentThread.CurrentCulture = value; } -#endif - } - - [FactUnlessCaseSensitiveDatabase] - public void TestParameterInclusionNotSensitiveToCurrentCulture() - { - // note this might fail if your database server is case-sensitive - CultureInfo current = ActiveCulture; - try - { - ActiveCulture = new CultureInfo("tr-TR"); - - connection.Query("select @pid", new { PId = 1 }).Single(); - } - finally - { - ActiveCulture = current; - } - } - - [Fact] - public void LiteralReplacement() - { - connection.Execute("create table #literal1 (id int not null, foo int not null)"); - connection.Execute("insert #literal1 (id,foo) values ({=id}, @foo)", new { id = 123, foo = 456 }); - var rows = new[] { new { id = 1, foo = 2 }, new { id = 3, foo = 4 } }; - connection.Execute("insert #literal1 (id,foo) values ({=id}, @foo)", rows); - var count = connection.Query("select count(1) from #literal1 where id={=foo}", new { foo = 123 }).Single(); - count.IsEqualTo(1); - int sum = connection.Query("select sum(id) + sum(foo) from #literal1").Single(); - sum.IsEqualTo(123 + 456 + 1 + 2 + 3 + 4); - } - - [Fact] - public void LiteralReplacementDynamic() - { - var args = new DynamicParameters(); - args.Add("id", 123); - connection.Execute("create table #literal2 (id int not null)"); - connection.Execute("insert #literal2 (id) values ({=id})", args); - - args = new DynamicParameters(); - args.Add("foo", 123); - var count = connection.Query("select count(1) from #literal2 where id={=foo}", args).Single(); - count.IsEqualTo(1); - } - - enum AnEnum - { - A = 2, - B = 1 - } - enum AnotherEnum : byte - { - A = 2, - B = 1 - } - - [Fact] - public void AdoNetEnumValue() - { - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "select @foo"; - var p = cmd.CreateParameter(); - p.ParameterName = "@foo"; - p.DbType = DbType.Int32; // it turns out that this is the key piece; setting the DbType - p.Value = AnEnum.B; - cmd.Parameters.Add(p); - object value = cmd.ExecuteScalar(); - AnEnum val = (AnEnum)value; - val.IsEqualTo(AnEnum.B); - } - } - - [Fact] - public void DapperEnumValue_SqlServer() - { - DapperEnumValue(connection); - } - -#if SQLITE - [FactSqlite] - public void DapperEnumValue_Sqlite() - { - using (var connection = GetSqliteConnection()) - { - DapperEnumValue(connection); - } - } -#endif -#if MYSQL - [FactMySql] - public void DapperEnumValue_Mysql() - { - using (var connection = GetMySqlConnection()) - { - DapperEnumValue(connection); - } - } -#endif - private static void DapperEnumValue(IDbConnection connection) - { - // test passing as AsEnum, reading as int - var v = (AnEnum)connection.QuerySingle("select @v, @y, @z", new { v = AnEnum.B, y = (AnEnum?)AnEnum.B, z = (AnEnum?)null }); - v.IsEqualTo(AnEnum.B); - - var args = new DynamicParameters(); - args.Add("v", AnEnum.B); - args.Add("y", AnEnum.B); - args.Add("z", null); - v = (AnEnum)connection.QuerySingle("select @v, @y, @z", args); - v.IsEqualTo(AnEnum.B); - - // test passing as int, reading as AnEnum - var k = (int)connection.QuerySingle("select @v, @y, @z", new { v = (int)AnEnum.B, y = (int?)(int)AnEnum.B, z = (int?)null }); - k.IsEqualTo((int)AnEnum.B); - - args = new DynamicParameters(); - args.Add("v", (int)AnEnum.B); - args.Add("y", (int)AnEnum.B); - args.Add("z", null); - k = (int)connection.QuerySingle("select @v, @y, @z", args); - k.IsEqualTo((int)AnEnum.B); - } - - [Fact] - public void LiteralReplacementEnumAndString() - { - var args = new { x = AnEnum.B, y = 123.45M, z = AnotherEnum.A }; - var row = connection.Query("select {=x} as x,{=y} as y,cast({=z} as tinyint) as z", args).Single(); - AnEnum x = (AnEnum)(int)row.x; - decimal y = row.y; - AnotherEnum z = (AnotherEnum)(byte)row.z; - x.Equals(AnEnum.B); - y.Equals(123.45M); - z.Equals(AnotherEnum.A); - } - - [Fact] - public void LiteralReplacementDynamicEnumAndString() - { - var args = new DynamicParameters(); - args.Add("x", AnEnum.B); - args.Add("y", 123.45M); - args.Add("z", AnotherEnum.A); - var row = connection.Query("select {=x} as x,{=y} as y,cast({=z} as tinyint) as z", args).Single(); - AnEnum x = (AnEnum)(int)row.x; - decimal y = row.y; - AnotherEnum z = (AnotherEnum)(byte)row.z; - x.Equals(AnEnum.B); - y.Equals(123.45M); - z.Equals(AnotherEnum.A); - } - - [Fact] - public void LiteralReplacementBoolean() - { - var row = connection.Query("select 42 where 1 = {=val}", new { val = true }).SingleOrDefault(); - row.IsNotNull(); - row.IsEqualTo(42); - row = connection.Query("select 42 where 1 = {=val}", new { val = false }).SingleOrDefault(); - row.IsNull(); - } - - [Fact] - public void LiteralReplacementWithIn() - { - var data = connection.Query("select @x where 1 in @ids and 1 ={=a}", - new { x = 1, ids = new[] { 1, 2, 3 }, a = 1 }).ToList(); - } - - class MyRow - { - public int x { get; set; } - } - - [Fact] - public void LiteralIn() - { - connection.Execute("create table #literalin(id int not null);"); - connection.Execute("insert #literalin (id) values (@id)", new[] { - new { id = 1 }, - new { id = 2 }, - new { id = 3 }, - }); - var count = connection.Query("select count(1) from #literalin where id in {=ids}", - new { ids = new[] { 1, 3, 4 } }).Single(); - count.IsEqualTo(2); - } - - [Fact] - public void DbStringAnsi() - { - var a = connection.Query("select datalength(@x)", - new { x = new DbString { Value = "abc", IsAnsi = true } }).Single(); - var b = connection.Query("select datalength(@x)", - new { x = new DbString { Value = "abc", IsAnsi = false } }).Single(); - a.IsEqualTo(3); - b.IsEqualTo(6); - } - - class HasInt32 - { - public int Value { get; set; } - } - - // http://stackoverflow.com/q/23696254/23354 - [Fact] - public void DownwardIntegerConversion() - { - const string sql = "select cast(42 as bigint) as Value"; - int i = connection.Query(sql).Single().Value; - Assert.IsEqualTo(42, i); - - i = connection.Query(sql).Single(); - Assert.IsEqualTo(42, i); - } - - class HasDoubleDecimal - { - public double A { get; set; } - public double? B { get; set; } - public decimal C { get; set; } - public decimal? D { get; set; } - } - - [Fact] - public void GuidIn_SO_24177902() - { - // invent and populate - Guid a = Guid.NewGuid(), b = Guid.NewGuid(), c = Guid.NewGuid(), d = Guid.NewGuid(); - connection.Execute("create table #foo (i int, g uniqueidentifier)"); - connection.Execute("insert #foo(i,g) values(@i,@g)", - new[] { new { i = 1, g = a }, new { i = 2, g = b }, - new { i = 3, g = c },new { i = 4, g = d }}); - - // check that rows 2&3 yield guids b&c - var guids = connection.Query("select g from #foo where i in (2,3)").ToArray(); - guids.Length.Equals(2); - guids.Contains(a).Equals(false); - guids.Contains(b).Equals(true); - guids.Contains(c).Equals(true); - guids.Contains(d).Equals(false); - - // in query on the guids - var rows = connection.Query("select * from #foo where g in @guids order by i", new { guids }) - .Select(row => new { i = (int)row.i, g = (Guid)row.g }).ToArray(); - rows.Length.Equals(2); - rows[0].i.Equals(2); - rows[0].g.Equals(b); - rows[1].i.Equals(3); - rows[1].g.Equals(c); - } - - [Fact] - public void TypeBasedViaDynamic() - { - Type type = GetSomeType(); - - dynamic template = Activator.CreateInstance(type); - dynamic actual = CheetViaDynamic(template, "select @A as [A], @B as [B]", new { A = 123, B = "abc" }); - ((object)actual).GetType().IsEqualTo(type); - int a = actual.A; - string b = actual.B; - a.IsEqualTo(123); - b.IsEqualTo("abc"); - } - - [Fact] - public void TypeBasedViaType() - { - Type type = GetSomeType(); - - dynamic actual = connection.Query(type, "select @A as [A], @B as [B]", new { A = 123, B = "abc" }).FirstOrDefault(); - ((object)actual).GetType().IsEqualTo(type); - int a = actual.A; - string b = actual.B; - a.IsEqualTo(123); - b.IsEqualTo("abc"); - } - - [Fact] - public void TypeBasedViaTypeMulti() - { - Type type = GetSomeType(); - - dynamic first, second; - using (var multi = connection.QueryMultiple("select @A as [A], @B as [B]; select @C as [A], @D as [B]", - new { A = 123, B = "abc", C = 456, D = "def" })) - { - first = multi.Read(type).Single(); - second = multi.Read(type).Single(); - } - ((object)first).GetType().IsEqualTo(type); - int a = first.A; - string b = first.B; - a.IsEqualTo(123); - b.IsEqualTo("abc"); - - ((object)second).GetType().IsEqualTo(type); - a = second.A; - b = second.B; - a.IsEqualTo(456); - b.IsEqualTo("def"); - } - - private T CheetViaDynamic(T template, string query, object args) - { - return connection.Query(query, args).SingleOrDefault(); - } - static Type GetSomeType() - { - return typeof(SomeType); - } - public class SomeType - { - public int A { get; set; } - public string B { get; set; } - } -#if !COREFX - class WithInit : ISupportInitialize - { - public string Value { get; set; } - public int Flags { get; set; } - - void ISupportInitialize.BeginInit() - { - Flags += 1; - } - - void ISupportInitialize.EndInit() - { - Flags += 30; - } - } -#endif - - [Fact] - public void SO24607639_NullableBools() - { - var obj = connection.Query( - @"declare @vals table (A bit null, B bit null, C bit null); - insert @vals (A,B,C) values (1,0,null); - select * from @vals").Single(); - obj.IsNotNull(); - obj.A.Value.IsEqualTo(true); - obj.B.Value.IsEqualTo(false); - obj.C.IsNull(); - } - class HazBools - { - public bool? A { get; set; } - public bool? B { get; set; } - public bool? C { get; set; } - } - - [Fact] - public void SO24605346_ProcsAndStrings() - { - connection.Execute(@"create proc #GetPracticeRebateOrderByInvoiceNumber @TaxInvoiceNumber nvarchar(20) as - select @TaxInvoiceNumber as [fTaxInvoiceNumber]"); - string InvoiceNumber = "INV0000000028PPN"; - var result = connection.Query("#GetPracticeRebateOrderByInvoiceNumber", new - { - TaxInvoiceNumber = InvoiceNumber - }, commandType: CommandType.StoredProcedure).FirstOrDefault(); - - result.TaxInvoiceNumber.IsEqualTo("INV0000000028PPN"); - } - class PracticeRebateOrders - { - public string fTaxInvoiceNumber; -#if !COREFX - [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)] -#endif - public string TaxInvoiceNumber { get { return fTaxInvoiceNumber; } set { fTaxInvoiceNumber = value; } } - } - - public class RatingValueHandler : Dapper.SqlMapper.TypeHandler - { - private RatingValueHandler() { } - public static readonly RatingValueHandler Default = new RatingValueHandler(); - public override RatingValue Parse(object value) - { - if (value is Int32) - return new RatingValue() { Value = (Int32)value }; - - throw new FormatException("Invalid conversion to RatingValue"); - } - - public override void SetValue(IDbDataParameter parameter, RatingValue value) - { - // ... null, range checks etc ... - parameter.DbType = System.Data.DbType.Int32; - parameter.Value = value.Value; - } - } - public class RatingValue - { - public Int32 Value { get; set; } - // ... some other properties etc ... - } - - public class MyResult - { - public String CategoryName { get; set; } - public RatingValue CategoryRating { get; set; } - } - - [Fact] - public void SO24740733_TestCustomValueHandler() - { - Dapper.SqlMapper.AddTypeHandler(RatingValueHandler.Default); - var foo = connection.Query("SELECT 'Foo' AS CategoryName, 200 AS CategoryRating").Single(); - - foo.CategoryName.IsEqualTo("Foo"); - foo.CategoryRating.Value.IsEqualTo(200); - } - - enum SO27024806Enum { Foo, Bar } - - private class SO27024806Class - { - public SO27024806Class(SO27024806Enum myField) - { - this.MyField = myField; - } - - public SO27024806Enum MyField { get; set; } - } - - [Fact] - public void SO27024806_TestVarcharEnumMemberWithExplicitConstructor() - { - var foo = connection.Query("SELECT 'Foo' AS myField").Single(); - foo.MyField.IsEqualTo(SO27024806Enum.Foo); - } - - - [Fact] - public void SO24740733_TestCustomValueSingleColumn() - { - Dapper.SqlMapper.AddTypeHandler(RatingValueHandler.Default); - var foo = connection.Query("SELECT 200 AS CategoryRating").Single(); - - foo.Value.IsEqualTo(200); - } - - public class StringListTypeHandler : Dapper.SqlMapper.TypeHandler> - { - private StringListTypeHandler() { } - public static readonly StringListTypeHandler Default = new StringListTypeHandler(); - //Just a simple List type handler implementation - public override void SetValue(IDbDataParameter parameter, List value) - { - parameter.Value = String.Join(",", value); - } - - public override List Parse(object value) - { - return ((value as String) ?? "").Split(',').ToList(); - } - } - public class MyObjectWithStringList - { - public List Names { get; set; } - } - - [Fact] - public void Issue253_TestIEnumerableTypeHandlerParsing() - { - Dapper.SqlMapper.ResetTypeHandlers(); - Dapper.SqlMapper.AddTypeHandler(StringListTypeHandler.Default); - var foo = connection.Query("SELECT 'Sam,Kyro' AS Names").Single(); - foo.Names.IsSequenceEqualTo(new[] { "Sam", "Kyro" }); - } - - [Fact] - public void Issue253_TestIEnumerableTypeHandlerSetParameterValue() - { - Dapper.SqlMapper.ResetTypeHandlers(); - Dapper.SqlMapper.AddTypeHandler(StringListTypeHandler.Default); - - connection.Execute("CREATE TABLE #Issue253 (Names VARCHAR(50) NOT NULL);"); - try - { - String names = "Sam,Kyro"; - List names_list = names.Split(',').ToList(); - var foo = connection.Query("INSERT INTO #Issue253 (Names) VALUES (@Names); SELECT Names FROM #Issue253;", new { Names = names_list }).Single(); - foo.IsEqualTo(names); - } - finally - { - connection.Execute("DROP TABLE #Issue253;"); - } - } - - - public class RecordingTypeHandler : Dapper.SqlMapper.TypeHandler - { - public override void SetValue(IDbDataParameter parameter, T value) - { - SetValueWasCalled = true; - parameter.Value = value; - } - - public override T Parse(object value) - { - ParseWasCalled = true; - return (T)value; - } - - public bool SetValueWasCalled { get; set; } - public bool ParseWasCalled { get; set; } - } - - [Fact] - public void Test_RemoveTypeMap() - { - Dapper.SqlMapper.ResetTypeHandlers(); - Dapper.SqlMapper.RemoveTypeMap(typeof(DateTime)); - - var dateTimeHandler = new RecordingTypeHandler(); - Dapper.SqlMapper.AddTypeHandler(dateTimeHandler); - - connection.Execute("CREATE TABLE #Test_RemoveTypeMap (x datetime NOT NULL);"); - - try - { - connection.Execute(@"INSERT INTO #Test_RemoveTypeMap VALUES (@Now)", new { DateTime.Now }); - connection.Query("SELECT * FROM #Test_RemoveTypeMap"); - - dateTimeHandler.ParseWasCalled.IsTrue(); - dateTimeHandler.SetValueWasCalled.IsTrue(); - } - finally - { - connection.Execute("DROP TABLE #Test_RemoveTypeMap"); - Dapper.SqlMapper.AddTypeMap(typeof(DateTime), DbType.DateTime); // or an option to reset type map? - } - - } - - [Fact] - public void Issue130_IConvertible() - { - dynamic row = connection.Query("select 1 as [a], '2' as [b]").Single(); - int a = row.a; - string b = row.b; - a.IsEqualTo(1); - b.IsEqualTo("2"); - - row = connection.Query("select 3 as [a], '4' as [b]").Single(); - a = row.a; - b = row.b; - a.IsEqualTo(3); - b.IsEqualTo("4"); - } - - [Fact] - public void Issue22_ExecuteScalar() - { - int i = connection.ExecuteScalar("select 123"); - i.IsEqualTo(123); - - i = connection.ExecuteScalar("select cast(123 as bigint)"); - i.IsEqualTo(123); - - long j = connection.ExecuteScalar("select 123"); - j.IsEqualTo(123L); - - j = connection.ExecuteScalar("select cast(123 as bigint)"); - j.IsEqualTo(123L); - - int? k = connection.ExecuteScalar("select @i", new { i = default(int?) }); - k.IsNull(); - -#if ENTITY_FRAMEWORK - Dapper.EntityFramework.Handlers.Register(); - var geo = DbGeography.LineFromText("LINESTRING(-122.360 47.656, -122.343 47.656 )", 4326); - var geo2 = connection.ExecuteScalar("select @geo", new { geo }); - geo2.IsNotNull(); -#endif - } - -#if ENTITY_FRAMEWORK - public void Issue570_DbGeo_HasValues() - { - Dapper.EntityFramework.Handlers.Register(); - string redmond = "POINT (122.1215 47.6740)"; - DbGeography point = DbGeography.PointFromText(redmond, DbGeography.DefaultCoordinateSystemId); - DbGeography orig = point.Buffer(20); - - - var fromDb = connection.QuerySingle("declare @geos table(geo geography); insert @geos(geo) values(@val); select * from @geos", - new { val = orig }); - - fromDb.Area.IsNotNull(); - fromDb.Area.IsEqualTo(orig.Area); - } -#endif - [Fact] - public void Issue142_FailsNamedStatus() - { - var row1 = connection.Query("select @Status as [Status]", new { Status = StatusType.Started }).Single(); - row1.Status.IsEqualTo(StatusType.Started); - - var row2 = connection.Query("select @Status as [Status]", new { Status = Status.Started }).Single(); - row2.Status.IsEqualTo(Status.Started); - } - - public class Issue142_Status - { - public StatusType Status { get; set; } - } - public class Issue142_StatusType - { - public Status Status { get; set; } - } - - public enum StatusType : byte - { - NotStarted = 1, Started = 2, Finished = 3 - } - public enum Status : byte - { - NotStarted = 1, Started = 2, Finished = 3 - } - - [Fact] - public void Issue136_ValueTypeHandlers() - { - Dapper.SqlMapper.ResetTypeHandlers(); - Dapper.SqlMapper.AddTypeHandler(typeof(LocalDate), LocalDateHandler.Default); - var param = new LocalDateResult - { - NotNullable = new LocalDate { Year = 2014, Month = 7, Day = 25 }, - NullableNotNull = new LocalDate { Year = 2014, Month = 7, Day = 26 }, - NullableIsNull = null, - }; - - var result = connection.Query("SELECT @NotNullable AS NotNullable, @NullableNotNull AS NullableNotNull, @NullableIsNull AS NullableIsNull", param).Single(); - - Dapper.SqlMapper.ResetTypeHandlers(); - Dapper.SqlMapper.AddTypeHandler(typeof(LocalDate?), LocalDateHandler.Default); - result = connection.Query("SELECT @NotNullable AS NotNullable, @NullableNotNull AS NullableNotNull, @NullableIsNull AS NullableIsNull", param).Single(); - } - - public class LocalDateHandler : Dapper.SqlMapper.TypeHandler - { - private LocalDateHandler() { } - - // Make the field type ITypeHandler to ensure it cannot be used with SqlMapper.AddTypeHandler(TypeHandler) - // by mistake. - public static readonly Dapper.SqlMapper.ITypeHandler Default = new LocalDateHandler(); - - public override LocalDate Parse(object value) - { - var date = (DateTime)value; - return new LocalDate { Year = date.Year, Month = date.Month, Day = date.Day }; - } - - public override void SetValue(IDbDataParameter parameter, LocalDate value) - { - parameter.DbType = DbType.DateTime; - parameter.Value = new DateTime(value.Year, value.Month, value.Day); - } - } - - public struct LocalDate - { - public int Year { get; set; } - public int Month { get; set; } - public int Day { get; set; } - } - - public class LocalDateResult - { - public LocalDate NotNullable { get; set; } - public LocalDate? NullableNotNull { get; set; } - public LocalDate? NullableIsNull { get; set; } - } - - public class LotsOfNumerics - { - public enum E_Byte : byte { A = 0, B = 1 } - public enum E_SByte : sbyte { A = 0, B = 1 } - public enum E_Short : short { A = 0, B = 1 } - public enum E_UShort : ushort { A = 0, B = 1 } - public enum E_Int : int { A = 0, B = 1 } - public enum E_UInt : uint { A = 0, B = 1 } - public enum E_Long : long { A = 0, B = 1 } - public enum E_ULong : ulong { A = 0, B = 1 } - - public E_Byte P_Byte { get; set; } - public E_SByte P_SByte { get; set; } - public E_Short P_Short { get; set; } - public E_UShort P_UShort { get; set; } - public E_Int P_Int { get; set; } - public E_UInt P_UInt { get; set; } - public E_Long P_Long { get; set; } - public E_ULong P_ULong { get; set; } - - public bool N_Bool { get; set; } - public byte N_Byte { get; set; } - public sbyte N_SByte { get; set; } - public short N_Short { get; set; } - public ushort N_UShort { get; set; } - public int N_Int { get; set; } - public uint N_UInt { get; set; } - public long N_Long { get; set; } - public ulong N_ULong { get; set; } - - public float N_Float { get; set; } - public double N_Double { get; set; } - public decimal N_Decimal { get; set; } - - public E_Byte? N_P_Byte { get; set; } - public E_SByte? N_P_SByte { get; set; } - public E_Short? N_P_Short { get; set; } - public E_UShort? N_P_UShort { get; set; } - public E_Int? N_P_Int { get; set; } - public E_UInt? N_P_UInt { get; set; } - public E_Long? N_P_Long { get; set; } - public E_ULong? N_P_ULong { get; set; } - - public bool? N_N_Bool { get; set; } - public byte? N_N_Byte { get; set; } - public sbyte? N_N_SByte { get; set; } - public short? N_N_Short { get; set; } - public ushort? N_N_UShort { get; set; } - public int? N_N_Int { get; set; } - public uint? N_N_UInt { get; set; } - public long? N_N_Long { get; set; } - public ulong? N_N_ULong { get; set; } - - public float? N_N_Float { get; set; } - public double? N_N_Double { get; set; } - public decimal? N_N_Decimal { get; set; } - } - - [Fact] - public void TestBigIntForEverythingWorks_SqlLite() - { - TestBigIntForEverythingWorks_SqlLite_ByDataType("bigint"); - TestBigIntForEverythingWorks_SqlLite_ByDataType("int"); - TestBigIntForEverythingWorks_SqlLite_ByDataType("tinyint"); - TestBigIntForEverythingWorks_SqlLite_ByDataType("smallint"); - TestBigIntForEverythingWorks_SqlLite_ByDataType("bit"); - TestBigIntForEverythingWorks_SqlLite_ByDataType("float(24)"); - TestBigIntForEverythingWorks_SqlLite_ByDataType("float(53)"); - } - - private void TestBigIntForEverythingWorks_SqlLite_ByDataType(string dbType) - { - using (var reader = connection.ExecuteReader("select cast(1 as " + dbType + ")")) - { - reader.Read().IsTrue(); - reader.GetFieldType(0).Equals(typeof(T)); - reader.Read().IsFalse(); - reader.NextResult().IsFalse(); - } - - string sql = "select " + string.Join(",", typeof(LotsOfNumerics).GetProperties().Select( - x => "cast (1 as " + dbType + ") as [" + x.Name + "]")); - var row = connection.Query(sql).Single(); - - row.N_Bool.IsTrue(); - row.N_SByte.IsEqualTo((sbyte)1); - row.N_Byte.IsEqualTo((byte)1); - row.N_Int.IsEqualTo((int)1); - row.N_UInt.IsEqualTo((uint)1); - row.N_Short.IsEqualTo((short)1); - row.N_UShort.IsEqualTo((ushort)1); - row.N_Long.IsEqualTo((long)1); - row.N_ULong.IsEqualTo((ulong)1); - row.N_Float.IsEqualTo((float)1); - row.N_Double.IsEqualTo((double)1); - row.N_Decimal.IsEqualTo((decimal)1); - - row.P_Byte.IsEqualTo(LotsOfNumerics.E_Byte.B); - row.P_SByte.IsEqualTo(LotsOfNumerics.E_SByte.B); - row.P_Short.IsEqualTo(LotsOfNumerics.E_Short.B); - row.P_UShort.IsEqualTo(LotsOfNumerics.E_UShort.B); - row.P_Int.IsEqualTo(LotsOfNumerics.E_Int.B); - row.P_UInt.IsEqualTo(LotsOfNumerics.E_UInt.B); - row.P_Long.IsEqualTo(LotsOfNumerics.E_Long.B); - row.P_ULong.IsEqualTo(LotsOfNumerics.E_ULong.B); - - row.N_N_Bool.Value.IsTrue(); - row.N_N_SByte.Value.IsEqualTo((sbyte)1); - row.N_N_Byte.Value.IsEqualTo((byte)1); - row.N_N_Int.Value.IsEqualTo((int)1); - row.N_N_UInt.Value.IsEqualTo((uint)1); - row.N_N_Short.Value.IsEqualTo((short)1); - row.N_N_UShort.Value.IsEqualTo((ushort)1); - row.N_N_Long.Value.IsEqualTo((long)1); - row.N_N_ULong.Value.IsEqualTo((ulong)1); - row.N_N_Float.Value.IsEqualTo((float)1); - row.N_N_Double.Value.IsEqualTo((double)1); - row.N_N_Decimal.IsEqualTo((decimal)1); - - row.N_P_Byte.Value.IsEqualTo(LotsOfNumerics.E_Byte.B); - row.N_P_SByte.Value.IsEqualTo(LotsOfNumerics.E_SByte.B); - row.N_P_Short.Value.IsEqualTo(LotsOfNumerics.E_Short.B); - row.N_P_UShort.Value.IsEqualTo(LotsOfNumerics.E_UShort.B); - row.N_P_Int.Value.IsEqualTo(LotsOfNumerics.E_Int.B); - row.N_P_UInt.Value.IsEqualTo(LotsOfNumerics.E_UInt.B); - row.N_P_Long.Value.IsEqualTo(LotsOfNumerics.E_Long.B); - row.N_P_ULong.Value.IsEqualTo(LotsOfNumerics.E_ULong.B); - - TestBigIntForEverythingWorks(true, dbType); - TestBigIntForEverythingWorks((sbyte)1, dbType); - TestBigIntForEverythingWorks((byte)1, dbType); - TestBigIntForEverythingWorks((int)1, dbType); - TestBigIntForEverythingWorks((uint)1, dbType); - TestBigIntForEverythingWorks((short)1, dbType); - TestBigIntForEverythingWorks((ushort)1, dbType); - TestBigIntForEverythingWorks((long)1, dbType); - TestBigIntForEverythingWorks((ulong)1, dbType); - TestBigIntForEverythingWorks((float)1, dbType); - TestBigIntForEverythingWorks((double)1, dbType); - TestBigIntForEverythingWorks((decimal)1, dbType); - - TestBigIntForEverythingWorks(LotsOfNumerics.E_Byte.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_SByte.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_Int.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_UInt.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_Short.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_UShort.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_Long.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_ULong.B, dbType); - - TestBigIntForEverythingWorks(true, dbType); - TestBigIntForEverythingWorks((sbyte)1, dbType); - TestBigIntForEverythingWorks((byte)1, dbType); - TestBigIntForEverythingWorks((int)1, dbType); - TestBigIntForEverythingWorks((uint)1, dbType); - TestBigIntForEverythingWorks((short)1, dbType); - TestBigIntForEverythingWorks((ushort)1, dbType); - TestBigIntForEverythingWorks((long)1, dbType); - TestBigIntForEverythingWorks((ulong)1, dbType); - TestBigIntForEverythingWorks((float)1, dbType); - TestBigIntForEverythingWorks((double)1, dbType); - TestBigIntForEverythingWorks((decimal)1, dbType); - - TestBigIntForEverythingWorks(LotsOfNumerics.E_Byte.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_SByte.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_Int.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_UInt.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_Short.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_UShort.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_Long.B, dbType); - TestBigIntForEverythingWorks(LotsOfNumerics.E_ULong.B, dbType); - } - - private void TestBigIntForEverythingWorks(T expected, string dbType) - { - var query = connection.Query("select cast(1 as " + dbType + ")").Single(); - query.IsEqualTo(expected); - - var scalar = connection.ExecuteScalar("select cast(1 as " + dbType + ")"); - scalar.IsEqualTo(expected); - } - - [Fact] - public void TestSubsequentQueriesSuccess() - { - var data0 = connection.Query("select 1 as [Id] where 1 = 0").ToList(); - data0.Count.IsEqualTo(0); - - var data1 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered)).ToList(); - data1.Count.IsEqualTo(0); - - var data2 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None)).ToList(); - data2.Count.IsEqualTo(0); - - data0 = connection.Query("select 1 as [Id] where 1 = 0").ToList(); - data0.Count.IsEqualTo(0); - - data1 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered)).ToList(); - data1.Count.IsEqualTo(0); - - data2 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None)).ToList(); - data2.Count.IsEqualTo(0); - } - class Fooz0 { public int Id { get; set; } } - class Fooz1 { public int Id { get; set; } } - class Fooz2 { public int Id { get; set; } } - - - [Fact] - public void Issue149_TypeMismatch_SequentialAccess() - { - string error; - Guid guid = Guid.Parse("cf0ef7ac-b6fe-4e24-aeda-a2b45bb5654e"); - try - { - var result = connection.Query(@"select @guid as Id", new { guid }).First(); - error = null; - } - catch (Exception ex) - { - error = ex.Message; - } - error.IsEqualTo("Error parsing column 0 (Id=cf0ef7ac-b6fe-4e24-aeda-a2b45bb5654e - Object)"); - } - public class Issue149_Person { public string Id { get; set; } } - - public class HazX - { - public string X { get; set; } - } - - [Fact] - public void Issue178_SqlServer() - { - const string sql = @"select count(*) from Issue178"; - try { connection.Execute("drop table Issue178"); } - catch { } - try { connection.Execute("create table Issue178(id int not null)"); } - catch { } - // raw ADO.net - var sqlCmd = new SqlCommand(sql, connection); - using (IDataReader reader1 = sqlCmd.ExecuteReader()) - { - Assert.IsTrue(reader1.Read()); - reader1.GetInt32(0).IsEqualTo(0); - Assert.IsFalse(reader1.Read()); - Assert.IsFalse(reader1.NextResult()); - } - - // dapper - using (var reader2 = connection.ExecuteReader(sql)) - { - Assert.IsTrue(reader2.Read()); - reader2.GetInt32(0).IsEqualTo(0); - Assert.IsFalse(reader2.Read()); - Assert.IsFalse(reader2.NextResult()); - } - } - - [Fact] - public void QueryBasicWithoutQuery() - { - int? i = connection.Query("print 'not a query'").FirstOrDefault(); - i.IsNull(); - } - - [Fact] - public void QueryComplexWithoutQuery() - { - var obj = connection.Query("print 'not a query'").FirstOrDefault(); - obj.IsNull(); - } - - [Fact] - public void SO29343103_UtcDates() - { - const string sql = "select @date"; - var date = DateTime.UtcNow; - var returned = connection.Query(sql, new { date }).Single(); - var delta = returned - date; - Assert.IsTrue(delta.TotalMilliseconds >= -10 && delta.TotalMilliseconds <= 10); - } - - public void Issue261_Decimals() - { - var parameters = new DynamicParameters(); - parameters.Add("c", dbType: DbType.Decimal, direction: ParameterDirection.Output, precision: 10, scale: 5); - connection.Execute("create proc #Issue261 @c decimal(10,5) OUTPUT as begin set @c=11.884 end"); - connection.Execute("#Issue261", parameters, commandType: CommandType.StoredProcedure); - var c = parameters.Get("c"); - c.IsEqualTo(11.884M); - } - public void Issue261_Decimals_ADONET_SetViaBaseClass() - { - Issue261_Decimals_ADONET(true); - } - - [Fact] - public void Issue261_Decimals_ADONET_SetViaConcreteClass() - { - Issue261_Decimals_ADONET(false); - } - private void Issue261_Decimals_ADONET(bool setPrecisionScaleViaAbstractApi) - { - try - { - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "create proc #Issue261Direct @c decimal(10,5) OUTPUT as begin set @c=11.884 end"; - cmd.ExecuteNonQuery(); - } - } - catch { /* we don't care that it already exists */ } - - using (var cmd = connection.CreateCommand()) - { - cmd.CommandType = CommandType.StoredProcedure; - cmd.CommandText = "#Issue261Direct"; - var c = cmd.CreateParameter(); - c.ParameterName = "c"; - c.Direction = ParameterDirection.Output; - c.Value = DBNull.Value; - c.DbType = DbType.Decimal; - - if (setPrecisionScaleViaAbstractApi) - { - IDbDataParameter baseParam = c; - baseParam.Precision = 10; - baseParam.Scale = 5; - } - else - { - c.Precision = 10; - c.Scale = 5; - } - - cmd.Parameters.Add(c); - cmd.ExecuteNonQuery(); - decimal value = (decimal)c.Value; - value.IsEqualTo(11.884M); - } - } - - [Fact] - public void BasicDecimals() - { - var c = connection.Query("select @c", new { c = 11.884M }).Single(); - c.IsEqualTo(11.884M); - } - - [FactLongRunning] - public void Issue263_Timeout() - { - var watch = Stopwatch.StartNew(); - var i = connection.Query("waitfor delay '00:01:00'; select 42;", commandTimeout: 300, buffered: false).Single(); - watch.Stop(); - i.IsEqualTo(42); - var minutes = watch.ElapsedMilliseconds / 1000 / 60; - Assert.IsTrue(minutes >= 0.95 && minutes <= 1.05); - } - - [Fact] - public void SO30435185_InvalidTypeOwner() - { - try - { - string sql = @" INSERT INTO #XXX - (XXXId, AnotherId, ThirdId, Value, Comment) - VALUES - (@XXXId, @AnotherId, @ThirdId, @Value, @Comment); select @@rowcount as [Foo]"; - - var command = new - { - MyModels = new[] - { - new {XXXId = 1, AnotherId = 2, ThirdId = 3, Value = "abc", Comment = "def" } - } - }; - var parameters = command - .MyModels - .Select(model => new - { - XXXId = model.XXXId, - AnotherId = model.AnotherId, - ThirdId = model.ThirdId, - Value = model.Value, - Comment = model.Comment - }) - .ToArray(); - - var rowcount = (int)connection.Query(sql, parameters).Single().Foo; - rowcount.IsEqualTo(1); - - Assert.Fail(); - } - catch (InvalidOperationException ex) - { - ex.Message.IsEqualTo("An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context"); - } - } - - [Fact] - public void Issue327_ReadEmptyProcedureResults() - { - // Actually testing for not erroring here on the mapping having no rows to map on in Read(); - connection.Execute(@" - CREATE PROCEDURE #TestEmptyResults - AS - SELECT Top 0 1 Id, 'Bob' Name; - SELECT Top 0 'Billy Goat' Creature, 'Unicorn' SpiritAnimal, 'Rainbow' Location;"); - var query = connection.QueryMultiple("#TestEmptyResults", commandType: CommandType.StoredProcedure); - var result1 = query.Read(); - var result2 = query.Read(); - result1.Any().IsFalse(); - result2.Any().IsFalse(); - } - class Issue327_Person - { - public int Id { get; set; } - public string Name { get; set; } - } - class Issue327_Magic - { - public string Creature { get; set; } - public string SpiritAnimal { get; set; } - public string Location { get; set; } - } - - [Fact] - public void Issue295_NullableDateTime_SqlServer() - { - TestDateTime(connection); - } - - private static readonly bool IsAppVeyor = Environment.GetEnvironmentVariable("Appveyor")?.ToUpperInvariant() == "TRUE"; - -#if MYSQL - private static MySql.Data.MySqlClient.MySqlConnection GetMySqlConnection(bool open = true, - bool convertZeroDatetime = false, bool allowZeroDatetime = false) - { - string cs = IsAppVeyor - ? "Server=localhost;Database=test;Uid=root;Pwd=Password12!;" - : "Server=localhost;Database=tests;Uid=test;Pwd=pass;"; - var csb = new MySql.Data.MySqlClient.MySqlConnectionStringBuilder(cs); - csb.AllowZeroDateTime = allowZeroDatetime; - csb.ConvertZeroDateTime = convertZeroDatetime; - var conn = new MySql.Data.MySqlClient.MySqlConnection(csb.ConnectionString); - if (open) conn.Open(); - return conn; - } - - [FactMySql] - public void Issue552_SignedUnsignedBooleans() - { - - using (var conn = GetMySqlConnection(true, false, false)) - { - conn.Execute(@" -CREATE TEMPORARY TABLE IF NOT EXISTS `bar` ( - `id` INT NOT NULL, - `bool_val` BOOL NULL, - PRIMARY KEY (`id`)); - - truncate table bar; - insert bar (id, bool_val) values (1, null); - insert bar (id, bool_val) values (2, 0); - insert bar (id, bool_val) values (3, 1); - insert bar (id, bool_val) values (4, null); - insert bar (id, bool_val) values (5, 1); - insert bar (id, bool_val) values (6, 0); - insert bar (id, bool_val) values (7, null); - insert bar (id, bool_val) values (8, 1);"); - - var rows = conn.Query("select * from bar;").ToDictionary(x => x.Id); - - rows[1].Bool_Val.IsNull(); - rows[2].Bool_Val.IsEqualTo(false); - rows[3].Bool_Val.IsEqualTo(true); - rows[4].Bool_Val.IsNull(); - rows[5].Bool_Val.IsEqualTo(true); - rows[6].Bool_Val.IsEqualTo(false); - rows[7].Bool_Val.IsNull(); - rows[8].Bool_Val.IsEqualTo(true); - } - } - class MySqlHasBool { - public int Id {get;set;} - public bool? Bool_Val {get;set;} - } - [FactMySql] - public void Issue295_NullableDateTime_MySql_Default() - { - using (var conn = GetMySqlConnection(true, false, false)) { TestDateTime(connection); } - } - [FactMySql] - public void Issue295_NullableDateTime_MySql_ConvertZeroDatetime() - { - using (var conn = GetMySqlConnection(true, true, false)) { TestDateTime(connection); } - } - [FactMySql] - public void Issue295_NullableDateTime_MySql_AllowZeroDatetime() - { - using (var conn = GetMySqlConnection(true, false, true)) { TestDateTime(connection); } - } - [FactMySql] - public void Issue295_NullableDateTime_MySql_ConvertAllowZeroDatetime() - { - using (var conn = GetMySqlConnection(true, true, true)) { TestDateTime(connection); } - } - [FactMySql] - public void Issue426_SO34439033_DateTimeGainsTicks() - { - using (var conn = GetMySqlConnection(true, true, true)) - { - try { conn.Execute("drop table Issue426_Test"); } catch { } - try { conn.Execute("create table Issue426_Test (Id int not null, Time time not null)"); } catch { } - const long ticks = 553440000000; - const int Id = 426; - - var localObj = new Issue426_Test - { - Id = Id, - Time = TimeSpan.FromTicks(ticks) // from code example - }; - conn.Execute("replace into Issue426_Test values (@Id,@Time)", localObj); - - var dbObj = conn.Query("select * from Issue426_Test where Id = @id", new { id = Id }).Single(); - dbObj.Id.IsEqualTo(Id); - dbObj.Time.Value.Ticks.IsEqualTo(ticks); - - } - } - - [FactMySql] - public void SO36303462_Tinyint_Bools() - { - using (var conn = GetMySqlConnection(true, true, true)) - { - try { conn.Execute("drop table SO36303462_Test"); } catch { } - conn.Execute("create table SO36303462_Test (Id int not null, IsBold tinyint not null);"); - conn.Execute("insert SO36303462_Test (Id, IsBold) values (1,1);"); - conn.Execute("insert SO36303462_Test (Id, IsBold) values (2,0);"); - conn.Execute("insert SO36303462_Test (Id, IsBold) values (3,1);"); - - var rows = conn.Query("select * from SO36303462_Test").ToDictionary(x => x.Id); - rows.Count.IsEqualTo(3); - rows[1].IsBold.IsTrue(); - rows[2].IsBold.IsFalse(); - rows[3].IsBold.IsTrue(); - } - } - class SO36303462 - { - public int Id { get; set; } - public bool IsBold { get; set; } - } - - public class Issue426_Test - { - public long Id { get; set; } - public TimeSpan? Time { get; set; } - } - public class FactMySqlAttribute : FactAttribute - { - public override string Skip - { - get { return unavailable ?? base.Skip; } - set { base.Skip = value; } - } - private static string unavailable; - static FactMySqlAttribute() - { - try - { - using (GetMySqlConnection(true)) { } - } - catch(Exception ex) - { - unavailable = $"MySql is unavailable: {ex.Message}"; - } - } - } -#endif - private void TestDateTime(DbConnection connection) - { - DateTime? now = DateTime.UtcNow; - try { connection.Execute("DROP TABLE Persons"); } catch { } - connection.Execute(@"CREATE TABLE Persons (id int not null, dob datetime null)"); - connection.Execute(@"INSERT Persons (id, dob) values (@id, @dob)", - new { id = 7, dob = (DateTime?)null }); - connection.Execute(@"INSERT Persons (id, dob) values (@id, @dob)", - new { id = 42, dob = now }); - - var row = connection.QueryFirstOrDefault( - "SELECT id, dob, dob as dob2 FROM Persons WHERE id=@id", new { id = 7}); - row.IsNotNull(); - row.Id.IsEqualTo(7); - row.DoB.IsNull(); - row.DoB2.IsNull(); - - row = connection.QueryFirstOrDefault( - "SELECT id, dob FROM Persons WHERE id=@id", new { id = 42 }); - row.IsNotNull(); - row.Id.IsEqualTo(42); - row.DoB.Equals(now); - row.DoB2.Equals(now); - } - class Issue295Person - { - public int Id { get; set; } - public DateTime? DoB { get; set; } - public DateTime? DoB2 { get; set; } - } -#if FIREBIRD - [Fact(Skip="Bug in Firebird; a PR to fix it has been submitted")] - public void Issue178_Firebird() - { - var cs = @"initial catalog=localhost:database;user id=SYSDBA;password=masterkey"; - - using (var connection = new FbConnection(cs)) - { - connection.Open(); - const string sql = @"select count(*) from Issue178"; - try { connection.Execute("drop table Issue178"); } - catch { } - connection.Execute("create table Issue178(id int not null)"); - connection.Execute("insert into Issue178(id) values(42)"); - // raw ADO.net - using (var sqlCmd = new FbCommand(sql, connection)) - using (IDataReader reader1 = sqlCmd.ExecuteReader()) - { - Assert.IsTrue(reader1.Read()); - reader1.GetInt32(0).IsEqualTo(1); - Assert.IsFalse(reader1.Read()); - Assert.IsFalse(reader1.NextResult()); - } - - // dapper - using (var reader2 = connection.ExecuteReader(sql)) - { - Assert.IsTrue(reader2.Read()); - reader2.GetInt32(0).IsEqualTo(1); - Assert.IsFalse(reader2.Read()); - Assert.IsFalse(reader2.NextResult()); - } - - var count = connection.Query(sql).Single(); - count.IsEqualTo(1); - } - } -#endif - -#if OLEDB - [Fact] - public void PseudoPositionalParameters_Simple() - { - using (var connection = ConnectViaOledb()) - { - int value = connection.Query("select ?x? + ?y_2? + ?z?", new { x = 1, y_2 = 3, z = 5, z2 = 24 }).Single(); - value.IsEqualTo(9); - } - } - - [Fact] - public void PseudoPositionalParameters_Dynamic() - { - using (var connection = ConnectViaOledb()) - { - var args = new DynamicParameters(); - args.Add("x", 1); - args.Add("y_2", 3); - args.Add("z", 5); - args.Add("z2", 24); - int value = connection.Query("select ?x? + ?y_2? + ?z?", args).Single(); - value.IsEqualTo(9); - } - } - - [Fact] - public void PseudoPositionalParameters_ReusedParameter() - { - using (var connection = ConnectViaOledb()) - { - try - { - int value = connection.Query("select ?x? + ?y_2? + ?x?", new { x = 1, y_2 = 3 }).Single(); - Assert.Fail(); - } - catch (InvalidOperationException ex) - { - ex.Message.IsEqualTo("When passing parameters by position, each parameter can only be referenced once"); - } - } - } - - [Fact] - public void Issue569_SO38527197_PseudoPositionalParameters_In() - { - using (var connection = ConnectViaOledb()) - { - int[] ids = { 1, 2, 5, 7 }; - var list = connection.Query("select * from string_split('1,2,3,4,5',',') where value in ?ids?", new { ids }).AsList(); - list.Sort(); - string.Join(",", list).IsEqualTo("1,2,5"); - } - } - - [Fact] - public void PseudoPositional_CanUseVariable() - { - using (var connection = ConnectViaOledb()) - { - int id = 42; - var row = connection.QuerySingle("declare @id int = ?id?; select @id as [A], @id as [B];", new { id }); - int a = (int)row.A; - int b = (int)row.B; - a.IsEqualTo(42); - b.IsEqualTo(42); - } - } - [Fact] - public void PseudoPositional_CannotUseParameterMultipleTimes() - { - - using (var connection = ConnectViaOledb()) - { - try - { - int id = 42; - var row = connection.QuerySingle("select ?id? as [A], ?id? as [B];", new { id }); - Assert.Fail(); - } - catch (InvalidOperationException ex) when (ex.Message == "When passing parameters by position, each parameter can only be referenced once") - { - // that's a win - } - } - } - - [Fact] - public void PseudoPositionalParameters_ExecSingle() - { - using (var connection = ConnectViaOledb()) - { - var data = new { x = 6 }; - connection.Execute("create table #named_single(val int not null)"); - int count = connection.Execute("insert #named_single (val) values (?x?)", data); - int sum = (int)connection.ExecuteScalar("select sum(val) from #named_single"); - count.IsEqualTo(1); - sum.IsEqualTo(6); - } - } - - [Fact] - public void PseudoPositionalParameters_ExecMulti() - { - using (var connection = ConnectViaOledb()) - { - var data = new[] - { - new { x = 1, y = 1 }, - new { x = 3, y = 1 }, - new { x = 6, y = 1 }, - }; - connection.Execute("create table #named_multi(val int not null)"); - int count = connection.Execute("insert #named_multi (val) values (?x?)", data); - int sum = (int)connection.ExecuteScalar("select sum(val) from #named_multi"); - count.IsEqualTo(3); - sum.IsEqualTo(10); - } - } - - [Fact] - public void Issue457_NullParameterValues() - { - const string sql = @" -DECLARE @since DATETIME, @customerCode nvarchar(10) -SET @since = ? -- ODBC parameter -SET @customerCode = ? -- ODBC parameter - -SELECT @since as [Since], @customerCode as [Code]"; - - using (var connection = ConnectViaOledb()) - { - DateTime? since = null; // DateTime.Now.Date; - string code = null; // "abc"; - var row = connection.QuerySingle(sql, new - { - since, - customerCode = code - }); - var a = (DateTime?)row.Since; - var b = (string)row.Code; - - a.IsEqualTo(since); - b.IsEqualTo(code); - } - } - - [Fact] - public void Issue457_NullParameterValues_Named() - { - const string sql = @" -DECLARE @since DATETIME, @customerCode nvarchar(10) -SET @since = ?since? -- ODBC parameter -SET @customerCode = ?customerCode? -- ODBC parameter - -SELECT @since as [Since], @customerCode as [Code]"; - - using (var connection = ConnectViaOledb()) - { - DateTime? since = null; // DateTime.Now.Date; - string code = null; // "abc"; - var row = connection.QuerySingle(sql, new - { - since, - customerCode = code - }); - var a = (DateTime?)row.Since; - var b = (string)row.Code; - - a.IsEqualTo(since); - b.IsEqualTo(code); - } - } -#if ASYNC - [Fact] - public async void Issue457_NullParameterValues_MultiAsync() - { - const string sql = @" -DECLARE @since DATETIME, @customerCode nvarchar(10) -SET @since = ? -- ODBC parameter -SET @customerCode = ? -- ODBC parameter - -SELECT @since as [Since], @customerCode as [Code]"; - - using (var connection = ConnectViaOledb()) - { - DateTime? since = null; // DateTime.Now.Date; - string code = null; // "abc"; - using (var multi = await connection.QueryMultipleAsync(sql, new - { - since, - customerCode = code - })) - { - var row = await multi.ReadSingleAsync(); - var a = (DateTime?)row.Since; - var b = (string)row.Code; - - a.IsEqualTo(since); - b.IsEqualTo(code); - } - } - } - - [Fact] - public async void Issue457_NullParameterValues_MultiAsync_Named() - { - const string sql = @" -DECLARE @since DATETIME, @customerCode nvarchar(10) -SET @since = ?since? -- ODBC parameter -SET @customerCode = ?customerCode? -- ODBC parameter - -SELECT @since as [Since], @customerCode as [Code]"; - - using (var connection = ConnectViaOledb()) - { - DateTime? since = null; // DateTime.Now.Date; - string code = null; // "abc"; - using (var multi = await connection.QueryMultipleAsync(sql, new - { - since, - customerCode = code - })) - { - var row = await multi.ReadSingleAsync(); - var a = (DateTime?)row.Since; - var b = (string)row.Code; - - a.IsEqualTo(since); - b.IsEqualTo(code); - } - } - } -#endif -#endif - -#if !COREFX - [Fact] - public void SO29596645_TvpProperty() - { - try { connection.Execute("CREATE TYPE SO29596645_ReminderRuleType AS TABLE (id int NOT NULL)"); } - catch { } - connection.Execute(@"create proc #SO29596645_Proc (@Id int, @Rules SO29596645_ReminderRuleType READONLY) - as begin select @Id + ISNULL((select sum(id) from @Rules), 0); end"); - var obj = new SO29596645_OrganisationDTO(); - int val = connection.Query("#SO29596645_Proc", obj.Rules, commandType: CommandType.StoredProcedure).Single(); - - // 4 + 9 + 7 = 20 - val.IsEqualTo(20); - - } - class SO29596645_RuleTableValuedParameters : Dapper.SqlMapper.IDynamicParameters { - private string parameterName; - - public SO29596645_RuleTableValuedParameters(string parameterName) - { - this.parameterName = parameterName; - } - - - public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity) - { - Console.WriteLine("> AddParameters"); - SqlCommand lazy = (SqlCommand)command; - lazy.Parameters.AddWithValue("Id", 7); - DataTable table = new DataTable { - Columns = {{"Id", typeof(int)}}, - Rows = {{4}, {9}} - }; - lazy.Parameters.AddWithValue("Rules", table); - Console.WriteLine("< AddParameters"); - } - } - class SO29596645_OrganisationDTO - { - public SO29596645_RuleTableValuedParameters Rules { get; private set; } - - public SO29596645_OrganisationDTO() - { - Rules = new SO29596645_RuleTableValuedParameters("@Rules"); - } - } -#endif -#if POSTGRESQL - - class Cat - { - public int Id { get; set; } - public string Breed { get; set; } - public string Name { get; set; } - } - - Cat[] Cats = { - new Cat() { Breed = "Abyssinian", Name="KACTUS"}, - new Cat() { Breed = "Aegean cat", Name="KADAFFI"}, - new Cat() { Breed = "American Bobtail", Name="KANJI"}, - new Cat() { Breed = "Balinese", Name="MACARONI"}, - new Cat() { Breed = "Bombay", Name="MACAULAY"}, - new Cat() { Breed = "Burmese", Name="MACBETH"}, - new Cat() { Breed = "Chartreux", Name="MACGYVER"}, - new Cat() { Breed = "German Rex", Name="MACKENZIE"}, - new Cat() { Breed = "Javanese", Name="MADISON"}, - new Cat() { Breed = "Persian", Name="MAGNA"} - }; - - [FactPostgresqlAttribute] - public void TestPostgresqlArrayParameters() - { - using (var conn = OpenPostgresqlConnection()) - { - IDbTransaction transaction = conn.BeginTransaction(); - conn.Execute("create table tcat ( id serial not null, breed character varying(20) not null, name character varying (20) not null);"); - conn.Execute("insert into tcat(breed, name) values(:breed, :name) ", Cats); - - var r = conn.Query("select * from tcat where id=any(:catids)", new { catids = new[] { 1, 3, 5 } }); - r.Count().IsEqualTo(3); - r.Count(c => c.Id == 1).IsEqualTo(1); - r.Count(c => c.Id == 3).IsEqualTo(1); - r.Count(c => c.Id == 5).IsEqualTo(1); - transaction.Rollback(); - } - } - static NpgsqlConnection OpenPostgresqlConnection() - { - string cs = IsAppVeyor - ? "Server=localhost;Port=5432;User Id=postgres;Password=Password12!;Database=test" - : "Server=localhost;Port=5432;User Id=dappertest;Password=dapperpass;Database=dappertest"; // ;Encoding = UNICODE - var conn = new NpgsqlConnection(cs); - conn.Open(); - return conn; - } - public class FactPostgresqlAttribute : FactAttribute - { - public override string Skip - { - get { return unavailable ?? base.Skip; } - set { base.Skip = value; } - } - private static string unavailable; - static FactPostgresqlAttribute() - { - try - { - using (OpenPostgresqlConnection()) { } - } - catch (Exception ex) - { - unavailable = $"Postgresql is unavailable: {ex.Message}"; - } - } - } -#endif - -#if ASYNC - [Fact] - public async void SO35470588_WrongValuePidValue() - { - // nuke, rebuild, and populate the table - try { connection.Execute("drop table TPTable"); } catch { } - connection.Execute(@" -create table TPTable (Pid int not null primary key identity(1,1), Value int not null); -insert TPTable (Value) values (2), (568)"); - - // fetch the data using the query in the question, then force to a dictionary - var rows = (await connection.QueryAsync("select * from TPTable")) - .ToDictionary(x => x.Pid); - - // check the number of rows - rows.Count.IsEqualTo(2); - - // check row 1 - var row = rows[1]; - row.Pid.IsEqualTo(1); - row.Value.IsEqualTo(2); - - // check row 2 - row = rows[2]; - row.Pid.IsEqualTo(2); - row.Value.IsEqualTo(568); - } - public class TPTable - { - public int Pid { get; set; } - public int Value { get; set; } - } -#endif - -#if SQLITE - [FactSqlite] - public void Issue466_SqliteHatesOptimizations() - { - using (var connection = GetSqliteConnection()) - { - SqlMapper.ResetTypeHandlers(); - var row = connection.Query("select 42 as Id").First(); - row.Id.IsEqualTo(42); - row = connection.Query("select 42 as Id").First(); - row.Id.IsEqualTo(42); - - SqlMapper.ResetTypeHandlers(); - row = connection.QueryFirst("select 42 as Id"); - row.Id.IsEqualTo(42); - row = connection.QueryFirst("select 42 as Id"); - row.Id.IsEqualTo(42); - } - } - -#if ASYNC - [FactSqlite] - public async Task Issue466_SqliteHatesOptimizations_Async() - { - using (var connection = GetSqliteConnection()) - { - SqlMapper.ResetTypeHandlers(); - var row = (await connection.QueryAsync("select 42 as Id")).First(); - row.Id.IsEqualTo(42); - row = (await connection.QueryAsync("select 42 as Id")).First(); - row.Id.IsEqualTo(42); - - SqlMapper.ResetTypeHandlers(); - row = await connection.QueryFirstAsync("select 42 as Id"); - row.Id.IsEqualTo(42); - row = await connection.QueryFirstAsync("select 42 as Id"); - row.Id.IsEqualTo(42); - } - } -#endif - - [FactSqlite] - public void Isse467_SqliteLikesParametersWithPrefix() - { - Isse467_SqliteParameterNaming(true); - } - [FactSqlite] - public void Isse467_SqliteLikesParametersWithoutPrefix() - { // see issue 375 / 467; note: fixed from RC2 onwards - Isse467_SqliteParameterNaming(false); - } - private void Isse467_SqliteParameterNaming(bool prefix) - { - using (var connection = GetSqliteConnection()) - { - var cmd = connection.CreateCommand(); - cmd.CommandText = "select @foo"; -#if NET40 || NET45 - const DbType type = DbType.Int32; -#else - const SqliteType type = SqliteType.Integer; -#endif - cmd.Parameters.Add(prefix ? "@foo" : "foo", type).Value = 42; - var i = Convert.ToInt32(cmd.ExecuteScalar()); - i.IsEqualTo(42); - } - } - public class FactSqliteAttribute : FactAttribute - { - public override string Skip - { - get { return unavailable ?? base.Skip; } - set { base.Skip = value; } - } - private static string unavailable; - static FactSqliteAttribute() - { - try - { - using (GetSqliteConnection()) { } - } - catch (Exception ex) - { - unavailable = $"Sqlite is unavailable: {ex.Message}"; - } - } - } - protected static SqliteConnection GetSqliteConnection(bool open = true) - { - var connection = new SqliteConnection("Data Source=:memory:"); - if (open) connection.Open(); - return connection; - } -#endif - [Fact] - public void GetOnlyProperties() - { - var obj = connection.QuerySingle("select 42 as [Id], 'def' as [Name];"); - obj.Id.IsEqualTo(42); - obj.Name.IsEqualTo("def"); - } - class HazGetOnly - { - public int Id { get; } - public string Name { get; } = "abc"; - } - } -} diff --git a/Dapper.Tests/TransactionTests.cs b/Dapper.Tests/TransactionTests.cs new file mode 100644 index 00000000..52fa9a99 --- /dev/null +++ b/Dapper.Tests/TransactionTests.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Dapper.Tests +{ + public class TransactionTests : TestBase + { + [Fact] + public void TestTransactionCommit() + { + try + { + connection.Execute("create table #TransactionTest ([ID] int, [Value] varchar(32));"); + + using (var transaction = connection.BeginTransaction()) + { + connection.Execute("insert into #TransactionTest ([ID], [Value]) values (1, 'ABC');", transaction: transaction); + + transaction.Commit(); + } + + connection.Query("select count(*) from #TransactionTest;").Single().IsEqualTo(1); + } + finally + { + connection.Execute("drop table #TransactionTest;"); + } + } + + [Fact] + public void TestTransactionRollback() + { + connection.Execute("create table #TransactionTest ([ID] int, [Value] varchar(32));"); + + try + { + using (var transaction = connection.BeginTransaction()) + { + connection.Execute("insert into #TransactionTest ([ID], [Value]) values (1, 'ABC');", transaction: transaction); + + transaction.Rollback(); + } + + connection.Query("select count(*) from #TransactionTest;").Single().IsEqualTo(0); + } + finally + { + connection.Execute("drop table #TransactionTest;"); + } + } + + [Fact] + public void TestCommandWithInheritedTransaction() + { + connection.Execute("create table #TransactionTest ([ID] int, [Value] varchar(32));"); + + try + { + using (var transaction = connection.BeginTransaction()) + { + var transactedConnection = new TransactedConnection(connection, transaction); + + transactedConnection.Execute("insert into #TransactionTest ([ID], [Value]) values (1, 'ABC');"); + + transaction.Rollback(); + } + + connection.Query("select count(*) from #TransactionTest;").Single().IsEqualTo(0); + } + finally + { + connection.Execute("drop table #TransactionTest;"); + } + } + } +} diff --git a/Dapper.Tests/TypeHandlerTests.cs b/Dapper.Tests/TypeHandlerTests.cs new file mode 100644 index 00000000..75dbd6e2 --- /dev/null +++ b/Dapper.Tests/TypeHandlerTests.cs @@ -0,0 +1,662 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace Dapper.Tests +{ + public class TypeHandlerTests : TestBase + { + [Fact] + public void TestChangingDefaultStringTypeMappingToAnsiString() + { + const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType"; + var param = new { testParam = "TestString" }; + + var result01 = connection.Query(sql, param).FirstOrDefault(); + result01.IsEqualTo("nvarchar"); + + SqlMapper.PurgeQueryCache(); + + SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString + var result02 = connection.Query(sql, param).FirstOrDefault(); + result02.IsEqualTo("varchar"); + + SqlMapper.PurgeQueryCache(); + SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String + } + + [Fact] + public void TestChangingDefaultStringTypeMappingToAnsiStringFirstOrDefault() + { + const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType"; + var param = new { testParam = "TestString" }; + + var result01 = connection.QueryFirstOrDefault(sql, param); + result01.IsEqualTo("nvarchar"); + + SqlMapper.PurgeQueryCache(); + + SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString + var result02 = connection.QueryFirstOrDefault(sql, param); + result02.IsEqualTo("varchar"); + + SqlMapper.PurgeQueryCache(); + SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String + } + + [Fact] + public void Issue136_ValueTypeHandlers() + { + SqlMapper.ResetTypeHandlers(); + SqlMapper.AddTypeHandler(typeof(LocalDate), LocalDateHandler.Default); + var param = new LocalDateResult + { + NotNullable = new LocalDate { Year = 2014, Month = 7, Day = 25 }, + NullableNotNull = new LocalDate { Year = 2014, Month = 7, Day = 26 }, + NullableIsNull = null, + }; + + var result = connection.Query("SELECT @NotNullable AS NotNullable, @NullableNotNull AS NullableNotNull, @NullableIsNull AS NullableIsNull", param).Single(); + + SqlMapper.ResetTypeHandlers(); + SqlMapper.AddTypeHandler(typeof(LocalDate?), LocalDateHandler.Default); + result = connection.Query("SELECT @NotNullable AS NotNullable, @NullableNotNull AS NullableNotNull, @NullableIsNull AS NullableIsNull", param).Single(); + } + + public class LocalDateHandler : SqlMapper.TypeHandler + { + private LocalDateHandler() { } + + // Make the field type ITypeHandler to ensure it cannot be used with SqlMapper.AddTypeHandler(TypeHandler) + // by mistake. + public static readonly SqlMapper.ITypeHandler Default = new LocalDateHandler(); + + public override LocalDate Parse(object value) + { + var date = (DateTime)value; + return new LocalDate { Year = date.Year, Month = date.Month, Day = date.Day }; + } + + public override void SetValue(IDbDataParameter parameter, LocalDate value) + { + parameter.DbType = DbType.DateTime; + parameter.Value = new DateTime(value.Year, value.Month, value.Day); + } + } + + public struct LocalDate + { + public int Year { get; set; } + public int Month { get; set; } + public int Day { get; set; } + } + + public class LocalDateResult + { + public LocalDate NotNullable { get; set; } + public LocalDate? NullableNotNull { get; set; } + public LocalDate? NullableIsNull { get; set; } + } + + public class LotsOfNumerics + { + public enum E_Byte : byte { A = 0, B = 1 } + public enum E_SByte : sbyte { A = 0, B = 1 } + public enum E_Short : short { A = 0, B = 1 } + public enum E_UShort : ushort { A = 0, B = 1 } + public enum E_Int : int { A = 0, B = 1 } + public enum E_UInt : uint { A = 0, B = 1 } + public enum E_Long : long { A = 0, B = 1 } + public enum E_ULong : ulong { A = 0, B = 1 } + + public E_Byte P_Byte { get; set; } + public E_SByte P_SByte { get; set; } + public E_Short P_Short { get; set; } + public E_UShort P_UShort { get; set; } + public E_Int P_Int { get; set; } + public E_UInt P_UInt { get; set; } + public E_Long P_Long { get; set; } + public E_ULong P_ULong { get; set; } + + public bool N_Bool { get; set; } + public byte N_Byte { get; set; } + public sbyte N_SByte { get; set; } + public short N_Short { get; set; } + public ushort N_UShort { get; set; } + public int N_Int { get; set; } + public uint N_UInt { get; set; } + public long N_Long { get; set; } + public ulong N_ULong { get; set; } + + public float N_Float { get; set; } + public double N_Double { get; set; } + public decimal N_Decimal { get; set; } + + public E_Byte? N_P_Byte { get; set; } + public E_SByte? N_P_SByte { get; set; } + public E_Short? N_P_Short { get; set; } + public E_UShort? N_P_UShort { get; set; } + public E_Int? N_P_Int { get; set; } + public E_UInt? N_P_UInt { get; set; } + public E_Long? N_P_Long { get; set; } + public E_ULong? N_P_ULong { get; set; } + + public bool? N_N_Bool { get; set; } + public byte? N_N_Byte { get; set; } + public sbyte? N_N_SByte { get; set; } + public short? N_N_Short { get; set; } + public ushort? N_N_UShort { get; set; } + public int? N_N_Int { get; set; } + public uint? N_N_UInt { get; set; } + public long? N_N_Long { get; set; } + public ulong? N_N_ULong { get; set; } + + public float? N_N_Float { get; set; } + public double? N_N_Double { get; set; } + public decimal? N_N_Decimal { get; set; } + } + + [Fact] + public void TestBigIntForEverythingWorks() + { + TestBigIntForEverythingWorks_ByDataType("bigint"); + TestBigIntForEverythingWorks_ByDataType("int"); + TestBigIntForEverythingWorks_ByDataType("tinyint"); + TestBigIntForEverythingWorks_ByDataType("smallint"); + TestBigIntForEverythingWorks_ByDataType("bit"); + TestBigIntForEverythingWorks_ByDataType("float(24)"); + TestBigIntForEverythingWorks_ByDataType("float(53)"); + } + + private void TestBigIntForEverythingWorks_ByDataType(string dbType) + { + using (var reader = connection.ExecuteReader("select cast(1 as " + dbType + ")")) + { + reader.Read().IsTrue(); + reader.GetFieldType(0).Equals(typeof(T)); + reader.Read().IsFalse(); + reader.NextResult().IsFalse(); + } + + string sql = "select " + string.Join(",", typeof(LotsOfNumerics).GetProperties().Select( + x => "cast (1 as " + dbType + ") as [" + x.Name + "]")); + var row = connection.Query(sql).Single(); + + row.N_Bool.IsTrue(); + row.N_SByte.IsEqualTo((sbyte)1); + row.N_Byte.IsEqualTo((byte)1); + row.N_Int.IsEqualTo((int)1); + row.N_UInt.IsEqualTo((uint)1); + row.N_Short.IsEqualTo((short)1); + row.N_UShort.IsEqualTo((ushort)1); + row.N_Long.IsEqualTo((long)1); + row.N_ULong.IsEqualTo((ulong)1); + row.N_Float.IsEqualTo((float)1); + row.N_Double.IsEqualTo((double)1); + row.N_Decimal.IsEqualTo((decimal)1); + + row.P_Byte.IsEqualTo(LotsOfNumerics.E_Byte.B); + row.P_SByte.IsEqualTo(LotsOfNumerics.E_SByte.B); + row.P_Short.IsEqualTo(LotsOfNumerics.E_Short.B); + row.P_UShort.IsEqualTo(LotsOfNumerics.E_UShort.B); + row.P_Int.IsEqualTo(LotsOfNumerics.E_Int.B); + row.P_UInt.IsEqualTo(LotsOfNumerics.E_UInt.B); + row.P_Long.IsEqualTo(LotsOfNumerics.E_Long.B); + row.P_ULong.IsEqualTo(LotsOfNumerics.E_ULong.B); + + row.N_N_Bool.Value.IsTrue(); + row.N_N_SByte.Value.IsEqualTo((sbyte)1); + row.N_N_Byte.Value.IsEqualTo((byte)1); + row.N_N_Int.Value.IsEqualTo((int)1); + row.N_N_UInt.Value.IsEqualTo((uint)1); + row.N_N_Short.Value.IsEqualTo((short)1); + row.N_N_UShort.Value.IsEqualTo((ushort)1); + row.N_N_Long.Value.IsEqualTo((long)1); + row.N_N_ULong.Value.IsEqualTo((ulong)1); + row.N_N_Float.Value.IsEqualTo((float)1); + row.N_N_Double.Value.IsEqualTo((double)1); + row.N_N_Decimal.IsEqualTo((decimal)1); + + row.N_P_Byte.Value.IsEqualTo(LotsOfNumerics.E_Byte.B); + row.N_P_SByte.Value.IsEqualTo(LotsOfNumerics.E_SByte.B); + row.N_P_Short.Value.IsEqualTo(LotsOfNumerics.E_Short.B); + row.N_P_UShort.Value.IsEqualTo(LotsOfNumerics.E_UShort.B); + row.N_P_Int.Value.IsEqualTo(LotsOfNumerics.E_Int.B); + row.N_P_UInt.Value.IsEqualTo(LotsOfNumerics.E_UInt.B); + row.N_P_Long.Value.IsEqualTo(LotsOfNumerics.E_Long.B); + row.N_P_ULong.Value.IsEqualTo(LotsOfNumerics.E_ULong.B); + + TestBigIntForEverythingWorks(true, dbType); + TestBigIntForEverythingWorks((sbyte)1, dbType); + TestBigIntForEverythingWorks((byte)1, dbType); + TestBigIntForEverythingWorks((int)1, dbType); + TestBigIntForEverythingWorks((uint)1, dbType); + TestBigIntForEverythingWorks((short)1, dbType); + TestBigIntForEverythingWorks((ushort)1, dbType); + TestBigIntForEverythingWorks((long)1, dbType); + TestBigIntForEverythingWorks((ulong)1, dbType); + TestBigIntForEverythingWorks((float)1, dbType); + TestBigIntForEverythingWorks((double)1, dbType); + TestBigIntForEverythingWorks((decimal)1, dbType); + + TestBigIntForEverythingWorks(LotsOfNumerics.E_Byte.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_SByte.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_Int.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_UInt.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_Short.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_UShort.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_Long.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_ULong.B, dbType); + + TestBigIntForEverythingWorks(true, dbType); + TestBigIntForEverythingWorks((sbyte)1, dbType); + TestBigIntForEverythingWorks((byte)1, dbType); + TestBigIntForEverythingWorks((int)1, dbType); + TestBigIntForEverythingWorks((uint)1, dbType); + TestBigIntForEverythingWorks((short)1, dbType); + TestBigIntForEverythingWorks((ushort)1, dbType); + TestBigIntForEverythingWorks((long)1, dbType); + TestBigIntForEverythingWorks((ulong)1, dbType); + TestBigIntForEverythingWorks((float)1, dbType); + TestBigIntForEverythingWorks((double)1, dbType); + TestBigIntForEverythingWorks((decimal)1, dbType); + + TestBigIntForEverythingWorks(LotsOfNumerics.E_Byte.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_SByte.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_Int.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_UInt.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_Short.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_UShort.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_Long.B, dbType); + TestBigIntForEverythingWorks(LotsOfNumerics.E_ULong.B, dbType); + } + + private void TestBigIntForEverythingWorks(T expected, string dbType) + { + var query = connection.Query("select cast(1 as " + dbType + ")").Single(); + query.IsEqualTo(expected); + + var scalar = connection.ExecuteScalar("select cast(1 as " + dbType + ")"); + scalar.IsEqualTo(expected); + } + + [Fact] + public void TestSubsequentQueriesSuccess() + { + var data0 = connection.Query("select 1 as [Id] where 1 = 0").ToList(); + data0.Count.IsEqualTo(0); + + var data1 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered)).ToList(); + data1.Count.IsEqualTo(0); + + var data2 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None)).ToList(); + data2.Count.IsEqualTo(0); + + data0 = connection.Query("select 1 as [Id] where 1 = 0").ToList(); + data0.Count.IsEqualTo(0); + + data1 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.Buffered)).ToList(); + data1.Count.IsEqualTo(0); + + data2 = connection.Query(new CommandDefinition("select 1 as [Id] where 1 = 0", flags: CommandFlags.None)).ToList(); + data2.Count.IsEqualTo(0); + } + + private class Fooz0 + { + public int Id { get; set; } + } + + private class Fooz1 + { + public int Id { get; set; } + } + + private class Fooz2 + { + public int Id { get; set; } + } + + public class RatingValueHandler : SqlMapper.TypeHandler + { + private RatingValueHandler() + { + } + + public static readonly RatingValueHandler Default = new RatingValueHandler(); + + public override RatingValue Parse(object value) + { + if (value is Int32) + { + return new RatingValue() { Value = (Int32)value }; + } + + throw new FormatException("Invalid conversion to RatingValue"); + } + + public override void SetValue(IDbDataParameter parameter, RatingValue value) + { + // ... null, range checks etc ... + parameter.DbType = System.Data.DbType.Int32; + parameter.Value = value.Value; + } + } + + public class RatingValue + { + public Int32 Value { get; set; } + // ... some other properties etc ... + } + + public class MyResult + { + public String CategoryName { get; set; } + public RatingValue CategoryRating { get; set; } + } + + [Fact] + public void SO24740733_TestCustomValueHandler() + { + SqlMapper.AddTypeHandler(RatingValueHandler.Default); + var foo = connection.Query("SELECT 'Foo' AS CategoryName, 200 AS CategoryRating").Single(); + + foo.CategoryName.IsEqualTo("Foo"); + foo.CategoryRating.Value.IsEqualTo(200); + } + + [Fact] + public void SO24740733_TestCustomValueSingleColumn() + { + SqlMapper.AddTypeHandler(RatingValueHandler.Default); + var foo = connection.Query("SELECT 200 AS CategoryRating").Single(); + + foo.Value.IsEqualTo(200); + } + + public class StringListTypeHandler : SqlMapper.TypeHandler> + { + private StringListTypeHandler() + { + } + + public static readonly StringListTypeHandler Default = new StringListTypeHandler(); + //Just a simple List type handler implementation + public override void SetValue(IDbDataParameter parameter, List value) + { + parameter.Value = String.Join(",", value); + } + + public override List Parse(object value) + { + return ((value as String) ?? "").Split(',').ToList(); + } + } + + public class MyObjectWithStringList + { + public List Names { get; set; } + } + + [Fact] + public void Issue253_TestIEnumerableTypeHandlerParsing() + { + SqlMapper.ResetTypeHandlers(); + SqlMapper.AddTypeHandler(StringListTypeHandler.Default); + var foo = connection.Query("SELECT 'Sam,Kyro' AS Names").Single(); + foo.Names.IsSequenceEqualTo(new[] { "Sam", "Kyro" }); + } + + [Fact] + public void Issue253_TestIEnumerableTypeHandlerSetParameterValue() + { + SqlMapper.ResetTypeHandlers(); + SqlMapper.AddTypeHandler(StringListTypeHandler.Default); + + connection.Execute("CREATE TABLE #Issue253 (Names VARCHAR(50) NOT NULL);"); + try + { + const string names = "Sam,Kyro"; + List names_list = names.Split(',').ToList(); + var foo = connection.Query("INSERT INTO #Issue253 (Names) VALUES (@Names); SELECT Names FROM #Issue253;", new { Names = names_list }).Single(); + foo.IsEqualTo(names); + } + finally + { + connection.Execute("DROP TABLE #Issue253;"); + } + } + + public class RecordingTypeHandler : SqlMapper.TypeHandler + { + public override void SetValue(IDbDataParameter parameter, T value) + { + SetValueWasCalled = true; + parameter.Value = value; + } + + public override T Parse(object value) + { + ParseWasCalled = true; + return (T)value; + } + + public bool SetValueWasCalled { get; set; } + public bool ParseWasCalled { get; set; } + } + + [Fact] + public void Test_RemoveTypeMap() + { + SqlMapper.ResetTypeHandlers(); + SqlMapper.RemoveTypeMap(typeof(DateTime)); + + var dateTimeHandler = new RecordingTypeHandler(); + SqlMapper.AddTypeHandler(dateTimeHandler); + + connection.Execute("CREATE TABLE #Test_RemoveTypeMap (x datetime NOT NULL);"); + + try + { + connection.Execute(@"INSERT INTO #Test_RemoveTypeMap VALUES (@Now)", new { DateTime.Now }); + connection.Query("SELECT * FROM #Test_RemoveTypeMap"); + + dateTimeHandler.ParseWasCalled.IsTrue(); + dateTimeHandler.SetValueWasCalled.IsTrue(); + } + finally + { + connection.Execute("DROP TABLE #Test_RemoveTypeMap"); + SqlMapper.AddTypeMap(typeof(DateTime), DbType.DateTime); // or an option to reset type map? + } + } + + [Fact] + public void TestReaderWhenResultsChange() + { + try + { + connection.Execute("create table #ResultsChange (X int);create table #ResultsChange2 (Y int);insert #ResultsChange (X) values(1);insert #ResultsChange2 (Y) values(1);"); + + var obj1 = connection.Query("select * from #ResultsChange").Single(); + obj1.X.IsEqualTo(1); + obj1.Y.IsEqualTo(0); + obj1.Z.IsEqualTo(0); + + var obj2 = connection.Query("select * from #ResultsChange rc inner join #ResultsChange2 rc2 on rc2.Y=rc.X").Single(); + obj2.X.IsEqualTo(1); + obj2.Y.IsEqualTo(1); + obj2.Z.IsEqualTo(0); + + connection.Execute("alter table #ResultsChange add Z int null"); + connection.Execute("update #ResultsChange set Z = 2"); + + var obj3 = connection.Query("select * from #ResultsChange").Single(); + obj3.X.IsEqualTo(1); + obj3.Y.IsEqualTo(0); + obj3.Z.IsEqualTo(2); + + var obj4 = connection.Query("select * from #ResultsChange rc inner join #ResultsChange2 rc2 on rc2.Y=rc.X").Single(); + obj4.X.IsEqualTo(1); + obj4.Y.IsEqualTo(1); + obj4.Z.IsEqualTo(2); + } + finally + { + connection.Execute("drop table #ResultsChange;drop table #ResultsChange2;"); + } + } + + private class ResultsChangeType + { + public int X { get; set; } + public int Y { get; set; } + public int Z { get; set; } + } + + [Fact] + public void TestCustomTypeMap() + { + // default mapping + var item = connection.Query("Select 'AVal' as A, 'BVal' as B").Single(); + item.A.IsEqualTo("AVal"); + item.B.IsEqualTo("BVal"); + + // custom mapping + var map = new CustomPropertyTypeMap(typeof(TypeWithMapping), + (type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName)); + SqlMapper.SetTypeMap(typeof(TypeWithMapping), map); + + item = connection.Query("Select 'AVal' as A, 'BVal' as B").Single(); + item.A.IsEqualTo("BVal"); + item.B.IsEqualTo("AVal"); + + // reset to default + SqlMapper.SetTypeMap(typeof(TypeWithMapping), null); + item = connection.Query("Select 'AVal' as A, 'BVal' as B").Single(); + item.A.IsEqualTo("AVal"); + item.B.IsEqualTo("BVal"); + } + + private static string GetDescriptionFromAttribute(MemberInfo member) + { + if (member == null) return null; +#if COREFX + var data = member.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DescriptionAttribute)); + return (string)data?.ConstructorArguments.Single().Value; +#else + var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false); + return attrib?.Description; +#endif + } + + public class TypeWithMapping + { + [Description("B")] + public string A { get; set; } + + [Description("A")] + public string B { get; set; } + } + + public class WrongTypes + { + public int A { get; set; } + public double B { get; set; } + public long C { get; set; } + public bool D { get; set; } + } + + [Fact] + public void TestWrongTypes_WithRightTypes() + { + var item = connection.Query("select 1 as A, cast(2.0 as float) as B, cast(3 as bigint) as C, cast(1 as bit) as D").Single(); + item.A.Equals(1); + item.B.Equals(2.0); + item.C.Equals(3L); + item.D.Equals(true); + } + + [Fact] + public void TestWrongTypes_WithWrongTypes() + { + var item = connection.Query("select cast(1.0 as float) as A, 2 as B, 3 as C, cast(1 as bigint) as D").Single(); + item.A.Equals(1); + item.B.Equals(2.0); + item.C.Equals(3L); + item.D.Equals(true); + } + + [Fact] + public void SO24607639_NullableBools() + { + var obj = connection.Query( + @"declare @vals table (A bit null, B bit null, C bit null); + insert @vals (A,B,C) values (1,0,null); + select * from @vals").Single(); + obj.IsNotNull(); + obj.A.Value.IsEqualTo(true); + obj.B.Value.IsEqualTo(false); + obj.C.IsNull(); + } + + private class HazBools + { + public bool? A { get; set; } + public bool? B { get; set; } + public bool? C { get; set; } + } + + [Fact] + public void Issue130_IConvertible() + { + dynamic row = connection.Query("select 1 as [a], '2' as [b]").Single(); + int a = row.a; + string b = row.b; + a.IsEqualTo(1); + b.IsEqualTo("2"); + + row = connection.Query("select 3 as [a], '4' as [b]").Single(); + a = row.a; + b = row.b; + a.IsEqualTo(3); + b.IsEqualTo("4"); + } + + [Fact] + public void Issue149_TypeMismatch_SequentialAccess() + { + string error; + Guid guid = Guid.Parse("cf0ef7ac-b6fe-4e24-aeda-a2b45bb5654e"); + try + { + var result = connection.Query(@"select @guid as Id", new { guid }).First(); + error = null; + } + catch (Exception ex) + { + error = ex.Message; + } + error.IsEqualTo("Error parsing column 0 (Id=cf0ef7ac-b6fe-4e24-aeda-a2b45bb5654e - Object)"); + } + + public class Issue149_Person { public string Id { get; set; } } + + [Fact] + public void Issue295_NullableDateTime_SqlServer() => Common.TestDateTime(connection); + + [Fact] + public void SO29343103_UtcDates() + { + const string sql = "select @date"; + var date = DateTime.UtcNow; + var returned = connection.Query(sql, new { date }).Single(); + var delta = returned - date; + Assert.IsTrue(delta.TotalMilliseconds >= -10 && delta.TotalMilliseconds <= 10); + } + } +} diff --git a/Dapper.Tests/Tests.Xml.cs b/Dapper.Tests/XmlTests.cs similarity index 87% rename from Dapper.Tests/Tests.Xml.cs rename to Dapper.Tests/XmlTests.cs index 4007303c..38af216b 100644 --- a/Dapper.Tests/Tests.Xml.cs +++ b/Dapper.Tests/XmlTests.cs @@ -4,19 +4,19 @@ namespace Dapper.Tests { - public partial class TestSuite + public class XmlTests : TestBase { [Fact] public void CommonXmlTypesSupported() { var xml = new XmlDocument(); xml.LoadXml(""); - + var foo = new Foo { A = xml, B = XDocument.Parse(""), - C = XElement.Parse("") + C = XElement.Parse("") }; var bar = connection.QuerySingle("select @a as [A], @b as [B], @c as [C]", new { a = foo.A, b = foo.B, c = foo.C }); bar.A.DocumentElement.Name.IsEqualTo("abc"); diff --git a/Dapper.Tests/app.config b/Dapper.Tests/app.config deleted file mode 100644 index f7e38abe..00000000 --- a/Dapper.Tests/app.config +++ /dev/null @@ -1,42 +0,0 @@ - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Dapper.Tests/packages.config b/Dapper.Tests/packages.config deleted file mode 100644 index 62cc42fc..00000000 --- a/Dapper.Tests/packages.config +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Dapper.Tests/project.json b/Dapper.Tests/project.json deleted file mode 100644 index 1a8aa6bc..00000000 --- a/Dapper.Tests/project.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "packOptions": { - "summary": "Dapper.Tests", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "marc.gravell", "nick.craver" ], - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "authors": [ "Marc Gravell", "Nick Craver" ], - "description": "Dapper Core Test Suite", - "title": "Dapper.Tests", - "version": "1.0.0-*", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - "Dapper": { - "target": "project" - }, - "Dapper.Contrib": { - "target": "project" - }, - "xunit": "2.2.0-beta2-build3300", - "dotnet-test-xunit": "2.2.0-preview2-build1029", - "Belgrade.Sql.Client": "0.6.2" - }, - "buildOptions": { - "warningsAsErrors": true, - "emitEntryPoint": true, - "debugType": "portable" - }, - "testRunner": "xunit", - "frameworks": { - //"net40": { - // "buildOptions": { - // "define": [ - // "MYSQL", - // "ENTITY_FRAMEWORK", - // "LINQ2SQL", - // "FIREBIRD", - // "SQL_CE", - // "OLEDB", - // "MASSIVE", - // "ORMLITE", - // "SOMA", - // "SIMPLEDATA", - // "SQLITE" - // ] - // }, - // "frameworkAssemblies": { - // "System.Configuration": "4.0.0.0", - // "System.Data": "4.0.0.0", - // "System.Data.Linq": "4.0.0.0", - // "System.Xml": "4.0.0.0", - // "System.Xml.Linq": "4.0.0.0" - // }, - // "dependencies": { - // "Dapper.EntityFramework": { - // "target": "project" - // }, - // "EntityFramework": "6.1.3", - // "FirebirdSql.Data.FirebirdClient": "4.10.0", - // "Microsoft.SqlServer.Compact": "4.0.8876.1", - // "Microsoft.SqlServer.Types": "11.0.2", - // "MySql.Data": "6.9.8", - // "NHibernate": "4.0.4.4000", - // "ServiceStack.OrmLite": "4.0.48", - // "ServiceStack.OrmLite.SqlServer": "4.0.48", - // "Simple.Data.Ado": "1.0.0-rc3", - // "Simple.Data.Core": "1.0.0-rc3", - // "Simple.Data.SqlServer": "1.0.0-rc3", - // "Soma": "1.8.0.7", - // "System.Data.SQLite": "1.0.99" - // } - //}, - "net451": { - "buildOptions": { - "define": [ - "NET45", - "ASYNC", - "MYSQL", - "ENTITY_FRAMEWORK", - "LINQ2SQL", - "FIREBIRD", - "SQL_CE", - "POSTGRESQL", - "OLEDB", - "MASSIVE", - "ORMLITE", - "SOMA", - "SIMPLEDATA", - "SQLITE", - "XUNIT2", - "BELGRADE" - ] - }, - "frameworkAssemblies": { - "System.Configuration": "4.0.0.0", - "System.Data": "4.0.0.0", - "System.Data.Linq": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - }, - "dependencies": { - "Dapper.EntityFramework": { - "target": "project" - }, - "System.Runtime": "4.1.0", - "EntityFramework": "6.1.3", - "FirebirdSql.Data.FirebirdClient": "4.10.0", - "Microsoft.SqlServer.Compact": "4.0.8876.1", - "Microsoft.SqlServer.Types": "11.0.2", - "MySql.Data": "6.9.8", - "NHibernate": "4.0.4.4000", - "Npgsql": "3.0.5", - "ServiceStack.OrmLite": "4.0.48", - "ServiceStack.OrmLite.SqlServer": "4.0.48", - "Simple.Data.Ado": "2.0.0-alpha1", - "Simple.Data.Core": "2.0.0-alpha1", - "Simple.Data.SqlServer": "2.0.0-alpha1", - "Soma": "1.8.0.7", - "Susanoo.Core": "1.2.4", - "Susanoo.SqlServer": "1.2.4", - "System.Data.SQLite": "1.0.99" - } - }, - "netcoreapp1.0": { - "imports": [ - "portable-net451+win8", - "dnxcore50" - ], - "buildOptions": { - "define": [ "ASYNC", "COREFX", "XUNIT2", "SQLITE","BELGRADE" ] - }, - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.0", - "type": "platform" - }, - "Microsoft.Data.Sqlite": "1.0.0" - } - } - } -} \ No newline at end of file diff --git a/Dapper.sln b/Dapper.sln index af80babc..fe8fe442 100644 --- a/Dapper.sln +++ b/Dapper.sln @@ -1,35 +1,42 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A34907DF-958A-4E4C-8491-84CF303FD13E}" ProjectSection(SolutionItems) = preProject build.ps1 = build.ps1 build.sh = build.sh - global.json = global.json License.txt = License.txt - NuGet.Config = NuGet.Config Readme.md = Readme.md EndProjectSection EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper", "Dapper\Dapper.xproj", "{FAC24C3F-68F9-4247-A4B9-21D487E99275}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper", "Dapper\Dapper.csproj", "{FAC24C3F-68F9-4247-A4B9-21D487E99275}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.StrongName", "Dapper.StrongName\Dapper.StrongName.xproj", "{549C51A1-222B-4E12-96F1-3AEFF45A7709}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.StrongName", "Dapper\Dapper.StrongName.csproj", "{549C51A1-222B-4E12-96F1-3AEFF45A7709}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.Tests", "Dapper.Tests\Dapper.Tests.xproj", "{052C0817-DB26-4925-8929-8C5E42D148D5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Tests", "Dapper.Tests\Dapper.Tests.csproj", "{052C0817-DB26-4925-8929-8C5E42D148D5}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.Contrib", "Dapper.Contrib\Dapper.Contrib.xproj", "{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Contrib", "Dapper.Contrib\Dapper.Contrib.csproj", "{4E409F8F-CFBB-4332-8B0A-FD5A283051FD}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.EntityFramework", "Dapper.EntityFramework\Dapper.EntityFramework.xproj", "{BE401F7B-8611-4A1E-AEAA-5CB700128C16}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.EntityFramework", "Dapper.EntityFramework\Dapper.EntityFramework.csproj", "{BE401F7B-8611-4A1E-AEAA-5CB700128C16}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.Tests.Contrib", "Dapper.Tests.Contrib\Dapper.Tests.Contrib.xproj", "{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Tests.Contrib", "Dapper.Tests.Contrib\Dapper.Tests.Contrib.csproj", "{DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.SqlBuilder", "Dapper.SqlBuilder\Dapper.SqlBuilder.xproj", "{196928F0-7052-4585-90E8-817BD720F5E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.SqlBuilder", "Dapper.SqlBuilder\Dapper.SqlBuilder.csproj", "{196928F0-7052-4585-90E8-817BD720F5E3}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.EntityFramework.StrongName", "Dapper.EntityFramework.StrongName\Dapper.EntityFramework.StrongName.xproj", "{1A70B6D7-244E-41ED-8FF5-6F0E8E26A764}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Rainbow", "Dapper.Rainbow\Dapper.Rainbow.csproj", "{8A74F0B6-188F-45D2-8A4B-51E4F211805A}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dapper.Rainbow", "Dapper.Rainbow\Dapper.Rainbow.xproj", "{8A74F0B6-188F-45D2-8A4B-51E4F211805A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4E956F6B-6BD8-46F5-BC85-49292FF8F9AB}" + ProjectSection(SolutionItems) = preProject + Directory.build.props = Directory.build.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{568BD46C-1C65-4D44-870C-12CD72563262}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.EntityFramework.StrongName", "Dapper.EntityFramework\Dapper.EntityFramework.StrongName.csproj", "{39D3EEB6-9C05-4F4A-8C01-7B209742A7EB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Tests.Performance", "Dapper.Tests.Performance\Dapper.Tests.Performance.csproj", "{F017075A-2969-4A8E-8971-26F154EB420F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -65,16 +72,32 @@ Global {196928F0-7052-4585-90E8-817BD720F5E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {196928F0-7052-4585-90E8-817BD720F5E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {196928F0-7052-4585-90E8-817BD720F5E3}.Release|Any CPU.Build.0 = Release|Any CPU - {1A70B6D7-244E-41ED-8FF5-6F0E8E26A764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A70B6D7-244E-41ED-8FF5-6F0E8E26A764}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A70B6D7-244E-41ED-8FF5-6F0E8E26A764}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A70B6D7-244E-41ED-8FF5-6F0E8E26A764}.Release|Any CPU.Build.0 = Release|Any CPU {8A74F0B6-188F-45D2-8A4B-51E4F211805A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8A74F0B6-188F-45D2-8A4B-51E4F211805A}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A74F0B6-188F-45D2-8A4B-51E4F211805A}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A74F0B6-188F-45D2-8A4B-51E4F211805A}.Release|Any CPU.Build.0 = Release|Any CPU + {39D3EEB6-9C05-4F4A-8C01-7B209742A7EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39D3EEB6-9C05-4F4A-8C01-7B209742A7EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39D3EEB6-9C05-4F4A-8C01-7B209742A7EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39D3EEB6-9C05-4F4A-8C01-7B209742A7EB}.Release|Any CPU.Build.0 = Release|Any CPU + {F017075A-2969-4A8E-8971-26F154EB420F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F017075A-2969-4A8E-8971-26F154EB420F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F017075A-2969-4A8E-8971-26F154EB420F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F017075A-2969-4A8E-8971-26F154EB420F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FAC24C3F-68F9-4247-A4B9-21D487E99275} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {549C51A1-222B-4E12-96F1-3AEFF45A7709} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {052C0817-DB26-4925-8929-8C5E42D148D5} = {568BD46C-1C65-4D44-870C-12CD72563262} + {4E409F8F-CFBB-4332-8B0A-FD5A283051FD} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {BE401F7B-8611-4A1E-AEAA-5CB700128C16} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {DAB3C5B7-BCD1-4A5F-BB6B-50D2BB63DB4A} = {568BD46C-1C65-4D44-870C-12CD72563262} + {196928F0-7052-4585-90E8-817BD720F5E3} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {8A74F0B6-188F-45D2-8A4B-51E4F211805A} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {39D3EEB6-9C05-4F4A-8C01-7B209742A7EB} = {4E956F6B-6BD8-46F5-BC85-49292FF8F9AB} + {F017075A-2969-4A8E-8971-26F154EB420F} = {568BD46C-1C65-4D44-870C-12CD72563262} + EndGlobalSection EndGlobal diff --git a/Dapper/Dapper.StrongName.csproj b/Dapper/Dapper.StrongName.csproj new file mode 100644 index 00000000..4e930f55 --- /dev/null +++ b/Dapper/Dapper.StrongName.csproj @@ -0,0 +1,30 @@ + + + Dapper.StrongName + orm;sql;micro-orm + Dapper (Strong Named) + A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc.. + Sam Saffron;Marc Gravell;Nick Craver + net40;net45;net451;netstandard1.3 + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/Dapper/Dapper.csproj b/Dapper/Dapper.csproj new file mode 100644 index 00000000..471bd502 --- /dev/null +++ b/Dapper/Dapper.csproj @@ -0,0 +1,28 @@ + + + Dapper + orm;sql;micro-orm + Dapper + A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc.. + Sam Saffron;Marc Gravell;Nick Craver + net40;net45;net451;netstandard1.3 + + + + + + + + + + + + + + + + + + + + diff --git a/Dapper/Dapper.xproj b/Dapper/Dapper.xproj deleted file mode 100644 index ea2aaa62..00000000 --- a/Dapper/Dapper.xproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - fac24c3f-68f9-4247-a4b9-21d487e99275 - Dapper - .\obj - .\bin\ - - - 2.0 - - - \ No newline at end of file diff --git a/Dapper/Properties/AssemblyInfo.cs b/Dapper/Properties/AssemblyInfo.cs deleted file mode 100644 index ddee5e0c..00000000 --- a/Dapper/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Dapper")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Stack Exchange")] -[assembly: AssemblyProduct("Dapper")] -[assembly: AssemblyCopyright("Copyright © Sam Saffron 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: CLSCompliant(true)] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("59080aa9-fa65-438f-ad2e-772d840effa9")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.50.2.0")] -[assembly: AssemblyFileVersion("1.50.2.0")] diff --git a/Dapper/SqlMapper.Identity.cs b/Dapper/SqlMapper.Identity.cs index f151615b..ec41b150 100644 --- a/Dapper/SqlMapper.Identity.cs +++ b/Dapper/SqlMapper.Identity.cs @@ -60,62 +60,69 @@ private Identity(string sql, CommandType? commandType, string connectionString, } /// - /// + /// Whether this equals another. /// - /// - /// + /// The other to compare to. public override bool Equals(object obj) { return Equals(obj as Identity); } + /// - /// The sql + /// The raw SQL command. /// public readonly string sql; + /// - /// The command type + /// The SQL command type. /// public readonly CommandType? commandType; /// - /// + /// The hash code of this Identity. /// - public readonly int hashCode, gridIndex; + public readonly int hashCode; + + /// + /// The grid index (position in the reader) of this Identity. + /// + public readonly int gridIndex; + /// - /// + /// This of this Identity. /// public readonly Type type; + /// - /// + /// The connection string for this Identity. /// public readonly string connectionString; + /// - /// + /// The type of the parameters object for this Identity. /// public readonly Type parametersType; + /// - /// + /// Gets the hash code for this identity. /// /// - public override int GetHashCode() - { - return hashCode; - } + public override int GetHashCode() => hashCode; + /// /// Compare 2 Identity objects /// - /// - /// + /// The other object to compare. + /// Whether the two are equal public bool Equals(Identity other) { - return - other != null && - gridIndex == other.gridIndex && - type == other.type && - sql == other.sql && - commandType == other.commandType && - connectionStringComparer.Equals(connectionString, other.connectionString) && - parametersType == other.parametersType; + return other != null + && gridIndex == other.gridIndex + && type == other.type + && sql == other.sql + && commandType == other.commandType + && connectionStringComparer.Equals(connectionString, other.connectionString) + && parametersType == other.parametersType; } } } diff --git a/Dapper/project.json b/Dapper/project.json deleted file mode 100644 index 9db968c5..00000000 --- a/Dapper/project.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "packOptions": { - "summary": "A high performance Micro-ORM", - "tags": [ "orm", "sql", "micro-orm" ], - "owners": [ "marc.gravell", "nick.craver" ], - "releaseNotes": "http://stackexchange.github.io/dapper-dot-net/", - "projectUrl": "https://github.com/StackExchange/dapper-dot-net", - "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", - "repository": { - "type": "git", - "url": "https://github.com/StackExchange/dapper-dot-net" - } - }, - "version": "1.50.2-*", - "authors": [ "Sam Saffron", "Marc Gravell", "Nick Craver" ], - "description": "A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, SqlCE, Firebird etc..", - "title": "Dapper", - "copyright": "2017 Stack Exchange, Inc.", - "dependencies": { - }, - "buildOptions": { - "xmlDoc": true, - "warningsAsErrors": true - }, - "frameworks": { - "net40": { - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - } - }, - "net45": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - } - }, - "net451": { - "buildOptions": { - "define": [ "ASYNC" ] - }, - "frameworkAssemblies": { - "System.Data": "4.0.0.0", - "System.Xml": "4.0.0.0", - "System.Xml.Linq": "4.0.0.0" - } - }, - "netstandard1.3": { - "buildOptions": { - "define": [ "ASYNC", "COREFX" ] - }, - "dependencies": { - "System.Collections": "4.0.11", - "System.Collections.Concurrent": "4.0.12", - "System.Collections.NonGeneric": "4.0.1", - "System.Data.SqlClient": "4.1.0", - "System.Dynamic.Runtime": "4.0.11", - "System.Linq": "4.1.0", - "System.Reflection": "4.1.0", - "System.Reflection.Emit": "4.0.1", - "System.Reflection.Emit.Lightweight": "4.0.1", - "System.Reflection.Extensions": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.RegularExpressions": "4.1.0", - "System.Threading": "4.0.11", - "System.Xml.XDocument": "4.0.11", - "System.Xml.XmlDocument": "4.0.1" - } - } - } -} diff --git a/Directory.build.props b/Directory.build.props new file mode 100644 index 00000000..459d3530 --- /dev/null +++ b/Directory.build.props @@ -0,0 +1,36 @@ + + + 1.50.2 + + 2017 Stack Exchange, Inc. + + true + true + ../Dapper.snk + + $(AssemblyName) + https://stackexchange.github.io/Dapper/ + https://github.com/StackExchange/Dapper + http://www.apache.org/licenses/LICENSE-2.0 + git + https://github.com/StackExchange/Dapper + + true + embedded + en-US + false + + + + $(DefineConstants);ASYNC + + + $(DefineConstants);ASYNC;COREFX + + + + + + + + \ No newline at end of file diff --git a/NuGet.Config b/NuGet.Config deleted file mode 100644 index 7604d005..00000000 --- a/NuGet.Config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Readme.md b/Readme.md index 3ebece65..7a6b2839 100644 --- a/Readme.md +++ b/Readme.md @@ -223,7 +223,7 @@ The performance tests are broken in to 3 lists:
-Performance benchmarks are available [here](https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper.Tests/PerformanceTests.cs). +Performance benchmarks are available [here](https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper.Tests.Performance/PerformanceTests.cs). Feel free to submit patches that include other ORMs - when running benchmarks, be sure to compile in Release and not attach a debugger (ctrl F5). @@ -434,6 +434,6 @@ Who is using this? --------------------- Dapper is in production use at: -[Stack Overflow](http://stackoverflow.com/), [helpdesk](https://www.jitbit.com/web-helpdesk/) +[Stack Overflow](https://stackoverflow.com/), [helpdesk](https://www.jitbit.com/web-helpdesk/) (if you would like to be listed here let me know) diff --git a/build.ps1 b/build.ps1 index e3c6c57b..b25f6a68 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,76 +1,42 @@ param( [parameter(Position=0)][string] $PreReleaseSuffix = '' ) -$solutionPath = split-path $MyInvocation.MyCommand.Definition -$getDotNet = join-path $solutionPath "tools\install.ps1" -$env:DOTNET_INSTALL_DIR="$(Convert-Path "$PSScriptRoot")\.dotnet\win7-x64" -if (!(Test-Path $env:DOTNET_INSTALL_DIR)) -{ - mkdir $env:DOTNET_INSTALL_DIR | Out-Null -} - -& $getDotNet - -$env:PATH = "$env:DOTNET_INSTALL_DIR;$env:PATH" -$dotnet = "$env:DOTNET_INSTALL_DIR\dotnet" - -$autoGeneratedVersion = $false - -# Generate version number if not set -if ($env:BuildSemanticVersion -eq $null) { - $autoVersion = [math]::floor((New-TimeSpan $(Get-Date) $(Get-Date -month 1 -day 1 -year 2016 -hour 0 -minute 0 -second 0)).TotalMinutes * -1).ToString() + "-" + (Get-Date).ToString("ss") - $env:BuildSemanticVersion = "rc2-" + $autoVersion - $autoGeneratedVersion = $true - - Write-Host "Set version to $autoVersion" -} - -ls */*/project.json | foreach { echo $_.FullName} | -foreach { - $content = get-content "$_" - $content = $content.Replace("99.99.99-rc2", "1.0.0-$env:BuildSemanticVersion") - set-content "$_" $content -encoding UTF8 -} +$packageOutputFolder = "$PSScriptRoot\.nupkgs" # Restore packages and build product -& $dotnet restore -v Minimal # Restore all packages +Write-Host "Restoring..." -ForegroundColor "Green" +& dotnet restore -v Minimal # Restore all packages if ($LASTEXITCODE -ne 0) { throw "dotnet restore failed with exit code $LASTEXITCODE" } # Build all -dir "Dapper*" | where {$_.PsIsContainer} | -foreach { +Write-Host "Building..." -ForegroundColor "Green" +Get-ChildItem "Dapper*.csproj" -Recurse | +ForEach-Object { if ($PreReleaseSuffix) { - & $dotnet build "$_" --version-suffix "$PreReleaseSuffix" + & dotnet build "$_" --version-suffix "$PreReleaseSuffix" } else { - & $dotnet build "$_" + & dotnet build "$_" } } + # Run tests -dir "*.Tests*" | where {$_.PsIsContainer} | -foreach { - & $dotnet test "$_" +Write-Host "Running Tests..." -ForegroundColor "Green" +Get-ChildItem "Dapper.Test*.csproj" -Recurse | +ForEach-Object { + & dotnet test "$_" } + # Package all -dir "Dapper*" | where {$_.PsIsContainer -and $_ -NotLike "*.Tests*" } | -foreach { +Write-Host "Packaging..." -ForegroundColor "Green" +Get-ChildItem "Dapper*.csproj" -Recurse | Where-Object { $_.Name -NotLike "*.Tests*" } | +ForEach-Object { if ($PreReleaseSuffix) { - & $dotnet pack "$_" -c Release -o .\.nupkg\ --version-suffix "$PreReleaseSuffix" + & dotnet pack "$_" -c Release -o "$packageOutputFolder" --version-suffix "$PreReleaseSuffix" } else { - & $dotnet pack "$_" -c Release -o .\.nupkg\ + & dotnet pack "$_" -c Release -o "$packageOutputFolder" } -} - -ls */*/project.json | foreach { echo $_.FullName} | -foreach { - $content = get-content "$_" - $content = $content.Replace("1.0.0-$env:BuildSemanticVersion", "99.99.99-rc2") - set-content "$_" $content -encoding UTF8 -} - -if ($autoGeneratedVersion){ - $env:BuildSemanticVersion = $null } \ No newline at end of file diff --git a/global.json b/global.json deleted file mode 100644 index d25e18dd..00000000 --- a/global.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "sdk": { - "version": "1.0.0-preview2-003121" - }, - "projects": [ - "Dapper", - "Dapper.EntityFramework", - "Dapper.Contrib", - "Dapper.Rainbow", - "Dapper.SqlBuilder", - "Dapper.Tests" - ] -} diff --git a/tools/install.ps1 b/tools/install.ps1 deleted file mode 100644 index ba739d74..00000000 --- a/tools/install.ps1 +++ /dev/null @@ -1,363 +0,0 @@ -# -# Copyright (c) .NET Foundation and contributors. All rights reserved. -# Licensed under the MIT license. See LICENSE file in the project root for full license information. -# - -<# -.SYNOPSIS - Installs dotnet cli -.DESCRIPTION - Installs dotnet cli. If dotnet installation already exists in the given directory - it will update it only if the requested version differs from the one already installed. -.PARAMETER Channel - Default: preview - Channel is the way of reasoning about stability and quality of dotnet. This parameter takes one of the values: - - future - Possibly unstable, frequently changing, may contain new finished and unfinished features - - preview - Pre-release stable with known issues and feature gaps - - production - Most stable releases -.PARAMETER Version - Default: latest - Represents a build version on specific channel. Possible values: - - 4-part version in a format A.B.C.D - represents specific version of build - - latest - most latest build on specific channel - - lkg - last known good version on specific channel - Note: LKG work is in progress. Once the work is finished, this will become new default -.PARAMETER InstallDir - Default: %LocalAppData%\Microsoft\dotnet - Path to where to install dotnet. Note that binaries will be placed directly in a given directory. -.PARAMETER Architecture - Default: - this value represents currently running OS architecture - Architecture of dotnet binaries to be installed. - Possible values are: , x64 and x86 -.PARAMETER SharedRuntime - Default: false - Installs just the shared runtime bits, not the entire SDK -.PARAMETER DebugSymbols - If set the installer will include symbols in the installation. -.PARAMETER DryRun - If set it will not perform installation but instead display what command line to use to consistently install - currently requested version of dotnet cli. In example if you specify version 'latest' it will display a link - with specific version so that this command can be used deterministicly in a build script. - It also displays binaries location if you prefer to install or download it yourself. -.PARAMETER NoPath - By default this script will set environment variable PATH for the current process to the binaries folder inside installation folder. - If set it will display binaries location but not set any environment variable. -.PARAMETER Verbose - Displays diagnostics information. -.PARAMETER AzureFeed - Default: https://dotnetcli.blob.core.windows.net/dotnet - This parameter should not be usually changed by user. It allows to change URL for the Azure feed used by this installer. -#> -[cmdletbinding()] -param( - [string]$Channel="preview", - [string]$Version="Latest", - [string]$InstallDir="", - [string]$Architecture="", - [switch]$SharedRuntime, - [switch]$DebugSymbols, # TODO: Switch does not work yet. Symbols zip is not being uploaded yet. - [switch]$DryRun, - [switch]$NoPath, - [string]$AzureFeed="https://dotnetcli.blob.core.windows.net/dotnet" -) - -Set-StrictMode -Version Latest -$ErrorActionPreference="Stop" -$ProgressPreference="SilentlyContinue" - -$BinFolderRelativePath="" - -# example path with regex: shared/1.0.0-beta-12345/somepath -$VersionRegEx="/\d+\.\d+[^/]+/" -$OverrideNonVersionedFiles=$true - -function Say($str) { - Write-Host "dotnet-install: $str" -} - -function Say-Verbose($str) { - Write-Verbose "dotnet-install: $str" -} - -function Say-Invocation($Invocation) { - $command = $Invocation.MyCommand; - $args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ") - Say-Verbose "$command $args" -} - -function Get-Machine-Architecture() { - Say-Invocation $MyInvocation - - # possible values: AMD64, IA64, x86 - return $ENV:PROCESSOR_ARCHITECTURE -} - -# TODO: Architecture and CLIArchitecture should be unified -function Get-CLIArchitecture-From-Architecture([string]$Architecture) { - Say-Invocation $MyInvocation - - switch ($Architecture.ToLower()) { - { $_ -eq "" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) } - { ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" } - { $_ -eq "x86" } { return "x86" } - default { throw "Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" } - } -} - -function Get-Version-Info-From-Version-Text([string]$VersionText) { - Say-Invocation $MyInvocation - - $Data = @($VersionText.Split([char[]]@(), [StringSplitOptions]::RemoveEmptyEntries)); - - $VersionInfo = @{} - $VersionInfo.CommitHash = $Data[0].Trim() - $VersionInfo.Version = $Data[1].Trim() - return $VersionInfo -} - -function Get-Latest-Version-Info([string]$AzureFeed, [string]$AzureChannel, [string]$CLIArchitecture) { - Say-Invocation $MyInvocation - - $VersionFileUrl = $null - if ($SharedRuntime) { - $VersionFileUrl = "$AzureFeed/$AzureChannel/dnvm/latest.sharedfx.win.$CLIArchitecture.version" - } - else { - $VersionFileUrl = "$AzureFeed/$AzureChannel/dnvm/latest.win.$CLIArchitecture.version" - } - - $Response = Invoke-WebRequest -UseBasicParsing $VersionFileUrl - - switch ($Response.Headers.'Content-Type'){ - { ($_ -eq "application/octet-stream") } { $VersionText = [Text.Encoding]::UTF8.GetString($Response.Content) } - { ($_ -eq "text/plain") } { $VersionText = $Response.Content } - default { throw "``$Response.Headers.'Content-Type'`` is an unknown .version file content type." } - } - - - $VersionInfo = Get-Version-Info-From-Version-Text $VersionText - - return $VersionInfo -} - -# TODO: AzureChannel and Channel should be unified -function Get-Azure-Channel-From-Channel([string]$Channel) { - Say-Invocation $MyInvocation - - # For compatibility with build scripts accept also directly Azure channels names - switch ($Channel.ToLower()) { - { ($_ -eq "future") -or ($_ -eq "dev") } { return "dev" } - { ($_ -eq "beta") } { return "beta" } - { ($_ -eq "preview") } { return "preview" } - { $_ -eq "production" } { throw "Production channel does not exist yet" } - default { throw "``$Channel`` is an invalid channel name. Use one of the following: ``future``, ``preview``, ``production``" } - } -} - -function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$AzureChannel, [string]$CLIArchitecture, [string]$Version) { - Say-Invocation $MyInvocation - - switch ($Version.ToLower()) { - { $_ -eq "latest" } { - $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -AzureChannel $AzureChannel -CLIArchitecture $CLIArchitecture - return $LatestVersionInfo.Version - } - { $_ -eq "lkg" } { throw "``-Version LKG`` not supported yet." } - default { return $Version } - } -} - -function Get-Download-Links([string]$AzureFeed, [string]$AzureChannel, [string]$SpecificVersion, [string]$CLIArchitecture) { - Say-Invocation $MyInvocation - - $ret = @() - $files = @() - if ($SharedRuntime) { - $files += "dotnet"; - } - else { - $files += "dotnet-dev"; - } - - foreach ($file in $files) { - $PayloadURL = "$AzureFeed/$AzureChannel/Binaries/$SpecificVersion/$file-win-$CLIArchitecture.$SpecificVersion.zip" - Say-Verbose "Constructed payload URL: $PayloadURL" - $ret += $PayloadURL - } - - return $ret -} - -function Get-User-Share-Path() { - Say-Invocation $MyInvocation - - $InstallRoot = $env:DOTNET_INSTALL_DIR - if (!$InstallRoot) { - $InstallRoot = "$env:LocalAppData\Microsoft\dotnet" - } - return $InstallRoot -} - -function Resolve-Installation-Path([string]$InstallDir) { - Say-Invocation $MyInvocation - - if ($InstallDir -eq "") { - return Get-User-Share-Path - } - return $InstallDir -} - -function Get-Version-Info-From-Version-File([string]$InstallRoot, [string]$RelativePathToVersionFile) { - Say-Invocation $MyInvocation - - $VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile - Say-Verbose "Local version file: $VersionFile" - - if (Test-Path $VersionFile) { - $VersionText = cat $VersionFile - Say-Verbose "Local version file text: $VersionText" - return Get-Version-Info-From-Version-Text $VersionText - } - - Say-Verbose "Local version file not found." - - return $null -} - -function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) { - Say-Invocation $MyInvocation - - $DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion - Say-Verbose "Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath" - return Test-Path $DotnetPackagePath -PathType Container -} - -function Get-Absolute-Path([string]$RelativeOrAbsolutePath) { - # Too much spam - # Say-Invocation $MyInvocation - - return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RelativeOrAbsolutePath) -} - -function Get-Path-Prefix-With-Version($path) { - $match = [regex]::match($path, $VersionRegEx) - if ($match.Success) { - return $entry.FullName.Substring(0, $match.Index + $match.Length) - } - - return $null -} - -function Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package([System.IO.Compression.ZipArchive]$Zip, [string]$OutPath) { - Say-Invocation $MyInvocation - - $ret = @() - foreach ($entry in $Zip.Entries) { - $dir = Get-Path-Prefix-With-Version $entry.FullName - if ($dir -ne $null) { - $path = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $dir) - if (-Not (Test-Path $path -PathType Container)) { - $ret += $dir - } - } - } - - $ret = $ret | Sort-Object | Get-Unique - - $values = ($ret | foreach { "$_" }) -join ";" - Say-Verbose "Directories to unpack: $values" - - return $ret -} - -# Example zip content and extraction algorithm: -# Rule: files if extracted are always being extracted to the same relative path locally -# .\ -# a.exe # file does not exist locally, extract -# b.dll # file exists locally, override only if $OverrideFiles set -# aaa\ # same rules as for files -# ... -# abc\1.0.0\ # directory contains version and exists locally -# ... # do not extract content under versioned part -# abc\asd\ # same rules as for files -# ... -# def\ghi\1.0.1\ # directory contains version and does not exist locally -# ... # extract content -function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) { - Say-Invocation $MyInvocation - - Add-Type -Assembly System.IO.Compression.FileSystem | Out-Null - Set-Variable -Name Zip - try { - $Zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath) - - $DirectoriesToUnpack = Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package -Zip $Zip -OutPath $OutPath - - foreach ($entry in $Zip.Entries) { - $PathWithVersion = Get-Path-Prefix-With-Version $entry.FullName - if (($PathWithVersion -eq $null) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) { - $DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName) - $DestinationDir = Split-Path -Parent $DestinationPath - $OverrideFiles=$OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath)) - if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) { - New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null - [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles) - } - } - } - } - finally { - if ($Zip -ne $null) { - $Zip.Dispose() - } - } -} - -$AzureChannel = Get-Azure-Channel-From-Channel -Channel $Channel -$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture -$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -AzureChannel $AzureChannel -CLIArchitecture $CLIArchitecture -Version $Version -$DownloadLinks = Get-Download-Links -AzureFeed $AzureFeed -AzureChannel $AzureChannel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture - -if ($DryRun) { - Say "Payload URLs:" - foreach ($DownloadLink in $DownloadLinks) { - Say "- $DownloadLink" - } - Say "Repeatable invocation: .\$($MyInvocation.MyCommand) -Version $SpecificVersion -Channel $Channel -Architecture $CLIArchitecture -InstallDir $InstallDir" - exit 0 -} - -$InstallRoot = Resolve-Installation-Path $InstallDir -Say-Verbose "InstallRoot: $InstallRoot" - -$IsSdkInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage "sdk" -SpecificVersion $SpecificVersion -Say-Verbose ".NET SDK installed? $IsSdkInstalled" -if ($IsSdkInstalled) { - Say ".NET SDK version $SpecificVersion is already installed." - exit 0 -} - -New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null - -foreach ($DownloadLink in $DownloadLinks) { - $ZipPath = [System.IO.Path]::GetTempFileName() - Say "Downloading $DownloadLink" - $resp = Invoke-WebRequest -UseBasicParsing $DownloadLink -OutFile $ZipPath - - Say "Extracting zip from $DownloadLink" - Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot - - Remove-Item $ZipPath -} - -$BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath) -if (-Not $NoPath) { - Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process." - $env:path = "$BinPath;" + $env:path -} -else { - Say "Binaries of dotnet can be found in $BinPath" -} - -Say "Installation finished" -exit 0 \ No newline at end of file diff --git a/tools/install.sh b/tools/install.sh deleted file mode 100644 index 3e5514fc..00000000 --- a/tools/install.sh +++ /dev/null @@ -1,671 +0,0 @@ -# Copyright (c) .NET Foundation and contributors. All rights reserved. -# Licensed under the MIT license. See LICENSE file in the project root for full license information. -# - -# Note: This script should be compatible with the dash shell used in Ubuntu. So avoid bashisms! See https://wiki.ubuntu.com/DashAsBinSh for more info - -# Stop script on NZEC -set -e -# Stop script if unbound variable found (use ${var:-} if intentional) -set -u -# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success -# This is causing it to fail -set -o pipefail - -# Use in the the functions: eval $invocation -invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"' - -# standard output may be used as a return value in the functions -# we need a way to write text on the screen in the functions so that -# it won't interfere with the return value. -# Exposing stream 3 as a pipe to standard output of the script itself -exec 3>&1 - -# Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors. -# See if stdout is a terminal -if [ -t 1 ]; then - # see if it supports colors - ncolors=$(tput colors) - if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then - bold="$(tput bold)" - normal="$(tput sgr0)" - black="$(tput setaf 0)" - red="$(tput setaf 1)" - green="$(tput setaf 2)" - yellow="$(tput setaf 3)" - blue="$(tput setaf 4)" - magenta="$(tput setaf 5)" - cyan="$(tput setaf 6)" - white="$(tput setaf 7)" - fi -fi - -say_err() { - printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2 -} - -say() { - # using stream 3 (defined in the beginning) to not interfere with stdout of functions - # which may be used as return value - printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3 -} - -say_verbose() { - if [ "$verbose" = true ]; then - say "$1" - fi -} - -get_current_os_name() { - eval $invocation - - local uname=$(uname) - if [ "$uname" = "Darwin" ]; then - echo "osx" - return 0 - else - # Detect Distro - if [ "$(cat /etc/*-release | grep -cim1 ubuntu)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 16.04)" -eq 1 ]; then - echo "ubuntu.16.04" - return 0 - fi - - echo "ubuntu" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 centos)" -eq 1 ]; then - echo "centos" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 rhel)" -eq 1 ]; then - echo "rhel" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 debian)" -eq 1 ]; then - echo "debian" - return 0 - elif [ "$(cat /etc/*-release | grep -cim1 fedora)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 23)" -eq 1 ]; then - echo "fedora.23" - return 0 - fi - elif [ "$(cat /etc/*-release | grep -cim1 opensuse)" -eq 1 ]; then - if [ "$(cat /etc/*-release | grep -cim1 13.2)" -eq 1 ]; then - echo "opensuse.13.2" - return 0 - fi - fi - fi - - say_err "OS name could not be detected" - return 1 -} - -machine_has() { - eval $invocation - - which "$1" > /dev/null 2>&1 - return $? -} - -check_min_reqs() { - if ! machine_has "curl"; then - say_err "curl is required to download dotnet. Install curl to proceed." - return 1 - fi - - return 0 -} - -check_pre_reqs() { - eval $invocation - - local failing=false; - - if [ "${DOTNET_INSTALL_SKIP_PREREQS:-}" = "1" ]; then - return 0 - fi - - if [ "$(uname)" = "Linux" ]; then - if ! [ -x "$(command -v ldconfig)" ]; then - echo "ldconfig is not in PATH, trying /sbin/ldconfig." - LDCONFIG_COMMAND="/sbin/ldconfig" - else - LDCONFIG_COMMAND="ldconfig" - fi - - [ -z "$($LDCONFIG_COMMAND -p | grep libunwind)" ] && say_err "Unable to locate libunwind. Install libunwind to continue" && failing=true - [ -z "$($LDCONFIG_COMMAND -p | grep libssl)" ] && say_err "Unable to locate libssl. Install libssl to continue" && failing=true - [ -z "$($LDCONFIG_COMMAND -p | grep libcurl)" ] && say_err "Unable to locate libcurl. Install libcurl to continue" && failing=true - [ -z "$($LDCONFIG_COMMAND -p | grep libicu)" ] && say_err "Unable to locate libicu. Install libicu to continue" && failing=true - fi - - if [ "$failing" = true ]; then - return 1 - fi - - return 0 -} - -# args: -# input - $1 -to_lowercase() { - #eval $invocation - - echo "$1" | tr '[:upper:]' '[:lower:]' - return 0 -} - -# args: -# input - $1 -remove_trailing_slash() { - #eval $invocation - - local input=${1:-} - echo "${input%/}" - return 0 -} - -# args: -# input - $1 -remove_beginning_slash() { - #eval $invocation - - local input=${1:-} - echo "${input#/}" - return 0 -} - -# args: -# root_path - $1 -# child_path - $2 - this parameter can be empty -combine_paths() { - eval $invocation - - # TODO: Consider making it work with any number of paths. For now: - if [ ! -z "${3:-}" ]; then - say_err "combine_paths: Function takes two parameters." - return 1 - fi - - local root_path=$(remove_trailing_slash $1) - local child_path=$(remove_beginning_slash ${2:-}) - say_verbose "combine_paths: root_path=$root_path" - say_verbose "combine_paths: child_path=$child_path" - echo "$root_path/$child_path" - return 0 -} - -get_machine_architecture() { - eval $invocation - - # Currently the only one supported - echo "x64" - return 0 -} - -# args: -# architecture - $1 -get_normalized_architecture_from_architecture() { - eval $invocation - - local architecture=$(to_lowercase $1) - case $architecture in - \) - echo "$(get_normalized_architecture_from_architecture $(get_machine_architecture))" - return 0 - ;; - amd64|x64) - echo "x64" - return 0 - ;; - x86) - say_err "Architecture ``x86`` currently not supported" - return 1 - ;; - esac - - say_err "Architecture ``$architecture`` not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" - return 1 -} - -# version_info is a conceptual two line string representing commit hash and 4-part version -# format: -# Line 1: # commit_hash -# Line 2: # 4-part version - -# args: -# version_text - stdin -get_version_from_version_info() { - eval $invocation - - cat | tail -n 1 - return 0 -} - -# args: -# version_text - stdin -get_commit_hash_from_version_info() { - eval $invocation - - cat | head -n 1 - return 0 -} - -# args: -# install_root - $1 -# relative_path_to_package - $2 -# specific_version - $3 -is_dotnet_package_installed() { - eval $invocation - - local install_root=$1 - local relative_path_to_package=$2 - local specific_version=${3//[$'\t\r\n']} - - local dotnet_package_path=$(combine_paths $(combine_paths $install_root $relative_path_to_package) $specific_version) - say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path" - - if [ -d "$dotnet_package_path" ]; then - return 0 - else - return 1 - fi -} - -# args: -# azure_feed - $1 -# azure_channel - $2 -# normalized_architecture - $3 -get_latest_version_info() { - eval $invocation - - local azure_feed=$1 - local azure_channel=$2 - local normalized_architecture=$3 - - local osname=$(get_current_os_name) - - local version_file_url=null - if [ "$shared_runtime" = true ]; then - version_file_url="$azure_feed/$azure_channel/dnvm/latest.sharedfx.$osname.$normalized_architecture.version" - else - version_file_url="$azure_feed/$azure_channel/dnvm/latest.$osname.$normalized_architecture.version" - fi - say_verbose "get_latest_version_info: latest url: $version_file_url" - - download $version_file_url - return $? -} - -# args: -# channel - $1 -get_azure_channel_from_channel() { - eval $invocation - - local channel=$(to_lowercase $1) - case $channel in - future|dev) - echo "dev" - return 0 - ;; - beta) - echo "beta" - return 0 - ;; - preview) - echo "preview" - return 0 - ;; - production) - say_err "Production channel does not exist yet" - return 1 - esac - - say_err "``$1`` is an invalid channel name. Use one of the following: ``future``, ``preview``, ``production``" - return 1 -} - -# args: -# azure_feed - $1 -# azure_channel - $2 -# normalized_architecture - $3 -# version - $4 -get_specific_version_from_version() { - eval $invocation - - local azure_feed=$1 - local azure_channel=$2 - local normalized_architecture=$3 - local version=$(to_lowercase $4) - - case $version in - latest) - local version_info="$(get_latest_version_info $azure_feed $azure_channel $normalized_architecture)" - say_verbose "get_specific_version_from_version: version_info=$version_info" - echo "$version_info" | get_version_from_version_info - return 0 - ;; - lkg) - say_err "``--version LKG`` not supported yet." - return 1 - ;; - *) - echo $version - return 0 - ;; - esac -} - -# args: -# azure_feed - $1 -# azure_channel - $2 -# normalized_architecture - $3 -# specific_version - $4 -construct_download_link() { - eval $invocation - - local azure_feed=$1 - local azure_channel=$2 - local normalized_architecture=$3 - local specific_version=${4//[$'\t\r\n']} - - local osname=$(get_current_os_name) - - local download_link=null - if [ "$shared_runtime" = true ]; then - download_link="$azure_feed/$azure_channel/Binaries/$specific_version/dotnet-$osname-$normalized_architecture.$specific_version.tar.gz" - else - download_link="$azure_feed/$azure_channel/Binaries/$specific_version/dotnet-dev-$osname-$normalized_architecture.$specific_version.tar.gz" - fi - - echo "$download_link" - return 0 -} - -get_user_share_path() { - eval $invocation - - if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then - echo $DOTNET_INSTALL_DIR - else - echo "$HOME/.dotnet" - fi - return 0 -} - -# args: -# install_dir - $1 -resolve_installation_path() { - eval $invocation - - local install_dir=$1 - if [ "$install_dir" = "" ]; then - local user_share_path=$(get_user_share_path) - say_verbose "resolve_installation_path: share_path=$user_share_path" - echo "$user_share_path" - return 0 - fi - - echo "$install_dir" - return 0 -} - -# args: -# install_root - $1 -get_installed_version_info() { - eval $invocation - - local install_root=$1 - local version_file=$(combine_paths "$install_root" "$local_version_file_relative_path") - say_verbose "Local version file: $version_file" - if [ ! -z "$version_file" ] | [ -r "$version_file" ]; then - local version_info="$(cat $version_file)" - echo "$version_info" - return 0 - fi - - say_verbose "Local version file not found." - return 0 -} - -# args: -# relative_or_absolute_path - $1 -get_absolute_path() { - eval $invocation - - local relative_or_absolute_path=$1 - echo $(cd $(dirname "$1") && pwd -P)/$(basename "$1") - return 0 -} - -# args: -# input_files - stdin -# root_path - $1 -# out_path - $2 -# override - $3 -copy_files_or_dirs_from_list() { - eval $invocation - - local root_path=$(remove_trailing_slash $1) - local out_path=$(remove_trailing_slash $2) - local override=$3 - local override_switch=$(if [ "$override" = false ]; then printf -- "-n"; fi) - - cat | uniq | while read -r file_path; do - local path=$(remove_beginning_slash ${file_path#$root_path}) - local target=$out_path/$path - if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then - mkdir -p $out_path/$(dirname $path) - cp -R $override_switch $root_path/$path $target - fi - done -} - -# args: -# zip_path - $1 -# out_path - $2 -extract_dotnet_package() { - eval $invocation - - local zip_path=$1 - local out_path=$2 - - local temp_out_path=$(mktemp -d $temporary_file_template) - - local failed=false - tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true - - local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/' - find $temp_out_path -type f | grep -Eo $folders_with_version_regex | copy_files_or_dirs_from_list $temp_out_path $out_path false - find $temp_out_path -type f | grep -Ev $folders_with_version_regex | copy_files_or_dirs_from_list $temp_out_path $out_path true - - rm -rf $temp_out_path - - if [ "$failed" = true ]; then - say_err "Extraction failed" - return 1 - fi -} - -# args: -# remote_path - $1 -# [out_path] - $2 - stdout if not provided -download() { - eval $invocation - - local remote_path=$1 - local out_path=${2:-} - - local failed=false - if [ -z "$out_path" ]; then - curl --fail -s $remote_path || failed=true - else - curl --fail -s -o $out_path $remote_path || failed=true - fi - - if [ "$failed" = true ]; then - say_err "Download failed" - return 1 - fi -} - -calculate_vars() { - eval $invocation - - azure_channel=$(get_azure_channel_from_channel "$channel") - say_verbose "azure_channel=$azure_channel" - - normalized_architecture=$(get_normalized_architecture_from_architecture "$architecture") - say_verbose "normalized_architecture=$normalized_architecture" - - specific_version=$(get_specific_version_from_version $azure_feed $azure_channel $normalized_architecture $version) - say_verbose "specific_version=$specific_version" - if [ -z "$specific_version" ]; then - say_err "Could not get version information." - return 1 - fi - - download_link=$(construct_download_link $azure_feed $azure_channel $normalized_architecture $specific_version) - say_verbose "download_link=$download_link" - - install_root=$(resolve_installation_path $install_dir) - say_verbose "install_root=$install_root" -} - -install_dotnet() { - eval $invocation - - if is_dotnet_package_installed $install_root "sdk" $specific_version; then - say ".NET SDK version $specific_version is already installed." - return 0 - fi - - mkdir -p $install_root - zip_path=$(mktemp $temporary_file_template) - say_verbose "Zip path: $zip_path" - - say "Downloading $download_link" - download "$download_link" $zip_path - say_verbose "Downloaded file exists and readable? $(if [ -r $zip_path ]; then echo "yes"; else echo "no"; fi)" - - say "Extracting zip" - extract_dotnet_package $zip_path $install_root - - return 0 -} - -local_version_file_relative_path="/.version" -bin_folder_relative_path="" -temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX" - -channel="preview" -version="Latest" -install_dir="" -architecture="" -debug_symbols=false -dry_run=false -no_path=false -azure_feed="https://dotnetcli.blob.core.windows.net/dotnet" -verbose=false -shared_runtime=false - -while [ $# -ne 0 ] -do - name=$1 - case $name in - -c|--channel|-[Cc]hannel) - shift - channel=$1 - ;; - -v|--version|-[Vv]ersion) - shift - version="$1" - ;; - -i|--install-dir|-[Ii]nstall[Dd]ir) - shift - install_dir="$1" - ;; - --arch|--architecture|-[Aa]rch|-[Aa]rchitecture) - shift - architecture="$1" - ;; - --shared-runtime|-[Ss]hared[Rr]untime) - shared_runtime=true - ;; - --debug-symbols|-[Dd]ebug[Ss]ymbols) - debug_symbols=true - ;; - --dry-run|-[Dd]ry[Rr]un) - dry_run=true - ;; - --no-path|-[Nn]o[Pp]ath) - no_path=true - ;; - --verbose|-[Vv]erbose) - verbose=true - ;; - --azure-feed|-[Aa]zure[Ff]eed) - shift - azure_feed="$1" - ;; - -?|--?|-h|--help|-[Hh]elp) - script_name="$(basename $0)" - echo ".NET Tools Installer" - echo "Usage: $script_name [-c|--channel ] [-v|--version ] [-p|--prefix ]" - echo " $script_name -h|-?|--help" - echo "" - echo "$script_name is a simple command line interface for obtaining dotnet cli." - echo "" - echo "Options:" - echo " -c,--channel Download from the CHANNEL specified (default: $channel)." - echo " -Channel" - echo " -v,--version Use specific version, ``latest`` or ``lkg``. Defaults to ``latest``." - echo " -Version" - echo " -i,--install-dir Install under specified location (see Install Location below)" - echo " -InstallDir" - echo " --architecture Architecture of .NET Tools. Currently only x64 is supported." - echo " --arch,-Architecture,-Arch" - echo " --shared-runtime Installs just the shared runtime bits, not the entire SDK." - echo " -SharedRuntime" - echo " --debug-symbols,-DebugSymbols Specifies if symbols should be included in the installation." - echo " --dry-run,-DryRun Do not perform installation. Display download link." - echo " --no-path, -NoPath Do not set PATH for the current process." - echo " --verbose,-Verbose Display diagnostics information." - echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed" - echo " -?,--?,-h,--help,-Help Shows this help message" - echo "" - echo "Install Location:" - echo " Location is chosen in following order:" - echo " - --install-dir option" - echo " - Environmental variable DOTNET_INSTALL_DIR" - echo " - /usr/local/share/dotnet" - exit 0 - ;; - *) - say_err "Unknown argument \`$name\`" - exit 1 - ;; - esac - - shift -done - -check_min_reqs -calculate_vars -if [ "$dry_run" = true ]; then - say "Payload URL: $download_link" - say "Repeatable invocation: ./$(basename $0) --version $specific_version --channel $channel --install-dir $install_dir" - exit 0 -fi - -check_pre_reqs -install_dotnet - -bin_path=$(get_absolute_path $(combine_paths $install_root $bin_folder_relative_path)) -if [ "$no_path" = false ]; then - say "Adding to current process PATH: ``$bin_path``. Note: This change will be visible only when sourcing script." - export PATH=$bin_path:$PATH -else - say "Binaries of dotnet can be found in $bin_path" -fi - -say "Installation finished successfully." \ No newline at end of file