- Notifications
You must be signed in to change notification settings - Fork0
AdoNetCore.AseClient - a .NET Core DB Provider for SAP ASE
License
LincolnYang1/AdoNetCore.AseClient
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
A .NET Core DB Provider for SAP ASE
Packages available at NuGet:
| Name | Version |
|---|---|
AdoNetCore.AseClient (Recommended) | |
AdoNetCore.AseClient.StrongName (Strongly Named) |
SAP (formerly Sybase) has supported accessing the ASE database management system from ADO.NET for many years. Unfortunately SAP has not yet made a driver available to support .NET Core, so this project enables product teams that are dependent upon ASE to keep moving their application stack forwards.
The current .NET 4 version of SAP'sSybase.Data.AseClient driver is a .NET Framework managed wrapper around SAP's unmangedADO DB provider and is dependent uponCOM. COM is a Windows-only technology and will never be available to .NET Core, making it difficult to port the existing SAP driver.
Under the hood, ASE (and Microsoft Sql Server for that matter) relies on an application-layer protocol calledTabular Data Stream to transfer data between the database server and the client application. ASE uses TDS 5.0.
This project provides a .NET Core native implementation of the TDS 5.0 protocol via an ADO.NET DB Provider, making SAP ASE accessible from .NET Core applications hosted on Windows, Linux, Docker and also serverless platforms likeAWS Lambda.
The latest stable release of the AdoNetCore.AseClient isavailable on NuGet.
Functional parity with the
Sybase.Data.AseClientprovided by SAP. Ideally, our driver will be a drop in replacement for theSybase.Data.AseClient(with some namespace changes). The following types are supported:- AseClientFactory - .NET Core 2.1+
- AseCommand
- AseCommandBuilder
- AseConnection
- AseConnectionPool
- AseConnectionPoolManager
- AseDataAdapter
- AseDataReader
- AseDbType
- AseDecimal
- AseError
- AseErrorCollection
- AseException
- AseInfoMessageEventArgs
- AseInfoMessageEventHandler
- AseParameter
- AseParameterCollection
- AseRowUpdatedEventArgs - .NET Core 2.0+
- AseRowUpdatedEventHandler - .NET Core 2.0+
- AseRowUpdatingEventArgs - .NET Core 2.0+
- AseRowUpdatingEventHandler - .NET Core 2.0+
- TraceEnterEventHandler
- TraceExitEventHandler
Not all features are currently supported, and some features will not be supported. Refer toUnsupported features.
Performance equivalent to or better than that of
Sybase.Data.AseClientprovided by SAP. This is possible as we are eliminating the COM and OLE DB layers from this driver and .NET Core is fast.Target all versions of .NET Core (1.0, 1.1, 2.0, 2.1 and 2.2)
Should work withDapper at least as well as the
Sybase.Data.AseClient
To help adopt theAdoNetCore.AseClient, we have benchmarked it against theSybase.Data.AseClient. See the wiki for how torun the benchmarks yourself.
We have benchmarked theAdoNetCore.AseClient against theSybase.Data.AseClient in the following ways:
Open a connection (unpooled) and invoke AseCommand.ExecuteReader(...) once and read back one row of data.
Open a connection (pooled) and invoke AseCommand.ExecuteReader(...) once and read back one row of data.
Open a connection (unpooled) and invoke AseCommand.ExecuteReader(...) once and read back 12 rows of data.
Open a connection (pooled) and invoke AseCommand.ExecuteReader(...) once and read back 12 rows of data.
Open a connection (unpooled) and invoke AseCommand.ExecuteReader(...) 9 times, and read back 11-12 rows of data each time.
Open a connection (pooled) and invoke AseCommand.ExecuteReader(...) 9 times, and read back 11-12 rows of data each time.
Open a connection (unpooled) and invoke AseCommand.ExecuteReader(...) once, reading back 56 rows of data. Prepare a new AseCommand and invoke AseCommand.ExecuteNonQuery(...) for each of the 56 rows to update the database.
Open a connection (pooled) and invoke AseCommand.ExecuteReader(...) once, reading back 56 rows of data. Prepare a new AseCommand and invoke AseCommand.ExecuteNonQuery(...) for each of the 56 rows to update the database.
We perform these tests for .NET Core 1.1, .NET Core 2.0, and .NET Standard 4.6 using theAdoNetCore.AseClient. For comparison, we also perform these tests on .NET Standard 4.6 using theSybase.Data.AseClient from SAP.
The goal of the benchmarking is not to establish the absolute performance of the driver or the ASE Server, but to show its equivalence as a substitute. As such, the test client and database server have been held constant in all tests.
Adaptive Server Enterprise/16.0 SP03 PL02/EBF 27413 SMP/P/AMD64/Windows 2008 R2 SP1/ase160sp03pl02x/0/64-bit/FBO/Fri Oct 06 14:34:03 2017
BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)Processor=Intel Core i7-6700 CPU 3.40GHz (Skylake), ProcessorCount=8Frequency=3328123 Hz, Resolution=300.4697 ns, Timer=TSC.NET Core SDK=2.1.3[Host] : .NET Core 2.0.4 (Framework 4.6.25921.01), 64bit RyuJITDefaultJob : .NET Core 2.0.4 (Framework 4.6.25921.01), 64bit RyuJIT
In all of the test cases theAdoNetCore.AseClient performed better or equivalent to theSybase.Data.AseClient.
connectionstrings.com lists the following connection string properties for the ASE ADO.NET Data Provider. In keeping with our objective of being a drop-in replacement for theSybase.Data.AseClient, we aim to use identical connection string syntax to theSybase.Data.AseClient, however our support for the various properties will be limited. Our support is as follows:
| Property | Support | Notes |
|---|---|---|
AnsiNull | ✓ | By default (0) AnsiNull is disabled which means that SQL statements can use= NULL andIS NULL syntax. Set to 1 to instruct the connection to only permitIS NULL syntax. |
ApplicationName orApplication Name | ✓ | |
BufferCacheSize | ✓ | Buffer caching is automatically managed via an internalArrayPool. Setting this value in the connection string does nothing, but the behaviour is supported. |
Charset | ✓ | If not specified, the server should dictate the character set |
ClientHostName | ✓ | |
ClientHostProc | ✓ | |
CodePageType | ✕ | This doesn't appear to be relevant any more. You can specify theCharset without reference to a code page type, or allow the server to set theCharset which is the default behaviour. |
Connection Lifetime orConnectionLifetime | ✓ | |
ConnectionIdleTimeout orConnection IdleTimeout orConnection Idle Timeout | ✓ | |
CumulativeRecordCount | TODO | |
Database orDb orInitial Catalog | ✓ | |
Data Source orDataSource orAddress orAddr orNetwork Address orServer Name | ✓ | |
DSURL orDirectory Service URL | ✓ | Multiple URLs are not supported; network drivers other than NLWNSCK (TCP/IP socket) are not supported; LDAP is not supported |
EnableServerPacketSize | ✓ | |
Encryption | ✓ | The designated encryption. Possible values: ssl, none. |
EncryptPassword | ✓ | Values 0 (disabled) and 1 (enabled) are supported. The highest encryption standard of the ASE 15.x and 16x servers is implemented. |
LoginTimeOut orConnect Timeout orConnection Timeout | ✓ | For pooled connections this translates to the time it takes to reserve a connection from the pool |
Max Pool Size | ✓ | |
Min Pool Size | ✓ |
|
NamedParameters | ✓ | |
PacketSize orPacket Size | ✓ | The server can decide to change this value |
Ping Server | ✓ | |
Pooling | ✓ | |
Port orServer Port | ✓ | |
Pwd orPassword | ✓ | |
TextSize | ✓ | |
TrustedFile | ✓ | This property must be used along withEncryption=ssl. The value must be set to the path to the trusted file. |
Uid orUserID orUser ID orUser | ✓ | |
UseAseDecimal | ✓ |
| DbType | Send | .NET Type(s) | Notes |
|---|---|---|---|
AnsiString | ✓ | string | |
AnsiStringFixedLength | ✓ | string | |
Binary | ✓ | byte[] | |
Boolean | ✓ | bool | |
Byte | ✓ | byte | |
Currency | ✓ | decimal | Sent as decimal type; may change to send asTDS_MONEY, which is shorter |
Date | ✓ | DateTime | Time component is ignored |
DateTime | ✓ | DateTime | |
DateTime2 | X | ASE does not support aDateTime2 type. UseDateTime instead | |
DateTimeOffset | X | ASE does not support aDateTimeOffset type. UseDateTime instead | |
Decimal | ✓ | decimal | |
Double | ✓ | double | |
Guid | ✓ | System.Guid | Technically ASE does not support GUID or UUID types. Our driver supports it, but converts toBinary under the hood. You can obtain the same result by calling.ToByteArray() and usingDbType.Binary. |
Int16 | ✓ | short | |
Int32 | ✓ | int | |
Int64 | ✓ | long | |
Object | X | ASE does not support anObject type | |
SByte | ✓ | sbyte | Sent as int16 |
Single | ✓ | float | |
String | ✓ | string | UTF-16 encoded, sent to server as binary with usertype35 |
StringFixedLength | ✓ | string | UTF-16 encoded, sent to server as binary with usertype34 |
Time | ✓ | TimeSpan | |
UInt16 | ✓ | ushort | |
UInt32 | ✓ | uint | |
UInt64 | ✓ | ulong | |
VarNumeric | ✓ | decimal | |
Xml | X | ASE does not support anXml type |
| ASE Type | Receive | .NET Type(s) | Notes |
|---|---|---|---|
bigdatetime | X | DateTime | To be implemented.TDS_BIGDATETIME = 0xBB |
bigint | ✓ | long | |
bigtime | X | DateTime | To be implemented.TDS_BIGTIME = 0xBC |
binary | ✓ | byte[] | |
bit | ✓ | bool | |
char | ✓ | string | |
date | ✓ | DateTime | |
datetime | ✓ | DateTime | |
decimal | ✓ | decimal | |
double precision | ✓ | double | |
float | ✓ | float | |
image | ✓ | byte[] | |
int | ✓ | int | |
money | ✓ | decimal | |
nchar | ✓ | string | |
numeric | ✓ | decimal | |
nvarchar | ✓ | string | |
smalldatetime | ✓ | DateTime | |
smallint | ✓ | short | |
smallmoney | ✓ | decimal | |
time | ✓ | DateTime | We have added aGetTimeSpan method toAseDataReader |
tinyint | ✓ | byte | |
unichar | ✓ | string | Server sends as binary with usertype34 |
univarchar | ✓ | string | Server sends as binary with usertype35 |
unsigned bigint | ✓ | ulong | |
unsigned int | ✓ | uint | |
unsigned smallint | ✓ | usmallint | |
varchar | ✓ | string | |
text | ✓ | string | |
unitext | ✓ | string | |
varbinary | ✓ | byte[] |
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();// use the connection...}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();using(varcommand=connection.CreateCommand()){command.CommandText="SELECT FirstName, LastName FROM Customer";using(varreader=command.ExecuteReader()){// Get the results.while(reader.Read()){varfirstName=reader.GetString(0);varlastName=reader.GetString(1);// Do something with the data...}}}}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();using(varcommand=connection.CreateCommand()){command.CommandText="INSERT INTO Customer (FirstName, LastName) VALUES ('Fred', 'Flintstone')";varrecordsModified=command.ExecuteNonQuery();}}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();using(varcommand=connection.CreateCommand()){command.CommandText="SELECT COUNT(*) FROM Customer";varresult=command.ExecuteScalar();}}
Note: ASE only allowsOutput,InputOutput, andReturnValue parameters with stored procedures
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString){connection.Open();using(varcommand= connection.CreateCommand()){command.CommandText="SELECT TOP 1 FirstName FROM Customer WHERE LastName = @lastName";command.Parameters.AddWithValue("@lastName","Rubble");varresult=command.ExecuteScalar();}}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString){connection.Open();using(varcommand= connection.CreateCommand()){command.CommandText="GetCustomer";command.CommandType=CommandType.StoredProcedure;command.Parameters.AddWithValue("@lastName","Rubble");using(varreader=command.ExecuteReader()){// Get the results.while(reader.Read()){varfirstName=reader.GetString(0);varlastName=reader.GetString(1);// Do something with the data...}}}}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();using(varcommand=connection.CreateCommand()){command.CommandText="CreateCustomer";command.CommandType=CommandType.StoredProcedure;command.Parameters.AddWithValue("@firstName","Fred");command.Parameters.AddWithValue("@lastName","Flintstone");command.ExecuteNonQuery();}}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();using(varcommand=connection.CreateCommand()){command.CommandText="CountCustomer";command.CommandType=CommandType.StoredProcedure;varresult=command.ExecuteScalar();}}
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();using(varcommand=connection.CreateCommand()){command.CommandText="GetCustomerFirstName";command.CommandType=CommandType.StoredProcedure;command.Parameters.AddWithValue("@lastName","Rubble");varoutputParameter=command.Parameters.Add("@firstName",AseDbType.VarChar);outputParameter.Direction=ParameterDirection.Output;varreturnParameter=command.Parameters.Add("@returnValue",AseDbType.Integer);returnParameter.Direction=ParameterDirection.ReturnValue;command.ExecuteNonQuery();//Do something with outputParameter.Value and returnParameter.Value...}}
Execute a stored procedure and read response data withDapper
varconnectionString="Data Source=myASEserver;Port=5000;Database=myDataBase;Uid=myUsername;Pwd=myPassword;";using(varconnection=newAseConnection(connectionString)){connection.Open();varbarneyRubble=connection.Query<Customer>("GetCustomer",new{lastName="Rubble"},commandType:CommandType.StoredProcedure).First();// Do something with the result...}
About
AdoNetCore.AseClient - a .NET Core DB Provider for SAP ASE
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Languages
- C#100.0%
