- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
identity column, mono
Joined: 29-Mar-2015
Hi all.
I know this is a Mono bug but I'm looking for way to get around it.
Very simple table, very simple code. LLBLGen Pro 2.6, but same goes for 4.2 (tried it). SQL Server database...
Table looks like this:
CREATE TABLE [dbo].[llblgen_test](
[id] [int] IDENTITY(1,1) NOT NULL,
[title] [nvarchar](50) NULL,
CONSTRAINT [PK_llblgen_test] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Code looks like this:
LlblgenTestEntity t = new LlblgenTestEntity();
t.Title = "Test";
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.SaveEntity(t);
}
In Windows works great, in Linux and Mono error is this:
Unhandled Exception:
SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException: An exception was caught during the execution of an action query: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 3 ("@Id"): Data type 0x26 has an invalid data length or metadata length.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception. ---> System.Data.SqlClient.SqlException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 3 ("@Id"): Data type 0x26 has an invalid data length or metadata length.
at System.Data.SqlClient.SqlConnection.ErrorHandler (System.Object sender, Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.OnTdsErrorMessage (Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessMessage (TdsPacketSubType subType) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessSubPacket () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.NextResult () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.SkipToEnd () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds70.ExecRPC (TdsRpcProcId rpcId, System.String sql, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds80.Execute (System.String commandText, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.Execute (Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.BatchActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteActionQuery (IActionQuery queryToExecute) [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.PersistQueue (System.Collections.Generic.List`1 queueToPersist, Boolean insertActions, System.Int32& totalAmountSaved) [0x00000] in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException: An exception was caught during the execution of an action query: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 3 ("@Id"): Data type 0x26 has an invalid data length or metadata length.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception. ---> System.Data.SqlClient.SqlException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 3 ("@Id"): Data type 0x26 has an invalid data length or metadata length.
at System.Data.SqlClient.SqlConnection.ErrorHandler (System.Object sender, Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.OnTdsErrorMessage (Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessMessage (TdsPacketSubType subType) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessSubPacket () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.NextResult () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.SkipToEnd () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds70.ExecRPC (TdsRpcProcId rpcId, System.String sql, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds80.Execute (System.String commandText, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.Execute (Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.BatchActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteActionQuery (IActionQuery queryToExecute) [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.PersistQueue (System.Collections.Generic.List`1 queueToPersist, Boolean insertActions, System.Int32& totalAmountSaved) [0x00000] in <filename unknown>:0
I repeat: I know this is a Mono bug, it's been mentioned on more than few places on web. It happens because of this part of inner LLBLGen query:
SELECT @Id=SCOPE_IDENTITY()
And it happens because @Id is undefined value.
When I do the query "by hand", like this:
using (SqlConnection connection = new SqlConnection(init_string)
using (SqlCommand cmd = new SqlCommand("insert into llblgen_test values(@p1); SELECT SCOPE_IDENTITY()", connection))
{
connection.Open();
cmd.Parameters.AddWithValue("p1", "Test");
int insertedID = (int)((decimal)cmd.ExecuteScalar());
Console.WriteLine(insertedID);
}
... I get the ID properly and there is no error.
Is there a way to get around this? Because if there's not, that means that I need to learn to use some other ORM or to write queries manually (because this huge project that's coming must be usable on Windows and Linux, and everything else I've tested works except of this). And I'm using LLBLGen Pro until 2008.
Thanks to everyone who tries to contribute!
I'm not a Mono expert, but it looks like this could be solved if you set a value to the @Id parameter just before been sent as part of the query.
If you are using SqlServer, the location where the parameter is created is in the SqlServerSpecificCreator. You could inherit from it and create your own MySqlServerSpecificCreator and override the following method:
DbParameter CreateParameter(IEntityFieldCore field, IFieldPersistenceInfo persistenceInfo, ParameterDirection direction, object valueToSet)
... you can call the parent and then set the param to a not-null value (like 0).
To use MySqlServerSpecificCreator you need to subclass the DynamicQueryEngine as well, and use a custom DataAccessAdapter that use that DQE. It seems like a lot of work, but it isn't, it's just a couple of lines per class.
Joined: 29-Mar-2015
Wow.
This is the definition of proper support.
Thank you.
Here is the code, everything works now. I'm pasting this because someone else might need it, too.
LlblgenTestEntity t = new LlblgenTestEntity();
t.Title = "Test";
using (DataAccessAdapterV2 adapter = new DataAccessAdapterV2())
{
adapter.SaveEntity(t);
Console.WriteLine(t.Id);
}
This works in Mono!
DataAccessAdapterV2 (and other classes) are declared here.
class SqlServerSpecificCreatorV2 : SD.LLBLGen.Pro.DQE.SqlServer.SqlServerSpecificCreator
{
public override System.Data.IDataParameter CreateParameter(IEntityFieldCore field, IFieldPersistenceInfo persistenceInfo, System.Data.ParameterDirection direction, object valueToSet)
{
if ((valueToSet == null) && (field.IsPrimaryKey) && (field.DataType.Name.ToLower().StartsWith("int")))
valueToSet = 0;
return base.CreateParameter(field, persistenceInfo, direction, valueToSet);
}
}
class DynamicQueryEngineV2 : SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine
{
public DynamicQueryEngineV2() : base()
{
Creator = new SqlServerSpecificCreatorV2();
}
}
class DataAccessAdapterV2 : Database.DatabaseSpecific.DataAccessAdapter
{
protected override SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase CreateDynamicQueryEngine()
{
return new DynamicQueryEngineV2();
}
}
This sets the value to 0 if value is null, type is integer and parameter is primary key. Of course this can be freely modified, expanded etc, according to needs.
If you have any objections regarding my code, please tell me. For instance, I'm using "using" keyword for creating the adapter and that's why I'm not closing the connection (i think it should be closed automatically). Then, I'm creating a new instance of Creator, should I maybe dispose the old one first? I don't want to create some memory leak, bunch of unclosed connections etc. This is the first time for me using the Adapter, until now I've used SelfServicing.
Thank you again.
EDIT: I've continued with my work and stumbled upon similar problem. When there is XML datatype in SQL Server table and I try to write anything into that field, I also get an error. If I leave that field empty, everything works well. Field name is "Response".
Unhandled Exception:
SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException: An exception was caught during the execution of an action query: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 8 ("@Response"): Data type 0xE7 has an invalid data length or metadata length.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception. ---> System.Data.SqlClient.SqlException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 8 ("@Response"): Data type 0xE7 has an invalid data length or metadata length.
at System.Data.SqlClient.SqlConnection.ErrorHandler (System.Object sender, Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.OnTdsErrorMessage (Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessMessage (TdsPacketSubType subType) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessSubPacket () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.NextResult () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.SkipToEnd () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds70.ExecRPC (TdsRpcProcId rpcId, System.String sql, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds80.Execute (System.String commandText, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.Execute (Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.BatchActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteActionQuery (IActionQuery queryToExecute) [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.PersistQueue (System.Collections.Generic.List`1 queueToPersist, Boolean insertActions, System.Int32& totalAmountSaved) [0x00000] in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException: An exception was caught during the execution of an action query: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 8 ("@Response"): Data type 0xE7 has an invalid data length or metadata length.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception. ---> System.Data.SqlClient.SqlException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 8 ("@Response"): Data type 0xE7 has an invalid data length or metadata length.
at System.Data.SqlClient.SqlConnection.ErrorHandler (System.Object sender, Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.OnTdsErrorMessage (Mono.Data.Tds.Protocol.TdsInternalErrorMessageEventArgs e) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessMessage (TdsPacketSubType subType) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.ProcessSubPacket () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.NextResult () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds.SkipToEnd () [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds70.ExecRPC (TdsRpcProcId rpcId, System.String sql, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at Mono.Data.Tds.Protocol.Tds80.Execute (System.String commandText, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.Execute (Boolean wantResults) [0x00000] in <filename unknown>:0
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.BatchActionQuery.Execute () [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteActionQuery (IActionQuery queryToExecute) [0x00000] in <filename unknown>:0
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.PersistQueue (System.Collections.Generic.List`1 queueToPersist, Boolean insertActions, System.Int32& totalAmountSaved) [0x00000] in <filename unknown>:0
When I run the app in debugging mode, this is what LLBLGen debug output looks like:
Generated Sql query:
Query: INSERT INTO [Logs].[dbo].[Log] ([Source], [Severity], [Details], [Timestamp], [Response]) VALUES (@Source, @Severity, @Details, @Timestamp, @Response);SELECT @Id=SCOPE_IDENTITY()
Parameter: @Id : Int64. Length: 0. Precision: 19. Scale: 0. Direction: Output. Value: 0.
Parameter: @Source : String. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: "Internal API test".
Parameter: @Severity : Byte. Length: 1. Precision: 3. Scale: 0. Direction: Input. Value: 0.
Parameter: @Details : String. Length: 1073741823. Precision: 0. Scale: 0. Direction: Input. Value: "Test log".
Parameter: @Timestamp : DateTime. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: 29.9.2015. 23:25:07.
Parameter: @Response : Xml. Length: 2147483647. Precision: 0. Scale: 0. Direction: Input. Value: TEST.
Any clues?
klokan wrote:
Here is the code, everything works now. I'm pasting this because someone else might need it, too. ... This sets the value to 0 if value is null, type is integer and parameter is primary key. Of course this can be freely modified, expanded etc, according to needs.
Thanks for the feedback
klokan wrote:
If you have any objections regarding my code, please tell me. For instance, I'm using "using" keyword for creating the adapter and that's why I'm not closing the connection (i think it should be closed automatically). Then, I'm creating a new instance of Creator, should I maybe dispose the old one first? I don't want to create some memory leak, bunch of unclosed connections etc. This is the first time for me using the Adapter, until now I've used SelfServicing.
It's ok to write it with "using" as the connection is disposable if possible when the adapter goes out of scope.
klokan wrote:
EDIT: I've continued with my work and stumbled upon similar problem. When there is XML datatype in SQL Server table and I try to write anything into that field, I also get an error. If I leave that field empty, everything works well. Field name is "Response".
Unhandled Exception: SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException: An exception was caught during the execution of an action query: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 8 ("@Response"): Data type 0xE7 has an invalid data length or metadata length.. Check InnerException, QueryExecuted and ... Parameter: @Response : Xml. Length: 2147483647. Precision: 0. Scale: 0. Direction: Input. Value: TEST.
...
- At this point, what LLBLGen version and runtime library version are you using?
- What is column definition of 'Response' column in your DB?
- What is the mapped type specifics (type, lenght, precision, etc) of 'Response' field in LLBLGen Designer?
Joined: 29-Mar-2015
LLBLGen Pro 2.6, runtime version v2.0.50727.
"Response" is type XML, like this:
CREATE TABLE [dbo].[Log](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Source] [nvarchar](50) NOT NULL,
[Severity] [tinyint] NOT NULL,
[Details] [ntext] NULL,
[Timestamp] [datetime] NULL,
[Response] [xml] NULL,
CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
In attachment you can find screenshot of LLBLGen designer.
Filename | File size | Added on | Approval |
---|---|---|---|
table.png | 78,842 | 30-Sep-2015 08:51.55 | Approved |
klokan wrote:
When I change data type to ntext, save works, but i need to have XML data type, not text.
Can you live with ntext on you LLBLGen Designer even if you db column is XML? To avoid re-editing your mappings on every refresh, you could have ntext in your dev database and xml in your production database. I'm not sure if this would work but I think it might.
I didn't found any information out there that gives me a clue of this issue. I think that, if you modify the length of the field to a 'expected' length it should work, but I don't know what is the appropriate length SqlClient on mono is expecting.
Joined: 29-Mar-2015
Well maybe I could live with it, but I don't know how to change data type to ntext in designer without having to modify DB and do the refresh.
Is there a way to find out XML data type length when reading from DB? Maybe there is some property in LLBLGen generated classes which has that value...
klokan wrote:
Well maybe I could live with it, but I don't know how to change data type to ntext in designer without having to modify DB and do the refresh.
Is there a way to find out XML data type length when reading from DB? Maybe there is some property in LLBLGen generated classes which has that value...
There are a couple of ways: (in v3 and higher you can change the length in the designer) 1) you build the sqldriver from source (it's available in the SDK archive available on the website under 'My Account'), and in SqlServerSchemaRetriever.cs, you change at line 983 the maxlength if newField.TypeDefinition.DBType == (int)SqlDbTypes.Xml.
2) You alter the length in the PersistenceInfoProvider.cs file, in the method which maps the table with the xml field
3) You could also change the SqlServer DQE (as you seem to have already done so, according to your posts in this thread ), and for Xml typed parameters, it could simply not set the length at all or to 1GB instead of 2GB-1. This is done in SqlServerSpecificCreator.cs, CreateParameter(4). Best is to override that, first call the base method, then check the parameter returned what its DbType property is, if it's DbType.Xml, set the length to e.g. 1GB.
No idea why Mono throws here though...
Joined: 29-Mar-2015
I tried solution 2) and it works.
I took this line:
base.AddElementFieldMapping( "LogEntity", "Response", "Response", true, (int)SqlDbType.Xml, 2147483647, 0, 0, false, "", null, typeof(System.String), 5 );
And when I changed 2147483647 to 0, it worked!
The only problem there is that I have to modify this line after every code generation.
The most elegant solution would be 3) and I solved it like this:
public override System.Data.IDataParameter CreateParameter(IEntityFieldCore field, IFieldPersistenceInfo persistenceInfo, System.Data.ParameterDirection direction)
{
System.Data.IDataParameter p = base.CreateParameter(field, persistenceInfo, direction);
if (p.DbType==System.Data.DbType.Xml)
((System.Data.SqlClient.SqlParameter)(p)).Size = 0;
return p;
}
Thank you for your patience.