Oracle .NET Data Provider and casting - ado.net

I use Oracle's specific data provider (11g), not the Microsoft provider that is being discontinued. The thing I've found about ODP.NET is how picky it is with data types. Where JDBC and other ADO providers just convert and make things work, ODP.NET will throw an invalid cast exception unless you get it exactly right.
Consider this code:
String strSQL = "SELECT DOCUMENT_SEQ.NEXTVAL FROM DUAL";
OracleCommand cmd = new OracleCommand(strSQL, conn);
reader = cmd.ExecuteReader();
if (reader != null && reader.Read()) {
Int64 id = reader.GetInt64(0);
return id;
}
Due to ODP.NET's pickiness on conversion, this doesn't work. My usual options are:
1) Retrieve into a Decimal and return it with a cast to an Int64 (I don't like this because Decimal is just overkill, and at least once I remember reading it was deprecated...)
Decimal id = reader.GetDecimal(0);
return (Int64)id;
2) Or cast in the SQL statement to make sure it fits into Int64, like NUMBER(18)
String strSQL = "SELECT CAST(DOCUMENT_SEQ.NEXTVAL AS NUMBER(18)) FROM DUAL";
I do (2), because I feel its just not clean pulling a number into a .NET Decimal when my domain types are Int32 or Int64. Other providers I've used are nice (smart) enough to do the conversion on the fly.
Any suggestions from the ODP.NET gurus?

This will work with your original SQL:
Int64 id = System.Convert.ToInt64(reader.GetValue(0));

Related

How to enumerate over columns with tokio-postgres when the field types are unknown at compile-time?

I would like a generic function that converts the result of a SQL query to JSON. I would like to build a JSON string manually (or use an external library). For that to happen, I need to be able to enumerate the columns in a row dynamically.
let rows = client
.query("select * from ExampleTable;")
.await?;
// This is how you read a string if you know the first column is a string type.
let thisValue: &str = rows[0].get(0);
Dynamic types are possible with Rust, but not with the tokio-postgres library API.
The row.get function of tokio-postgres is designed to require generic inference according to the source code
Without the right API, how can I enumerate rows and columns?
You need to enumerate the rows and columns, doing so you can get the column reference while enumerating, and from that get the postgresql-type. With the type information it's possible to have conditional logic to choose different sub-functions to both: i) get the strongly typed variable; and, ii) convert to a JSON value.
for (rowIndex, row) in rows.iter().enumerate() {
for (colIndex, column) in row.columns().iter().enumerate() {
let colType: string = col.type_().to_string();
if colType == "int4" { //i32
let value: i32 = row.get(colIndex);
return value.to_string();
}
else if colType == "text" {
let value: &str = row.get(colIndex);
return value; //TODO: escape characters
}
//TODO: more type support
else {
//TODO: raise error
}
}
}
Bonus tips for tokio-postgres code maintainers
Ideally, tokio-postgres would include a direct API that returns a dyn any type. The internals of row.rs already use the database column type information to confirm that the supplied generic type is valid. Ideally a new API uses would use the internal column information quite directly with improved FromSQL API, but a simpler middle-ground exists:-
It would be possible for an extra function layer in row.rs that uses the same column type conditional logic used in this answer to then leverage the existing get function. If a user such as myself needs to handle this kind of conditional logic, I also need to maintain this code when new types are handled by tokio-postgresql, therefore, this kind of logic should be included inside the library where such functionality can be better maintained.

How to compare a date in entity framework to avoid rounding errors?

I have a .NET DateTime value that I write to a SQL Server database "datetime" field (and trust me, I WISH we were just using "datetime2(7)" that matches .NET's DateTime precision exactly, but we're not).
Anyway, I write the entity to the database and that particular field ends up being '2016-03-03 08:55:19.560'.
It's a last processing time, and I'm looking for other records that were processed before that time. When I run an entity framework where clause, it ends up running a statement ending with "#p__linq__0='2016-03-03 08:55:19.5602354'" as the value it's comparing against, which ends up being slightly greater, even though these two values originate from the exact same DateTime instance.
I tried changing the time it's comparing against to an SqlDateTime, but then the lambda doesn't compile because it can't compare a DateTime? to a SqlDateTime. SqlDateTime has comparison methods, but I don't know whether entity framework recognizes the functions.
I can't even cast between the two in entity framework, which just gives the error "Unable to cast the type 'System.DateTime' to type 'System.Data.SqlTypes.SqlDateTime'. LINQ to Entities only supports casting EDM primitive or enumeration types."
I face the same problem. In my case I finally value the DateTime fields by using:
public static DateTime RoundedToMs(this DateTime dt) {
return new DateTime(dt.Ticks - (dt.Ticks % TimeSpan.TicksPerMillisecond), dt.Kind);
}
public static DateTime RoundedToMsForSql(this DateTime dt) {
DateTime n = dt.RoundedToMs();
return new DateTime(n.Year, n.Month, n.Day, n.Hour, n.Minute, n.Second, (n.Millisecond / 10) * 10);
}
And in the business code:
someEntity.SomeDate = dateValue.RoundedToMsForSql();
The point, in my case, is sql datetime has a 3ms precision, so I decided to remove millisecond unit.
Same extension may be used in the queries, well in fact to populate variables used in the queries.
var d = DateTime.Now.RoundedToMsForSql();
var q = from e in ctx.Entities where e.SomeDate <= d;

NHibernate QueryOver Conversion Error On DB2 Date Type

