Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MicrosoftSQL.ExecuteQuery - Added handling of SqlGeoraphy objects #60

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Frends.MicrosoftSQL.ExecuteQuery/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [2.2.0] - 2024-11-26
### Added
- Added method to form JToken from the SqlDataReader so that SqlGeography typed objects can be handled.
- Fixed how Scalar handles the data so that SqlGeography typed objects can be handled.
- Added Microsoft.SqlServer.Types version 160.1000.6 as dependency.
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved

## [2.1.0] - 2024-09-10
### Fixed
- Fixed how null values are handled by setting them as DBNull.Value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,72 @@ public async Task TestExecuteQuery_Auto()
CleanUp();
}
}

[TestMethod]
public async Task TestWithGeographyData()
{
var table = "geographytest";

var options = new Options()
{
SqlTransactionIsolationLevel = SqlTransactionIsolationLevel.None,
CommandTimeoutSeconds = 2,
ThrowErrorOnFailure = true
};

var createInput = new Input
{
ConnectionString = _connString,
Query = $"IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='{table}') BEGIN CREATE TABLE {table} ( Id int IDENTITY(1, 1), GeogCol1 geography, GeogCol2 AS GeogCol1.STAsText()); END",
ExecuteType = ExecuteTypes.Auto,
Parameters = null
};
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved

var create = await MicrosoftSQL.ExecuteQuery(createInput, options, default);
Assert.IsTrue(create.Success, "Create table");

var insert1Input = new Input
{
ConnectionString = _connString,
Query = $"INSERT INTO {table} (GeogCol1) VALUES (geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656 )', 4326));",
ExecuteType = ExecuteTypes.Auto,
Parameters = null
};

var insert1 = await MicrosoftSQL.ExecuteQuery(insert1Input, options, default);
Assert.IsTrue(insert1.Success, "First insert");

RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved
var insert2Input = new Input
{
ConnectionString = _connString,
Query = $"INSERT INTO {table} (GeogCol1) VALUES(geography::STGeomFromText('POLYGON((-122.358 47.653 , -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))', 4326));",
ExecuteType = ExecuteTypes.Auto,
Parameters = null
};

var insert2 = await MicrosoftSQL.ExecuteQuery(insert2Input, options, default);
Assert.IsTrue(insert2.Success, "Second insert");

var selectInput = new Input
{
ConnectionString = _connString,
Query = $"SELECT * From {table}",
ExecuteType = ExecuteTypes.Auto,
Parameters = null
};

var select = await MicrosoftSQL.ExecuteQuery(selectInput, options, default);
Assert.IsTrue(select.Success, "Select");

RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved
var dropInput = new Input
{
ConnectionString = _connString,
Query = $"DROP TABLE {table}",
ExecuteType = ExecuteTypes.Auto,
Parameters = null
};

var drop = await MicrosoftSQL.ExecuteQuery(dropInput, options, default);
Assert.IsTrue(drop.Success, "Drop");
}
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public void Init()
using var connection = new SqlConnection(_connString);
connection.Open();
var createTable = connection.CreateCommand();
createTable.CommandText = $@"IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='{_tableName}') BEGIN CREATE TABLE {_tableName} ( Id int, LastName varchar(255), FirstName varchar(255) ); END";
createTable.CommandText = $@"IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='{_tableName}') BEGIN CREATE TABLE {_tableName} ( Id int, LastName varchar(255), FirstName varchar(255)); END";
createTable.ExecuteNonQuery();
connection.Close();
connection.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,50 @@ public async Task TestExecuteQuery_Scalar()
CleanUp();
}
}

