diff --git a/Dapper.GraphQL.Test/Dapper.GraphQL.Test.csproj b/Dapper.GraphQL.Test/Dapper.GraphQL.Test.csproj index 7b53f77..ff217de 100644 --- a/Dapper.GraphQL.Test/Dapper.GraphQL.Test.csproj +++ b/Dapper.GraphQL.Test/Dapper.GraphQL.Test.csproj @@ -4,6 +4,12 @@ netcoreapp1.1 false + + 0.2.0.0 + + 0.2.0.0 + + 0.2.0 @@ -21,10 +27,10 @@ - - - + + + diff --git a/Dapper.GraphQL.Test/EntityMappers/PersonEntityMapper.cs b/Dapper.GraphQL.Test/EntityMappers/PersonEntityMapper.cs index 4fb736f..de4b4af 100644 --- a/Dapper.GraphQL.Test/EntityMappers/PersonEntityMapper.cs +++ b/Dapper.GraphQL.Test/EntityMappers/PersonEntityMapper.cs @@ -51,7 +51,7 @@ public override Person Map( // NOTE: order matters here, if both supervisor // and careerCounselor exist, then supervisor must appear - // first in the list. + // first in the list. This order is guaranteed in PersonQueryBuilder. if (fields.ContainsKey("supervisor")) { supervisorIndex = splitOn.IndexOf(typeof(Person), startingIndex); diff --git a/Dapper.GraphQL.Test/GraphQLTests.cs b/Dapper.GraphQL.Test/GraphQLTests.cs index 24437f8..be380ee 100644 --- a/Dapper.GraphQL.Test/GraphQLTests.cs +++ b/Dapper.GraphQL.Test/GraphQLTests.cs @@ -2,8 +2,6 @@ using Dapper.GraphQL.Test.Models; using Dapper.GraphQL.Test.QueryBuilders; using DbUp; -using DbUp.SQLite.Helpers; -using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using System; @@ -25,7 +23,7 @@ public GraphQLTests( this.fixture = fixture; } - [Fact(DisplayName = "Full people query should succeed", Skip = "SQLite issues with dynamic typecasting cause this to incorrectly fail.")] + [Fact(DisplayName = "Full people query should succeed")] public async Task FullPeopleQuery() { var json = await fixture.QueryGraphQLAsync(@" @@ -57,7 +55,86 @@ public async Task FullPeopleQuery() }"); var expectedJson = @" -{ +{ + ""data"": { + ""people"": [ + { + ""id"": 1, + ""firstName"": ""Hyrum"", + ""lastName"": ""Clyde"", + ""emails"": [ + { + ""id"": 1, + ""address"": ""hclyde@landmarkhw.com"" + } + ], + ""phones"": [], + ""supervisor"": null, + ""careerCounselor"": null + }, + { + ""id"": 2, + ""firstName"": ""Doug"", + ""lastName"": ""Day"", + ""emails"": [ + { + ""id"": 2, + ""address"": ""dday@landmarkhw.com"" + }, + { + ""id"": 3, + ""address"": ""dougrday@gmail.com"" + } + ], + ""phones"": [ + { + ""id"": 1, + ""number"": ""8011234567"", + ""type"": 3 + } + ], + ""supervisor"": null, + ""careerCounselor"": { + ""id"": 1, + ""firstName"": ""Hyrum"", + ""lastName"": ""Clyde"" + } + }, + { + ""id"": 3, + ""firstName"": ""Kevin"", + ""lastName"": ""Russon"", + ""emails"": [ + { + ""id"": 4, + ""address"": ""krusson@landmarkhw.com"" + } + ], + ""phones"": [ + { + ""id"": 2, + ""number"": ""8019876543"", + ""type"": 3 + }, + { + ""id"": 3, + ""number"": ""8011111111"", + ""type"": 1 + } + ], + ""supervisor"": { + ""id"": 1, + ""firstName"": ""Hyrum"", + ""lastName"": ""Clyde"" + }, + ""careerCounselor"": { + ""id"": 2, + ""firstName"": ""Doug"", + ""lastName"": ""Day"" + } + } + ] + } }"; Assert.True(fixture.JsonEquals(expectedJson, json)); diff --git a/Dapper.GraphQL.Test/InsertTests.cs b/Dapper.GraphQL.Test/InsertTests.cs index 312b6bf..62548ef 100644 --- a/Dapper.GraphQL.Test/InsertTests.cs +++ b/Dapper.GraphQL.Test/InsertTests.cs @@ -2,8 +2,6 @@ using Dapper.GraphQL.Test.Models; using Dapper.GraphQL.Test.QueryBuilders; using DbUp; -using DbUp.SQLite.Helpers; -using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using System; @@ -31,13 +29,17 @@ public void InsertPerson() var person = new Person { FirstName = "Steven", - LastName = "Rollman", + LastName = "Rollman", }; // Ensure inserting a person works and we get the person's Id back - var personId = SqlBuilder - .Insert(person) - .ExecuteWithSqliteIdentity(fixture.DbConnection); + int personId; + using (var db = fixture.GetDbConnection()) + { + personId = SqlBuilder + .Insert(person) + .ExecuteWithSqlIdentity(db); + } Assert.True(personId > 0); @@ -55,43 +57,60 @@ public void InsertPerson() }; // Add email and phone number to the person - var insertedCount = SqlBuilder - .Insert(email) - .Insert(phone) - .Execute(fixture.DbConnection); + int insertedCount; + using (var db = fixture.GetDbConnection()) + { + insertedCount = SqlBuilder + .Insert(email) + .Insert(phone) + .Execute(fixture.GetDbConnection()); - // Ensure both were inserted properly - Assert.Equal(2, insertedCount); + // Ensure both were inserted properly + Assert.Equal(2, insertedCount); - // Build an identity mapper for person - var personMapper = fixture.BuildMapper(p => p.Id); + // Build an identity mapper for person + var personMapper = fixture.BuildMapper(p => p.Id); - // Query the person from the database - var query = SqlBuilder - .From("person") - .LeftJoin("Email email on person.Id = email.PersonId") - .LeftJoin("Phone phone on person.Id = phone.PersonId") - .Select("person.*, email.*, phone.*") - .SplitOn("Id") - .SplitOn("Id") - .SplitOn("Id") - .Where("person.Id = @id", new { id = personId }); + // Query the person from the database + var query = SqlBuilder + .From("person") + .LeftJoin("Email email on person.Id = email.PersonId") + .LeftJoin("Phone phone on person.Id = phone.PersonId") + .Select("person.*, email.*, phone.*") + .SplitOn("Id") + .SplitOn("Id") + .SplitOn("Id") + .Where("person.Id = @id", new { id = personId }); - person = query - .Execute(fixture.DbConnection, personMapper) - .FirstOrDefault(); + person = query + .Execute(fixture.GetDbConnection(), personMapper) + .FirstOrDefault(); + } - // Ensure all inserted data is present - Assert.NotNull(person); - Assert.Equal(personId, person.Id); - Assert.Equal("Steven", person.FirstName); - Assert.Equal("Rollman", person.LastName); - Assert.Equal(1, person.Emails.Count); - Assert.Equal("srollman@landmarkhw.com", person.Emails[0].Address); - Assert.Equal(personId, person.Emails[0].PersonId); - Assert.Equal(1, person.Phones.Count); - Assert.Equal("8011115555", person.Phones[0].Number); - Assert.Equal(personId, person.Phones[0].PersonId); + try + { + // Ensure all inserted data is present + Assert.NotNull(person); + Assert.Equal(personId, person.Id); + Assert.Equal("Steven", person.FirstName); + Assert.Equal("Rollman", person.LastName); + Assert.Equal(1, person.Emails.Count); + Assert.Equal("srollman@landmarkhw.com", person.Emails[0].Address); + Assert.Equal(personId, person.Emails[0].PersonId); + Assert.Equal(1, person.Phones.Count); + Assert.Equal("8011115555", person.Phones[0].Number); + Assert.Equal(personId, person.Phones[0].PersonId); + } + finally + { + // Delete the items we inserted so it doesn't skew other tests + using (var db = fixture.GetDbConnection()) + { + SqlBuilder.Delete(new { Id = person.Emails[0].Id }).Execute(db); + SqlBuilder.Delete(new { Id = person.Phones[0].Id }).Execute(db); + SqlBuilder.Delete(new { Id = person.Id }).Execute(db); + } + } } } } \ No newline at end of file diff --git a/Dapper.GraphQL.Test/QueryTests.cs b/Dapper.GraphQL.Test/QueryTests.cs index b2643fc..8d856be 100644 --- a/Dapper.GraphQL.Test/QueryTests.cs +++ b/Dapper.GraphQL.Test/QueryTests.cs @@ -1,19 +1,17 @@ using Dapper.GraphQL.Test.Models; -using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; +using System.Data.SqlClient; using Xunit; namespace Dapper.GraphQL.Test { public class QueryTests : IClassFixture { - private readonly TestFixture fixture; + private readonly TestFixture fixture; public QueryTests(TestFixture fixture) { - this.fixture = fixture; + this.fixture = fixture; } [Fact(DisplayName = "ORDER BY should work")] @@ -21,7 +19,7 @@ public void OrderByShouldWork() { var query = SqlBuilder .From("Person person") - .Select("person.Id", "notAnAlias.Id") + .Select("person.Id") .SplitOn("Id") .OrderBy("LastName"); @@ -31,7 +29,7 @@ public void OrderByShouldWork() [Fact(DisplayName = "SELECT without matching alias should throw")] public void SelectWithoutMatchingAliasShouldThrow() { - Assert.ThrowsAsync(async () => + Assert.Throws(() => { var query = SqlBuilder .From("Person person") @@ -44,7 +42,10 @@ public void SelectWithoutMatchingAliasShouldThrow() .GetRequiredService() .Build(person => person.Id); - query.Execute(fixture.DbConnection, personMapper); + using (var db = fixture.GetDbConnection()) + { + query.Execute(db, personMapper); + } }); } } diff --git a/Dapper.GraphQL.Test/Sql/1-Create.sql b/Dapper.GraphQL.Test/Sql/1-Create.sql index 74c2c78..184544b 100644 --- a/Dapper.GraphQL.Test/Sql/1-Create.sql +++ b/Dapper.GraphQL.Test/Sql/1-Create.sql @@ -1,7 +1,7 @@ CREATE TABLE Person ( - Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - FirstName TEXT, - LastName TEXT, + Id INTEGER NOT NULL PRIMARY KEY IDENTITY(1, 1), + FirstName NVARCHAR(50), + LastName NVARCHAR(50), -- Known issue with FK reference to non-null numeric types -- https://github.com/StackExchange/Dapper/issues/917 SupervisorId INTEGER, @@ -11,15 +11,15 @@ ); CREATE TABLE Email ( - Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - [Address] TEXT, + Id INTEGER NOT NULL PRIMARY KEY IDENTITY(1, 1), + [Address] NVARCHAR(250), PersonId INTEGER, FOREIGN KEY(PersonId) REFERENCES Person(Id) ); CREATE TABLE Phone ( - Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - Number TEXT, + Id INTEGER NOT NULL PRIMARY KEY IDENTITY(1, 1), + Number NVARCHAR(16), [Type] INTEGER, PersonId INTEGER, FOREIGN KEY(PersonId) REFERENCES Person(Id) diff --git a/Dapper.GraphQL.Test/Sql/2-Data.sql b/Dapper.GraphQL.Test/Sql/2-Data.sql index 4df3958..f837693 100644 --- a/Dapper.GraphQL.Test/Sql/2-Data.sql +++ b/Dapper.GraphQL.Test/Sql/2-Data.sql @@ -1,17 +1,23 @@ -INSERT INTO Person (Id, FirstName, LastName, SupervisorId, CareerCounselorId) VALUES (1, 'Hyrum', 'Clyde', NULL, NULL); +SET IDENTITY_INSERT Person ON +INSERT INTO Person (Id, FirstName, LastName, SupervisorId, CareerCounselorId) VALUES (1, 'Hyrum', 'Clyde', NULL, NULL); INSERT INTO Person (Id, FirstName, LastName, SupervisorId, CareerCounselorId) VALUES (2, 'Doug', 'Day', NULL, 1); INSERT INTO Person (Id, FirstName, LastName, SupervisorId, CareerCounselorId) VALUES (3, 'Kevin', 'Russon', 1, 2); +SET IDENTITY_INSERT Person OFF -- Add the relationship once all records are inserted --UPDATE Person --SET CareerCounselorId = 2 --WHERE Id = 1; +SET IDENTITY_INSERT Email ON INSERT INTO Email (Id, [Address], PersonId) VALUES (1, 'hclyde@landmarkhw.com', 1); INSERT INTO Email (Id, [Address], PersonId) VALUES (2, 'dday@landmarkhw.com', 2); INSERT INTO Email (Id, [Address], PersonId) VALUES (3, 'dougrday@gmail.com', 2); INSERT INTO Email (Id, [Address], PersonId) VALUES (4, 'krusson@landmarkhw.com', 3); +SET IDENTITY_INSERT Email OFF +SET IDENTITY_INSERT Phone ON INSERT INTO Phone (Id, Number, [Type], PersonId) VALUES (1, '8011234567', 3, 2); INSERT INTO Phone (Id, Number, [Type], PersonId) VALUES (2, '8019876543', 3, 3); INSERT INTO Phone (Id, Number, [Type], PersonId) VALUES (3, '8011111111', 1, 3); +SET IDENTITY_INSERT Phone OFF \ No newline at end of file diff --git a/Dapper.GraphQL.Test/TestFixture.cs b/Dapper.GraphQL.Test/TestFixture.cs index ac2e18a..4174259 100644 --- a/Dapper.GraphQL.Test/TestFixture.cs +++ b/Dapper.GraphQL.Test/TestFixture.cs @@ -3,25 +3,51 @@ using Dapper.GraphQL.Test.Models; using Dapper.GraphQL.Test.QueryBuilders; using DbUp; -using DbUp.SQLite.Helpers; using GraphQL; using GraphQL.Http; -using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using System; -using System.Collections.Generic; using System.Data; +using System.Data.SqlClient; using System.Reflection; using System.Threading.Tasks; namespace Dapper.GraphQL.Test { - public class TestFixture : IDisposable + public class TestFixture { - private readonly DocumentExecuter documentExecuter; + #region Statics + + private static string ConnectionString { get; set; } + + static TestFixture() + { + EnsureSqlExpressDatabase(); + } + + private static void EnsureSqlExpressDatabase() + { + ConnectionString = "Server=(localdb)\\mssqllocaldb;Integrated Security=true;MultipleActiveResultSets=true;Database=DapperGraphQLTest"; + + // Drop the database if it already exists + DropDatabase.For.SqlDatabase(ConnectionString); + + // Ensure the database exists + EnsureDatabase.For.SqlDatabase(ConnectionString); + + var upgrader = DeployChanges.To + .SqlDatabase(ConnectionString) + .WithScriptsEmbeddedInAssembly(typeof(Person).GetTypeInfo().Assembly) + .LogToConsole() + .Build(); - public SharedConnection DbConnection { get; set; } + var upgradeResult = upgrader.PerformUpgrade(); + } + + #endregion Statics + + private readonly DocumentExecuter documentExecuter; public PersonSchema Schema { get; set; } public IServiceProvider ServiceProvider { get; set; } @@ -31,7 +57,7 @@ public TestFixture() var serviceCollection = new ServiceCollection(); SetupDapperGraphQL(serviceCollection); - SetupInMemorySqliteDatabase(serviceCollection); + SetupDatabaseConnection(serviceCollection); this.ServiceProvider = serviceCollection.BuildServiceProvider(); this.Schema = ServiceProvider.GetRequiredService(); @@ -43,14 +69,9 @@ public Func BuildMapper(Func().Build(mapper); } - public void Dispose() + public IDbConnection GetDbConnection() { - if (DbConnection != null) - { - DbConnection.Close(); - DbConnection.Dispose(); - DbConnection = null; - } + return new SqlConnection(ConnectionString); } public bool JsonEquals(string expectedJson, string actualJson) @@ -97,20 +118,9 @@ private void SetupDapperGraphQL(IServiceCollection serviceCollection) }); } - private void SetupInMemorySqliteDatabase(IServiceCollection serviceCollection) + private void SetupDatabaseConnection(ServiceCollection serviceCollection) { - var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" }; - DbConnection = new SharedConnection(new SqliteConnection(connectionStringBuilder.ToString())); - - var upgrader = DeployChanges.To - .SQLiteDatabase(DbConnection) - .WithScriptsEmbeddedInAssembly(typeof(Person).GetTypeInfo().Assembly) - .LogToConsole() - .Build(); - - var upgradeResult = upgrader.PerformUpgrade(); - - serviceCollection.AddSingleton(DbConnection); + serviceCollection.AddTransient(serviceProvider => GetDbConnection()); } } } \ No newline at end of file diff --git a/Dapper.GraphQL.Test/UpdateTests.cs b/Dapper.GraphQL.Test/UpdateTests.cs index 30cd317..0a49252 100644 --- a/Dapper.GraphQL.Test/UpdateTests.cs +++ b/Dapper.GraphQL.Test/UpdateTests.cs @@ -2,8 +2,6 @@ using Dapper.GraphQL.Test.Models; using Dapper.GraphQL.Test.QueryBuilders; using DbUp; -using DbUp.SQLite.Helpers; -using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using System; @@ -28,34 +26,66 @@ public UpdateTests(TestFixture fixture) [Fact(DisplayName = "UPDATE person succeeds")] public void UpdatePerson() { - var person = new Person + Person person = new Person { FirstName = "Douglas" }; + Person previousPerson = null; - // Update the person with Id = 1 with a new FirstName - SqlBuilder - .Update(person) - .Where("Id = @id", new { id = 1 }) - .Execute(fixture.DbConnection); - - // Build a person mapper for dapper - var personMapper = fixture + // Get an entity mapper factory + var entityMapperFactory = fixture .ServiceProvider - .GetRequiredService() - .Build(p => p.Id); - - // Get the same person back - person = SqlBuilder - .From() - .Select("Id", "FirstName") - .Where("Id = @id", new { id = 1 }) - .Execute(fixture.DbConnection, personMapper) - .FirstOrDefault(); - - // Ensure we got a person and their name was indeed changed - Assert.NotNull(person); - Assert.Equal("Douglas", person.FirstName); + .GetRequiredService(); + + try + { + // Update the person with Id = 2 with a new FirstName + using (var db = fixture.GetDbConnection()) + { + previousPerson = SqlBuilder + .From() + .Select("Id", "FirstName") + .Where("FirstName = @firstName", new { firstName = "Doug" }) + .Execute(db, entityMapperFactory.Build(p => p.Id)) + .FirstOrDefault(); + + SqlBuilder + .Update(person) + .Where("Id = @id", new { id = previousPerson.Id }) + .Execute(db); + + // Get the same person back + person = SqlBuilder + .From() + .Select("Id", "FirstName") + .Where("Id = @id", new { id = previousPerson.Id }) + .Execute(db, entityMapperFactory.Build(p => p.Id)) + .FirstOrDefault(); + } + + // Ensure we got a person and their name was indeed changed + Assert.NotNull(person); + Assert.Equal("Douglas", person.FirstName); + } + finally + { + if (previousPerson != null) + { + using (var db = fixture.GetDbConnection()) + { + person = new Person + { + FirstName = previousPerson.FirstName + }; + + // Put the entity back to the way it was + SqlBuilder + .Update(person) + .Where("Id = @id", new { id = 2 }) + .Execute(db); + } + } + } } } } \ No newline at end of file diff --git a/Dapper.GraphQL/Contexts/SqlDeleteContext.cs b/Dapper.GraphQL/Contexts/SqlDeleteContext.cs new file mode 100644 index 0000000..2f6c863 --- /dev/null +++ b/Dapper.GraphQL/Contexts/SqlDeleteContext.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; + +namespace Dapper.GraphQL +{ + public class SqlDeleteContext + { + public DynamicParameters Parameters { get; set; } + public string Table { get; private set; } + private List Deletes { get; set; } + + public SqlDeleteContext( + string table, + dynamic parameters = null) + { + if (parameters != null && !(parameters is IEnumerable>)) + { + parameters = ParameterHelper.GetSetFlatProperties(parameters); + } + this.Parameters = new DynamicParameters(parameters); + this.Table = table; + } + + public static SqlDeleteContext Delete(dynamic parameters = null) + { + return new SqlDeleteContext(typeof(TEntityType).Name, parameters); + } + + /// + /// Adds an additional DELETE statement after this one. + /// + /// The table to delete data from. + /// The data to be deleted. + /// The context of the DELETE statement. + public SqlDeleteContext Delete(string table, dynamic parameters = null) + { + if (Deletes == null) + { + Deletes = new List(); + } + var delete = SqlBuilder.Delete(table, parameters); + Deletes.Add(delete); + return this; + } + + /// + /// Executes the DELETE statement with Dapper, using the provided database connection. + /// + /// The database connection. + public int Execute(IDbConnection connection) + { + int result = connection.Execute(BuildSql(), Parameters); + if (Deletes != null) + { + // Execute each delete and aggregate the results + result = Deletes.Aggregate(result, (current, delete) => current + delete.Execute(connection)); + } + return result; + } + + /// + /// Renders the generated SQL statement. + /// + /// The rendered SQL statement. + public override string ToString() + { + return BuildSql(); + } + + /// + /// Builds the DELETE statement. + /// + /// A SQL DELETE statement. + private string BuildSql() + { + var sb = new StringBuilder(); + sb.Append($"DELETE FROM {Table} WHERE "); + sb.Append(string.Join(" AND ", Parameters.ParameterNames.Select(name => $"{name} = @{name}"))); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Dapper.GraphQL/Contexts/SqlInsertContext.cs b/Dapper.GraphQL/Contexts/SqlInsertContext.cs index d605265..cffa0b8 100644 --- a/Dapper.GraphQL/Contexts/SqlInsertContext.cs +++ b/Dapper.GraphQL/Contexts/SqlInsertContext.cs @@ -27,7 +27,7 @@ public SqlInsertContext( } /// - /// Executes the update statement with Dapper, using the provided database connection. + /// Executes the INSERT statements with Dapper, using the provided database connection. /// /// The database connection. public int Execute(IDbConnection connection) diff --git a/Dapper.GraphQL/Contexts/SqlQueryContext.cs b/Dapper.GraphQL/Contexts/SqlQueryContext.cs index e3dfff8..8f9f1d1 100644 --- a/Dapper.GraphQL/Contexts/SqlQueryContext.cs +++ b/Dapper.GraphQL/Contexts/SqlQueryContext.cs @@ -112,6 +112,15 @@ public IEnumerable Execute(IDbConnection connection, F return results.Where(e => e != null); } + /// + /// Gets an array of types that are used to split objects during entity mapping. + /// + /// + public List GetSplitOnTypes() + { + return _types; + } + /// /// Performs an INNER JOIN. /// @@ -295,15 +304,6 @@ public SqlQueryContext Select(params string[] select) return this; } - /// - /// Gets an array of types that are used to split objects during entity mapping. - /// - /// - public List GetSplitOnTypes() - { - return _types; - } - /// /// Instructs dapper to deserialized data into a different type, beginning with the specified column. /// diff --git a/Dapper.GraphQL/Dapper.GraphQL.csproj b/Dapper.GraphQL/Dapper.GraphQL.csproj index 65febbd..4b269fb 100644 --- a/Dapper.GraphQL/Dapper.GraphQL.csproj +++ b/Dapper.GraphQL/Dapper.GraphQL.csproj @@ -2,7 +2,7 @@ netcoreapp1.1 - 0.1.0 + 0.2.0 Doug Day Landmark Home Warranty A library designed to integrate the Dapper and graphql-dotnet projects with ease-of-use in mind and performance as the primary concern. @@ -13,6 +13,8 @@ git Dapper Dapper.SqlBuilder GraphQL graphql-dotnet core coreclr .NET Initial release. + 0.2.0.0 + 0.2.0.0 diff --git a/Dapper.GraphQL/Extensions/SqlInsertContextExtensions.cs b/Dapper.GraphQL/Extensions/SqlInsertContextExtensions.cs index 9647396..2a5e6b3 100644 --- a/Dapper.GraphQL/Extensions/SqlInsertContextExtensions.cs +++ b/Dapper.GraphQL/Extensions/SqlInsertContextExtensions.cs @@ -17,11 +17,10 @@ public static TIdentityType ExecuteWithSqlIdentity(this SqlInsert sb.AppendLine(context.ToString()); sb.AppendLine("SELECT CAST(SCOPE_IDENTITY() AS INT)"); } - else if (typeof(TIdentityType) == typeof(Guid)) + else if (typeof(TIdentityType) == typeof(long)) { - sb.AppendLine($"DECLARE @InsertedRows AS TABLE (Id UNIQUEIDENTIFIER);"); sb.AppendLine(context.ToString()); - sb.AppendLine($"SELECT Id FROM @InsertedRows"); + sb.AppendLine("SELECT CAST(SCOPE_IDENTITY() AS BIGINT)"); } else throw new InvalidCastException($"Type {typeof(TIdentityType).Name} in not supported this SQL context."); diff --git a/Dapper.GraphQL/SqlBuilder.cs b/Dapper.GraphQL/SqlBuilder.cs index 8eb6e11..aa7937b 100644 --- a/Dapper.GraphQL/SqlBuilder.cs +++ b/Dapper.GraphQL/SqlBuilder.cs @@ -13,6 +13,16 @@ namespace Dapper.GraphQL /// public static class SqlBuilder { + public static SqlDeleteContext Delete(string from, dynamic parameters = null) + { + return new SqlDeleteContext(from, parameters); + } + + public static SqlDeleteContext Delete(dynamic parameters = null) + { + return new SqlDeleteContext(typeof(TEntityType).Name, parameters); + } + public static SqlQueryContext From(string from, dynamic parameters = null) { return new SqlQueryContext(from, parameters); diff --git a/README.md b/README.md index 3f28673..afaffde 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Let's break down what's happening in the `Build()` method: #### Query builder chaining -Query builders are intended to chain, as our entities tend to have a hierarchical relationship. See the `PersonQueryBuilder.cs` file in the test project for a good example of chaining. +Query builders are intended to chain, as our entities tend to have a hierarchical relationship. See the [PersonQueryBuilder.cs](https://github.com/landmarkhw/Dapper.GraphQL/blob/master/Dapper.GraphQL.Test/QueryBuilders/PersonQueryBuilder.cs) file in the test project for a good example of chaining. ## GraphQL integration @@ -184,6 +184,10 @@ Field>( ); ``` +# Mapping objects of the same type + +The test project contains an example of how to handle this scenario. See [PersonEntityMapper.cs](https://github.com/landmarkhw/Dapper.GraphQL/blob/master/Dapper.GraphQL.Test/EntityMappers/PersonEntityMapper.cs). + # Examples See the Dapper.GraphQL.Test project for a full set of examples, including how *query builders* and *entity mappers* are designed. @@ -191,3 +195,4 @@ See the Dapper.GraphQL.Test project for a full set of examples, including how *q # Roadmap * Fluent-style pagination +* Async support \ No newline at end of file