I'm new to NHibernate and I am hoping I can find some assistance in tracking down the source of a conversion error I'm getting when trying to use a DateTime comparison for a predicate.
return _session.QueryOver<ShipmentSegment>()
.Where(ss => ss.SegmentOrigin == selOrig)
// Whenever I add the predicate for the SegmentDate below
// I receive a conversion error
.And(ss => ss.SegmentDate == selDate)
.List<ShipmentSegment>();
Exception
NHibernate.Exceptions.GenericADOException was unhandled by user code
Message=could not execute query
[ SELECT this_.ajpro# as ajpro1_28_0_, this_.ajleg# as ajleg2_28_0_, this_.ajpu# as ajpu3_28_0_, this_.ajlorig as ajlorig28_0_, this_.ajldest as ajldest28_0_, this_.segdate as segdate28_0_, this_.ajldptwin as ajldptwin28_0_, this_.ajlfrtype as ajlfrtype28_0_, this_.ajlfrdest as ajlfrdest28_0_, this_.ajtpmfst# as ajtpmfst10_28_0_, this_.ajspplan as ajspplan28_0_, this_.ajhload as ajhload28_0_ FROM go52cst.tstshprte this_ WHERE this_.ajlorig = #p0 and this_.segdate = #p1 ]
Name:cp0 - Value:WIC Name:cp1 - Value:3/28/2012 12:00:00 AM
Inner Exception
Message=A conversion error occurred.
Source=IBM.Data.DB2.iSeries
ErrorCode=-2147467259
MessageCode=111
MessageDetails=Parameter: 2.
SqlState=""
StackTrace:
- at IBM.Data.DB2.iSeries.iDB2Exception.throwDcException(MpDcErrorInfo
mpEI, MPConnection conn)
- at IBM.Data.DB2.iSeries.iDB2Command.openCursor()
- at IBM.Data.DB2.iSeries.iDB2Command.ExecuteDbDataReader(CommandBehavior
behavior)
- at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
- at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd)
- at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection,
ISessionImplementor session)
- at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
- at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor
session, QueryParameters queryParameters, Boolean returnProxies)
- at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
I appreciate anything that can help point me in the right direction.
My experience with this specific iSeries exception came from when I was creating a parameter (of iDB2TypeParameter) for the ADO.NET command parameter list for a store procedure type ADO command. I had to explicitly tell what kind of iDB2DbType to use: iDB2DbType.Date, iDB2DbType.Time or iDB2DbType.TimeStamp. The iSeries ADO provider can't possibly know which of the three types to use when you create a parameter from a .NET System.DateTime type.
new iDB2Parameter(parameterName, iDB2DbType.Date){ Value = myValue };
new iDB2Parameter(parameterName, iDB2DbType.Time){ Value = myValue };
new iDB2Parameter(parameterName, iDB2DbType.TimeStamp){ Value = myValue };
I realize that you are not creating your parameters manually like this example but instead using NHibernate. So, I would make sure that NHibernate's LINQ provider for DB2/iSeries is aware of this. That's nothing against NHibernate, I just find it nearly impossible to find good, solid ORM for DB2/iSeries.

int to string in Entity Framework

How do you convert an int to a string in Link to EF?
The clr cant imagine casting an int to a string and Entity framework cant figure out what SQL snippet to translate .ToString() into.
So how do you write a linq statement that returns a string instead of an int?
Sadly EF does not know how to convert .ToString(). You must use the embedded function SqlFunctions.StringConvert: http://msdn.microsoft.com/en-us/library/dd466292.aspx
Also there is no overload for int so you must typecast to double :-(
var vendors =
from v in Vendors
select new
{
Code = SqlFunctions.StringConvert((double)v.VendorId)
};

Linq to Entities and Xml Fields

I have this scenario:
A SQL Server table myTable with field1, xmlField (nvarchar(50) and xml sql server data type)
Linq to entities
Now I'd like to get a query like this:
SELECT Field1, XmlField
FROM MyTable
WHERE CAST(XmlField AS nvarchar(4000)) = '<myXml />'
Obviously this is a correct query in SQL Server but I can't find a solution to write this in L2E.
Please notify that this code doesn't work:
var query = from row in context.MyTables
where (string)row.XmlField == "<myXml />"
select row
and other cast methods too.
This just because in L2E the "ToString" does't work correctly.
Now my idea is this one: an extension method:
var query = from row in context.MyTables
select row
query = query.CompareXml("XmlField", "<myXml />")
and this is the extended method:
public static IQueryable<TSource> CompareXml<TSource>(this IQueryable<TSource> source, string xmlFieldName, string xmlToCompare)
{
ConstantExpression xmlValue = Expression.Constant(xmlToCompare);
ParameterExpression parameter = Expression.Parameter(typeof(TSource), source.ElementType.Name);
PropertyInfo propertyInfo = typeof(TSource).GetProperty(xmlFieldName);
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, propertyInfo);
var stringMember = Expression.Convert(memberAccess, typeof(string));
BinaryExpression clauseExpression = Expression.Equal(xmlValue, stringMember);
return source.Where(Expression.Lambda<Func<TSource, bool>>(clauseExpression, parameter));
}
and again this doesn't work too.
Now I'd like to understand how I can force a "Convert" using Cast so I can compare Xml and nvarchar.
Thanks in advance
Massimiliano
Unfortunately EF still doesn't properly support XML columns. I'm afraid pretty much the only choice I know of is to create a view that does the cast and map that to a different entity. This will probably make the code awkward but also offers additional possible scenarios; for example, with a lot of SQL code, you could map elements in the XML columns to actual columns in the view, allowing you to make queries on specific parts of the XML.
On the bright side, at least inserting values in an XML column works pretty much as expected.