[TestMethod]
public async Task TestWithGeographyData_Scalar()
{
var table = "geographytest";

var options = new Options()
{
SqlTransactionIsolationLevel = SqlTransactionIsolationLevel.None,
CommandTimeoutSeconds = 2,
ThrowErrorOnFailure = true
};

var input = new Input
{
ConnectionString = _connString,
Query = $"IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='{table}') BEGIN CREATE TABLE {table} ( Id int IDENTITY(1, 1), GeogCol1 geography, GeogCol2 AS GeogCol1.STAsText()); END",
ExecuteType = ExecuteTypes.Auto,
Parameters = null
};

var create = await MicrosoftSQL.ExecuteQuery(input, options, default);
Assert.IsTrue(create.Success, "Create table");

RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved
input.Query = $"INSERT INTO {table} (GeogCol1) VALUES (geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656 )', 4326));";

var insert1 = await MicrosoftSQL.ExecuteQuery(input, options, default);
Assert.IsTrue(insert1.Success, "First insert");

input.Query = $"INSERT INTO {table} (GeogCol1) VALUES(geography::STGeomFromText('POLYGON((-122.358 47.653 , -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))', 4326));";

var insert2 = await MicrosoftSQL.ExecuteQuery(input, options, default);
Assert.IsTrue(insert2.Success, "Second insert");
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved

input.Query = $"SELECT GeogCol1 From {table}";
input.ExecuteType = ExecuteTypes.Scalar;

var select = await MicrosoftSQL.ExecuteQuery(input, options, default);
Assert.IsTrue(select.Success, "Select");
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved

input.Query = $"DROP TABLE {table}";
input.ExecuteType = ExecuteTypes.Auto;

var drop = await MicrosoftSQL.ExecuteQuery(input, options, default);
Assert.IsTrue(drop.Success, "Drop");
}
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using IsolationLevel = System.Data.IsolationLevel;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Types;

namespace Frends.MicrosoftSQL.ExecuteQuery;

Expand Down Expand Up @@ -102,8 +103,7 @@ private static async Task<Result> ExecuteHandler(Input input, Options options, S
if (input.Query.ToLower().StartsWith("select"))
{
dataReader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
table.Load(dataReader);
result = new Result(true, dataReader.RecordsAffected, null, JToken.FromObject(table));
result = new Result(true, dataReader.RecordsAffected, null, await LoadData(dataReader, cancellationToken));
await dataReader.CloseAsync();
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved
break;
}
Expand All @@ -116,12 +116,16 @@ private static async Task<Result> ExecuteHandler(Input input, Options options, S
break;
case ExecuteTypes.Scalar:
dataObject = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);

// JToken.FromObject() method can't handle SqlGeography typed objects so we convert it into string.
if (dataObject != null && dataObject.GetType() == typeof(SqlGeography))
dataObject = dataObject.ToString();

result = new Result(true, 1, null, JToken.FromObject(new { Value = dataObject }));
break;
case ExecuteTypes.ExecuteReader:
dataReader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
table.Load(dataReader);
result = new Result(true, dataReader.RecordsAffected, null, JToken.FromObject(table));
result = new Result(true, dataReader.RecordsAffected, null, await LoadData(dataReader, cancellationToken));
await dataReader.CloseAsync();
break;
default:
Expand Down Expand Up @@ -167,6 +171,35 @@ private static async Task<Result> ExecuteHandler(Input input, Options options, S
}
}

private static async Task<JToken> LoadData(SqlDataReader reader, CancellationToken cancellationToken)
{
var table = new JArray();
while (reader.HasRows)
{
while (await reader.ReadAsync(cancellationToken))
{
var row = new JObject();
for (var i = 0; i < reader.FieldCount; i++)
{
dynamic value;
if (reader.GetValue(i).GetType() == typeof(DBNull))
value = null;
else if (reader.GetValue(i).GetType() == typeof(SqlGeography))
value = reader.GetValue(i).ToString();
else
value = reader.GetValue(i);

row.Add(new JProperty(reader.GetName(i), value));
}

table.Add(row);
}
await reader.NextResultAsync(cancellationToken).ConfigureAwait(false);
}

return table;
}
RikuVirtanen marked this conversation as resolved.
Show resolved Hide resolved

private static IsolationLevel GetIsolationLevel(Options options)
{
return options.SqlTransactionIsolationLevel switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<Version>2.1.0</Version>
<Version>2.2.0</Version>
<Authors>Frends</Authors>
<Copyright>Frends</Copyright>
<Company>Frends</Company>
Expand All @@ -24,5 +24,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.SqlServer.Types" Version="160.1000.6" />
</ItemGroup>
</Project>