Has anybody experimented with these. Is this supported?
Table Value parameters aren't supported yet in LINQ to SQL. There's a few posts about this on Microsoft's MSDN forums:
DbType.Structured not available
SQL Server 2k8.User Defined Table Type. Mapping between DbType 'Structured' and Type 'System.Object'
The second link refers to Entity Framework, but the underlying issue is the same.
I actually had to try this myself. :0
It does not seem to be supported. I get this error when adding a stored procedure using a tvp parameter into the dbml file
DBML1005: Mapping between DbType 'Structured' and Type 'System.Object' in Parameter 'TVP' of Function 'dbo.spTestTableTypeParm' is not supported
Sad but true, I thought this could be a killer feature of sql 2008 and linq2sql
Related
I've inherited a project that uses SQLServer 2008 (and maybe 2005). Certain tables use nvarchar(255) and nvarchar(MAX) which Hibernate can't deal with. I've tried these proposed solutions:
How can Hibernate map the SQL data-type nvarchar(max)?
http://blog.cherouvim.com/sql-server-hbm2ddl-unicode-columns/
They don't work with nvarchar(MAX), unfortunately. I get this Exception
Caused by: org.hibernate.HibernateException: Wrong column type in MTP.dbo.Impact
edDetail for column Message. Found: ntext, expected: nvarchar(255)
The remaining problem is getting one of these solutions to work with nvarchar(MAX).
Is there a way to make Hibernate work with these unicode column types? If Hibernate won't work I can switch to another JPA provider that knows how to handle them properly.
Thanks.
I was able to handle nvarchar(MAX) by using the #Lob annotation on the corresponding field in the Entity class while also used the SqlServerNativeDialect class in the link above.
I am not able to call setNull on PreparedStatement using MS Access (sun.jdbc.odbc.JdbcOdbcDriver)
preparedStatement.setNull(index, sqltype).
Is there a workaround for this? For LONGBINARY data type, I tried the following calls, neither worked.
setNull(index, java.sql.Types.VARBINARY)
setNull(index, java.sql.Types.BINARY)
java.sql.SQLException: [Microsoft][ODBC Microsoft Access Driver]Invalid SQL data type
at sun.jdbc.odbc.JdbcOdbc.createSQLException(JdbcOdbc.java:6957)
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7114)
at sun.jdbc.odbc.JdbcOdbc.SQLBindInParameterNull(JdbcOdbc.java:986)
at sun.jdbc.odbc.JdbcOdbcPreparedStatement.setNull(JdbcOdbcPreparedStatement.java:363)
The answer that I have observed to work "quite well" for binding null to most data types with JDBC 4.1, Java 7, MS Access 2013 and the JDBC-ODBC bridge is this one, which I've built into jOOQ:
switch (sqlType) {
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.BLOB:
stmt.setNull(nextIndex(), Types.VARCHAR);
break;
default:
stmt.setString(nextIndex(), null);
break;
}
I just tested this and for an OLE Object (LONGBINARY) field in an Access 2010 database I found that all five of these variations allowed me to specify a null value as the parameter to a PreparedStatement using vanilla JDBC/ODBC Driver={Microsoft Access Driver (*.mdb, *.accdb)}:
s.setNull(4, java.sql.Types.LONGNVARCHAR);
s.setNull(4, java.sql.Types.LONGVARCHAR);
s.setNull(4, java.sql.Types.NCHAR);
s.setNull(4, java.sql.Types.NVARCHAR);
s.setNull(4, java.sql.Types.VARCHAR);
It is particularly interesting that
s.setNull(4, java.sql.Types.LONGVARBINARY);
does not work, considering that when we retrieve an OLE Object from an Access database what we get is a java.sql.Types.LONGVARBINARY according to a ResultSetMetaData object:
String SQL;
SQL = "SELECT Photo FROM City WHERE City_ID = 12";
s = conn.createStatement();
s.executeQuery(SQL);
ResultSet rs = s.getResultSet();
ResultSetMetaData rsmd = rs.getMetaData();
String accessTypeName = rsmd.getColumnTypeName(1);
int javaType = rsmd.getColumnType(1);
String javaTypeName = (
javaType == java.sql.Types.LONGVARBINARY
? "java.sql.Types.LONGVARBINARY"
: "some other Type"
);
System.out.println(String.format("The database-specific type name for this column is '%s'", accessTypeName));
System.out.println(String.format("The SQL type for this column is: %d (%s)", javaType, javaTypeName));
That returns:
The database-specific type name for this column is 'LONGBINARY'
The SQL type for this column is: -4 (java.sql.Types.LONGVARBINARY)
The Wikipedia article on ODBC includes a history suggesting that after an earlier effort ("SQL/CLI") became part of the ISO SQL standard, Microsoft essentially forked their own version and eventually came up with ODBC. If that is the case, then early efforts to conform to an "ODBC 'standard'" may have faced the same difficulties as those trying to conform to Microsoft's RTF document "standard": the "standard" was whatever Microsoft implemented and was subject to change at Microsoft's sole discretion.
However, Microsoft's 1995 ODBC White Paper, available via the download link here, consistently refers to the "OLE Object" datatype as mapping to "*BINARY" or "raw" types (or, in the case of SQL Server, to the now-deprecated IMAGE datatype). So, the CHAR/BINARY discrepancy doesn't appear to be a case of some early ODBC quirk that just got perpetuated.
Certainly this mystery is not new. A forum thread here from ~11 years ago suggests that this issue arose when something changed after JDK 1.4 was released.
And finally, Oracle has stated that the JDBC-ODBC Bridge "will be removed in JDK 8" (ref: here). So, if there hasn't been an "official" explanation (or a fix, for that matter), it is becoming increasingly unlikely that any will be forthcoming.
I saw a similar error once when I was sending a SQL query with 2 conditions in the where clause. One of the conditions needed to be quoted. It was a number in varchar format. The MSSQL server required that the condition be quoted or else I saw the error You got in your question.
In Linq to SQL SubmitChanges(ConflictMode.ContinueInConflict) i get Exception:
"String or binary data would be truncated.
The statement has been terminated."
But how to figure out the Property causing this? I guess i could start testing 1 by 1 the properties but what will happen if i have numerous properties?
Do you have SP1 installed? Looks like this was fixed.
http://social.msdn.microsoft.com/forums/en-US/linqtosql/thread/8a81482c-194a-4a16-8c04-80a27a90e1f4/
Great news... I installed SP1 for .Net
3.5 and guess what... they provide detailed information now!!!
Example: "Cannot insert the value NULL
into column 'FullQualifiedUserName',
table 'WcmsDevEmpty.Users.Users';
column does not allow nulls. INSERT
fails. The statement has been
terminated."
You can set the Log property on your data context to be a stream (such as Console.Out or a log file) to effectively profile the SQL being executed
This page from SQL Server 2008 BOL, talks about CLR Stored Procedures and has a section labelled, "Table-Valued Parameters", which talks about how they can be advantageous. That's great - I'd love to use TVPs in my CLR procs, but unfortunately this seems to be the only reference in the universe to such a possibility, and the section doesn't describe what the syntax would be (nor does the further information linked at the end of the paragraph)
Sure, I can easily find descriptions of how to use TVPs from T-SQL procs, or how to do CLR procs in general. But writing a CLR proc that takes a TVP? Nothing. This is all highly unusal since the passing of multi-row data to a stored proc is a popular problem.
This leads me to wonder if the presence of the section on that page is an error. Somebody please tell me it's not and point me to more info/ examples.
[EDIT]
I was about to post this to one of the MS forums too when I came across this, which seems to be the final nail in the coffin. Looks like it can't be done.
I can find a lot more references. However, these are all for passing table-valued parameters to TSQL procedures, so that's of little use.
However, I've come to the conclusion that it's impossible. First, there is the list of mappings between CLR and SQL types. For table types there is no mapping, so the following does not work, for example:
[SqlProcedure]
public static void StoredProcedure(DataTable tvp, out int sum)
{
return 42;
}
and then
CREATE TYPE MyTableType AS TABLE
(
Id INT NOT NULL PRIMARY KEY,
[Count] INT NOT NULL
)
GO
CREATE ASSEMBLY ClrTest FROM '<somePath>'
GO
CREATE PROCEDURE ClrTest
AS EXTERNAL NAME ClrTest.StoredProcedures.StoredProcedure
GO
Whatever type you try (DataTable, DbDataReader, IEnumerable), the CREATE PROCEDURE call keeps generating an error 6552: CREATE PROCEDURE for "ClrTest" failed because T-SQL and CLR types for parameter "#tvp" do not match.
Second, the documentation on the page you linked to says: A user-defined table type cannot be passed as a table-valued parameter to, or be returned from, a managed stored procedure or function executing in the SQL Server process.
I can not seem to find anywhere how to create a user defined table type in C#, but this also seems to be a dead end.
Maybe you can ask your question somewhere on a Microsoft forum. It's still odd that they mention table-valued parameters on the CLR sproc page but never explain how to implement this. If you find any solution, I'd like to know.
You can use a temporary table created and populated before you call the procedure and read the table inside the clr procedure.
The solution is to serialize your tabular data into a Json-formatted string then pass the string into your CLR proc. Within your clr proc or function you would parse the json to an IEnumerable, list, or tabular object. You may then work with the data as you would any other type of tabular data.
I have written some utilities capable of serializing any sql table into a Json formatted string. I would be happy to share them with anyone providing their e-mail address. Phil Factor has written a nice T-SQL Json parser he called parseJson. I have adapted his solution to the clr which performs much faster. Both accept a Json formatted string and produce a table from the string. I also have a variety of Json utilities I employ with both T-SQL and the CLR capable of serializing, parsing, inserting, deleting, and updating Json formatted strings stored in sql columns.
If you use C# (as opposed to VB, which lacks custom iterators) you can write ADO.NET code to invoke ExecuteNonQuery() and run a stored procedure with a SqlDbType.Structured parameter (i.e., a TVP).
The collection passed as the value of the TVP must implement IEnumerable<SqlDataRecord>. Each time this IEnumerable's yield return is executed, a SqlDataRecord “row” is pipelined to the "table" parameter.
See this article for details.
Whilst it looks like passing tables directly to CLR procedures is currently impossible, I got a result, albeit sub optimal by:
defining a TSQL table valued UDT FooTable
defining a TSQL function which takes FooTable as a param and returns XML using FOR XML EXPLICIT
passing the resultant XML to the CLR function/procedure instead of the table itself
Not ideal, but it gets a bit closer.
I'm trying to get LinqPad use my dbml model so I could analyze a query from my source code. I've set the DataContext to "Custom LINQ to SQL DataContext", and all the necessary settings, unfortunately even with the simplest query I get an error :
QUERY: from m in Linia select m
ERROR: Could not find an implementation of the query pattern for source type 'MyNameSpace.Linia'. 'Select' not found.
Does anybody know how to get this to work? Thx!
Found it! By default, LINQPad pluralizes table properties in the DataContext. I should have used Linias instead of Linia.