Query Service PositionBasedPaging doesn't work in MVC WebAPI - service

I want to get records by using paging from Query Service of Dynamics AX 2012 through MVC WebAPI.
When I run this code in Console Application works fine.
QueryServiceClient client = new QueryServiceClient();
DataSet dataSet;
Paging paging = new PositionBasedPaging() { StartingPosition = 1, NumberOfRecordsToFetch = 10 };
QueryMetadata query = new QueryMetadata();
QueryDataSourceMetadata customerDataSource;
query.QueryType = QueryType.Join;
query.DataSources = new QueryDataSourceMetadata[1];
customerDataSource = new QueryDataSourceMetadata();
customerDataSource.Name = "AccountNum";
customerDataSource.Enabled = true;
customerDataSource.FetchMode = FetchMode.OneToOne;
customerDataSource.Table = "CustTable";
customerDataSource.Company = "CEU";
customerDataSource.DynamicFieldList = true;
customerDataSource.OrderMode = OrderMode.OrderBy;
query.DataSources[0] = customerDataSource;
query.OrderByFields = new QueryDataOrderByMetadata[]{
new QueryDataOrderByMetadata(){ DataSource="AccountNum", FieldName="AccountNum",SortOrder=SortOrder.Ascending}
};
dataSet = client.ExecuteQuery(query, ref paging);
but the same code when runs in MVC WebAPI gives error
System.ServiceModel.FaultException`1 was unhandled by user code
HResult=-2146233087
Message=Page size 'PositionBasedPaging.StartingPosition' is out of range.
Parameter name: PositionBasedPaging.StartingPosition
Source=mscorlib
Action=http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault
StackTrace:
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at SL_AX_MVC_WebAPI.QueryService.IQueryService.ExecuteQuery(ExecuteQueryRequest request)
at SL_AX_MVC_WebAPI.QueryService.QueryServiceClient.SL_AX_MVC_WebAPI.QueryService.IQueryService.ExecuteQuery(ExecuteQueryRequest request) in d:\Projects\SL_AX_MVC\SL_AX_MVC_WebAPI\Service References\QueryService\Reference.cs:line 5269
at SL_AX_MVC_WebAPI.QueryService.QueryServiceClient.ExecuteQuery(QueryMetadata queryMetadata, Paging& paging) in d:\Projects\SL_AX_MVC\SL_AX_MVC_WebAPI\Service References\QueryService\Reference.cs:line 5276
at SL_AX_MVC_WebAPI.Repositories.CustomerRepository.getCustomers(Int32 skip, Int32 take) in d:\Projects\SL_AX_MVC\SL_AX_MVC_WebAPI\Repositories\CustomerRepository.cs:line 108
at SL_AX_MVC_WebAPI.Repositories.CustomerRepository.Get(Int32 skip, Int32 take) in d:\Projects\SL_AX_MVC\SL_AX_MVC_WebAPI\Repositories\CustomerRepository.cs:line 47
at SL_AX_MVC_WebAPI.Controllers.CustomerController.Get(Int32 skip, Int32 take) in d:\Projects\SL_AX_MVC\SL_AX_MVC_WebAPI\Controllers\CustomerController.cs:line 31
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
InnerException:
Any help ? i have wasted my time to tackle with this issue but no success...

Problem Solved!
When using in WebAPI, QueryService of Dynamics AX 2012 needs additional properties to provide value true for whatever property is given.
These properties are Post-fix with name "Specified". These properties must be given value true otherwise exception will be thrown or no data will return.
Correct Code is below:
QueryServiceClient client = new QueryServiceClient();
DataSet dataSet;
Paging paging = new PositionBasedPaging() { StartingPosition = 1, NumberOfRecordsToFetch = 10, NumberOfRecordsToFetchSpecified = true, StartingPositionSpecified = true };
QueryMetadata query = new QueryMetadata();
QueryDataSourceMetadata customerDataSource;
query.QueryType = QueryType.Join;
query.DataSources = new QueryDataSourceMetadata[1];
customerDataSource = new QueryDataSourceMetadata();
customerDataSource.Name = "AccountNum";
customerDataSource.Enabled = true;
customerDataSource.FetchMode = FetchMode.OneToOne;
customerDataSource.Table = "CustTable";
customerDataSource.Company = "CEU";
customerDataSource.DynamicFieldList = true;
customerDataSource.OrderMode = OrderMode.OrderBy;
customerDataSource.EnabledSpecified =
customerDataSource.FetchModeSpecified =
customerDataSource.DynamicFieldListSpecified =
customerDataSource.OrderModeSpecified = true;
query.DataSources[0] = customerDataSource;
query.OrderByFields = new QueryDataOrderByMetadata[]{
new QueryDataOrderByMetadata(){ DataSource="AccountNum", FieldName="AccountNum",SortOrder=SortOrder.Ascending}
};
dataSet = client.ExecuteQuery(query, ref paging);

Related

SqlDataReader not throwing an exception

I have method
public static SqlDataReader ExecuteReader(SqlCommand cmd, int level = 0)
{
SqlConnection conn = cmd.Connection;
SqlDataReader dr = null;
cmd.CommandTimeout = SqlConnectionTimeOut;
DateTime start = DateTime.Now;
DateTime end = new DateTime();
try
{
if (conn.State == System.Data.ConnectionState.Closed)
{
conn.Open();
}
dr = cmd.ExecuteReader();
}
catch (SqlException sqlex)
{
end = DateTime.Now;
TimeSpan requestDuration = end - start;
LoggerHelper.Info("SqlException - Execute reader duration before exception in milliseconds: " + requestDuration.TotalMilliseconds.ToString());
LoggerHelper.Error("Execute reader ", sqlex ,cmd);
if (sqlex.Number == 1205 && level <= 3) // will try to execute 3 times before throwing an Process deadlocked exception
{
LoggerHelper.Info(cmd.CommandText + " was a victim of deadlock. This was attempt " + (level + 1).ToString());
dr = ExecuteReader(cmd, level++);
}
else
{
throw new Exception("Execute reader ", sqlex);
}
// throw new Exception("Execute reader ", sqlex);
}
catch (Exception ex)
{
end = DateTime.Now;
TimeSpan requestDuration = end - start;
LoggerHelper.Info("Exception - Execute reader duration before exception in milliseconds: " + requestDuration.TotalMilliseconds.ToString());
LoggerHelper.Error("Execute reader ", ex, cmd);
throw new Exception("Execute reader ", ex);
}
return dr;
}
which is called from DAL
SqlDataReader reader = DataBridgeDb.ExecuteReader(TrisuraCommand);
datatable.Load(reader, LoadOption.OverwriteChanges, tablenames);
While debugging a SQL deadlock exception is properly thrown in ExecuteReader method.
But when I deploy or change connection string to staging database, exception is thrown in datatable.Load method.
Does anyone know why? I think it comes to database properties difference or SQL Server difference?
There is a problem on this line in the catch block:
dr = ExecuteReader(cmd, level++);
The ++ operator doesn't increment level until after the function call, so it passes 0 again the second time. And the third. And so on. You need this:
dr = ExecuteReader(cmd, level++);
As shown in this sample .Net Fiddle program:
https://dotnetfiddle.net/NWTZtS
I'm not sure how that causes the issue. I'd expect a StackOverflowException, the deadlock to eventually resolve, or an SqlException with a number other than 1205. But this is a bug, and I strongly suspect somehow tit is allows the method to eventually return a null value for the SqlDataReader object. At that point, of course the DataTable.Load() method will complain.

Parameter referenced in sql is an out parameter exception thrown

I have a named raw ad-hoc query and execute with an output parameter. I am adding both input and output parameters to the command object properly i believe. I am trying to understand that the parsing that goes on for output parameters in Npgsql and why it is failing. Any Ideas.. I have tried to provide some info here.. Let me know if you can help or would need additional info. I think this should be a straightforward use case to insert some data and get some scalar return vals back from a named query using out params
Postgres
BEGIN
SELECT nextval('Role_seq') into :v_roleId;
INSERT INTO Role (roleId, organizationId, name, notes, locked, roleTypeId, rightsFlags)
VALUES (:v_roleId, :v_organizationId, :v_name, :v_notes, :v_locked, :v_roleTypeId, :v_rightsFlags);
END;
SQL Server
INSERT INTO Role (organizationId, name, notes, locked, roleTypeId, rightsFlags)
VALUES (#organizationId, #name, #notes, #locked, #roleTypeId, #rightsFlags)
SELECT #roleId = SCOPE_IDENTITY()
Oracle
BEGIN
SELECT Role_roleId_SEQ.NEXTVAL into :v_roleId FROM DUAL;
INSERT INTO Role (roleId, organizationId, name, notes, locked, roleTypeId, rightsFlags)
VALUES (:v_roleId, :v_organizationId, :v_name, :v_notes, :v_locked, :v_roleTypeId, :v_rightsFlags);
END;
I am binding all the parameters properly and this code works on all platforms(providers) except Postgres where some query parsing is failing. Here is how i am adding the params.
dsh.AddNQParameter(cmd, "roleId", ParameterDirection.Output, (object)DBNull.Value, "Int", "Int32", "Integer");
dsh.AddNQParameter(cmd, "organizationId", ParameterDirection.Input, organizationId ?? (object)DBNull.Value, "Int", "Int32", "Integer");
dsh.AddNQParameter(cmd, "name", ParameterDirection.Input, name ?? (object)DBNull.Value, "VarChar", "Varchar2", "Varchar");
dsh.AddNQParameter(cmd, "notes", ParameterDirection.Input, notes ?? (object)DBNull.Value, "VarChar", "Varchar2", "Varchar");
dsh.AddNQParameter(cmd, "locked", ParameterDirection.Input, locked ?? (object)DBNull.Value, "Bit", "Byte", "Boolean");
dsh.AddNQParameter(cmd, "roleTypeId", ParameterDirection.Input, roleTypeId ?? (object)DBNull.Value, "Int", "Int32", "Integer");
dsh.AddNQParameter(cmd, "rightsFlags", ParameterDirection.Input, rightsFlags ?? (object)DBNull.Value, "Image", "Blob", "Bytea");
Stack Trace for Postgres
Result StackTrace:
at Npgsql.SqlQueryParser.ParseRawQuery(String sql, Boolean standardConformantStrings, NpgsqlParameterCollection parameters, List`1 statements)
at Npgsql.NpgsqlCommand.ProcessRawQuery()
at Npgsql.NpgsqlCommand.<Execute>d__71.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
at Npgsql.NpgsqlCommand.<ExecuteNonQuery>d__84.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Npgsql.NpgsqlCommand.ExecuteNonQuery()
at LandisGyr.Data.Helper.ExecuteNonQueryReturnInt(DbCommand cmd, String name) in D:\tfs\cc\Command Center\Components\LGDALGenerator\Main\LG.Data.Core\Foundation\Helper.cs:line 76
at DAL_Generator_Test.Data.NamedQueries.Test.NamedQueriesTest.InsRole(DbCommand cmd, Nullable`1 organizationId, String name, String notes, Nullable`1 locked, Nullable`1 roleTypeId, Byte[] rightsFlags, Nullable`1& roleId) in D:\tfs\cc\Command Center\Components\LGDALGenerator\Main\DAL Generator Test\Data\NamedQueries\NamedQueries.Test.Designer.cs:line 985
at DAL_Generator_Test.SqlServerTests.NamedQueriesPostgresTests.Execute_NonQuery_Test_Using_DbCommand() in D:\tfs\cc\Command Center\Components\LGDALGenerator\Main\DAL Generator Test\PostgresTests\NamedQueriesPostgresTests.cs:line 90
Result Message:
Test method DAL_Generator_Test.SqlServerTests.NamedQueriesPostgresTests.Execute_NonQuery_Test_Using_DbCommand threw exception:
System.Exception: Parameter ':v_roleId' referenced in SQL but is an out-only parameter
Code Sample to repro the problem
using Npgsql;
using NpgsqlTypes;
using System;
using System.Configuration;
using System.Data.Common;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string connectString = ConfigurationManager.ConnectionStrings["PostgresTest"].ConnectionString;
// create a table as follows
/*
* CREATE TABLE role
(
roleid integer NOT NULL DEFAULT nextval('role_seq'::regclass),
name character varying(50) COLLATE pg_catalog."default",
notes character varying(255) COLLATE pg_catalog."default",
organizationid integer NOT NULL,
roletypeid integer NOT NULL DEFAULT 0,
locked boolean NOT NULL DEFAULT false,
rightsflags bytea
)
*/
using (NpgsqlConnection con = new NpgsqlConnection(connectString))
{
con.Open();
using (DbCommand cmd = con.CreateCommand())
{
cmd.CommandText = #"BEGIN
SELECT nextval('Role_seq') into: v_roleId;
INSERT INTO Role(roleId, organizationId, name, notes, locked, roleTypeId, rightsFlags)
VALUES(:v_roleId, :v_organizationId, :v_name, :v_notes, :v_locked, :v_roleTypeId, :v_rightsFlags);
END;";
var roleIdParam = new NpgsqlParameter(":v_roleId", NpgsqlDbType.Integer);
roleIdParam.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(roleIdParam);
var orgParam = new NpgsqlParameter(":v_organizationId", NpgsqlDbType.Integer);
orgParam.Direction = System.Data.ParameterDirection.Input;
orgParam.Value = 1;
cmd.Parameters.Add(orgParam);
var nameParam = new NpgsqlParameter(":v_name", NpgsqlDbType.Varchar);
nameParam.Direction = System.Data.ParameterDirection.Input;
nameParam.Value = "test role";
cmd.Parameters.Add(nameParam);
var lockedParam = new NpgsqlParameter(":v_locked", NpgsqlDbType.Boolean);
lockedParam.Direction = System.Data.ParameterDirection.Input;
lockedParam.Value = false;
cmd.Parameters.Add(lockedParam);
var roleTypeIdParam = new NpgsqlParameter(":v_roleTypeId", NpgsqlDbType.Integer);
roleTypeIdParam.Direction = System.Data.ParameterDirection.Input;
roleTypeIdParam.Value = 1;
cmd.Parameters.Add(roleTypeIdParam);
var rightsFlagsParam = new NpgsqlParameter(":v_rightsFlags", NpgsqlDbType.Bytea);
rightsFlagsParam.Direction = System.Data.ParameterDirection.Input;
rightsFlagsParam.Value = DBNull.Value;
cmd.Parameters.Add(rightsFlagsParam);
cmd.ExecuteNonQuery();
object roleId = cmd.Parameters[":v_roleId"].Value;
Console.WriteLine($"role id is {roleId}");
Console.WriteLine("Press any key to continue");
Console.ReadLine();
}
}
}
}
}
I have read the documentation on "In / Out Parameters".
https://www.npgsql.org/doc/basic-usage.html
I did a test that returned the value of the sequence, in a very similar case.
Note that you do not set the output parameter in the SQL statement.
INSERT INTO x RETURNING x.roleIdINTO :roleId
Sample code
using Npgsql;
using NpgsqlTypes;
using System;
using System.Configuration;
using System.Data.Common;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string connectString = ConfigurationManager.ConnectionStrings["PostgresTest"].ConnectionString;
using (NpgsqlConnection con = new NpgsqlConnection(connectString))
{
con.Open();
using (DbCommand cmd = con.CreateCommand())
{
cmd.CommandText = #"INSERT INTO Role(roleId, organizationId, name, notes, locked, roleTypeId, rightsFlags)
VALUES(nextval('Role_seq'), :v_organizationId, :v_name, :v_notes, :v_locked, :v_roleTypeId, :v_rightsFlags) RETURNING roleId";
var orgParam = new NpgsqlParameter(":v_organizationId", NpgsqlDbType.Integer);
orgParam.Direction = System.Data.ParameterDirection.Input;
orgParam.Value = 1;
cmd.Parameters.Add(orgParam);
var nameParam = new NpgsqlParameter(":v_name", NpgsqlDbType.Varchar);
nameParam.Direction = System.Data.ParameterDirection.Input;
nameParam.Value = "test role";
cmd.Parameters.Add(nameParam);
var lockedParam = new NpgsqlParameter(":v_locked", NpgsqlDbType.Boolean);
lockedParam.Direction = System.Data.ParameterDirection.Input;
lockedParam.Value = false;
cmd.Parameters.Add(lockedParam);
var roleTypeIdParam = new NpgsqlParameter(":v_roleTypeId", NpgsqlDbType.Integer);
roleTypeIdParam.Direction = System.Data.ParameterDirection.Input;
roleTypeIdParam.Value = 1;
cmd.Parameters.Add(roleTypeIdParam);
var rightsFlagsParam = new NpgsqlParameter(":v_rightsFlags", NpgsqlDbType.Bytea);
rightsFlagsParam.Direction = System.Data.ParameterDirection.Input;
rightsFlagsParam.Value = DBNull.Value;
cmd.Parameters.Add(rightsFlagsParam);
var roleIdParam = new NpgsqlParameter("Returning_roleIdParam", NpgsqlDbType.Integer);
roleIdParam.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(roleIdParam);
cmd.ExecuteNonQuery();
object roleId = cmd.Parameters["Returning_roleIdParam"].Value;
Console.WriteLine($"role id is {roleId}");
Console.WriteLine("Press any key to continue");
Console.ReadLine();
}
}
}
}
}

How to edit and update the Drools rule file?

My project uses drools expert means (DRL) file. In the rules file, if the user wants to delete and update the rules, what should be done?
Rule file:
package com.sample;
import com.sample.Tuplebean;
import com.sample.DroolsBolt;
dialect "mvel"
rule "SafetyAlert-Critical"
when
t:Tuplebean(t.getSmoke() == true && t.getSmoke_density() == true && t.getTemperature() > 25.0)
then
DroolsBolt.insertToAlertLog("alert generated");
end
For example, the user wants to change the value of temperature to 30.0.
In my application i gone through same problem i fixed those problem by fallowing way , i am maintaining the all the rule file database.
I am keeping all the rule file in tables and i am loading every rule file based on the IMEI number(in my case i am generating one rule for one adaptor ie adaptor having unique IMEI number).
In below code first load the rule file from datbase and convert that string to the Resource beacuse Resource Factory accept only Resource as constructor argument. and store that in to the ksession and fire the rules.
For edit and delete operation i make one flag in database , if that flag is true again update the rule file to the database table.
if it is false load the same old rule file.
Here your generating the rule file and save to the database table and if flag is true and again your re-generate the rule file.
public void generateDrrols(String macAddress) throws SQLException, ClassNotFoundException, IOException{
logger.error("++++Generate drools file started++++");
Class.forName("org.postgresql.Driver");
Connection conn = DriverManager.getConnection("jdbc:postgresql://10.0.0.5:5432/iotdb",
"postgres", "track#123");
//String sql="SELECT alert_playload FROM iot.alert_configuration WHERE drools_boolean=true and alert_configuration_id=94 && 95";
String sql = ""
+ "select drools_condtion , iot.alert_configuration.alert_status , iot.alert_configuration.websocket_key , device_id , iot.alert_configuration.device_configuration_id , "
+ "email , iot.alert_configuration.mac_address , phone , sensor_id , iot.alert_configuration.sensor_configuration_id , alert_configuration_id , site_id , "
+ "startdatetime , enddatetime , exludedays , timetoexclude , log_update_time ,iot.sensor_configuration.sensor_name "
+ "from iot.alert_configuration "
+ "join iot.sensor_configuration on iot.sensor_configuration.sensor_configuration_id=iot.alert_configuration.sensor_configuration_id "
+ "where iot.alert_configuration.mac_address=? and soft_delete_flag=false";
PreparedStatement prepareStatement = conn.prepareStatement(sql);
prepareStatement.setString(1, macAddress);
ResultSet rs = prepareStatement.executeQuery();
//ResultSet rs = sta.executeQuery(sql);
final ResultSetGenerator converter = new ResultSetGenerator();
InputStream in = getClass().getResourceAsStream("/alert.drt");
final String drl = converter.compile(rs, in);
System.out.println(drl);
rs.close();
KnowledgeBuilder kbuilder =
KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newByteArrayResource(drl.getBytes()),
ResourceType.DRL);
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
StatefulKnowledgeSession kSession =
kbase.newStatefulKnowledgeSession();
logger.error("++++Reading rule file ie., rule.drl started++++");
logger.error("++++Making drool table status false++++");
String sql2="update iot.drool_rules set drool_rules=? where mac_address=?";
PreparedStatement prepareStatement2 = conn.prepareStatement(sql2);
prepareStatement2.setString(1, drl.toString());
prepareStatement2.setString(2, macAddress);
boolean execute2 = prepareStatement2.execute();
String sql1="update iot.alert_configuration set storm_drool_status=false where mac_address=?";
//Statement createStatement = conn.createStatement();
PreparedStatement prepareStatement3 = conn.prepareStatement(sql1);
prepareStatement3.setString(1, macAddress);
boolean execute = prepareStatement3.execute();
}
Here your loaing the rule file from database and fire all the rules.
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
try{
String sql="select drool_rules from iot.drool_rules where mac_address=?";
PreparedStatement prepareStatement = connectPostgres.prepareStatement(hql);
prepareStatement.setString(1, topicId);
ResultSet executeQuery1 = prepareStatement.executeQuery();
String rules=null;
while(executeQuery1.next()){
rules=executeQuery1.getString(1);
}
String i=null;
String replace = rules.replace('"', '\"');
System.out.println(replace);
Reader reader=(Reader)new StringReader(replace);
Resource myResource = ResourceFactory.newReaderResource(reader);
kbuilder.add(myResource, ResourceType.DRL);
}
catch(Exception e)
{
e.printStackTrace();
}
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
ksession = kbase.newStatefulKnowledgeSession();

How to use Multiple resultsets with POSTGRES JDBC?

I am using JDBC on a PostgreSQL database.
When I query for an entity in a resultset, it returns 5 rows.
Related to that entity is another entity, for which I query while i am using a row in the above resultset.
When I execute this query, the above resultset is closed.
This means that it is allowing only 1 resultset to be active on 1 connection at a time.
Previously the same code was working perfect for Oracle DB server.
Is it that I need to ask the DB admin to configure the server to allow multiple resultsets?
Or to do some change in the code?
Or is it impossible to do it in postgre?
Here is the code for more details:
Connection conn = PTSConnection.getConnection();
Statement stmt = conn.createStatement();
ResultSet lines = stmt.executeQuery("SELECT LINEID,STARTSTOPID,ENDSTOPID FROM LINES"); **//first resultset is active**
while (lines.next()){
int lineId= lines.getInt(1);
Stop ss = StopStorage.getByID(lines.getInt(2));
Stop es = StopStorage.getByID(lines.getInt(3));
ResultSet stops = stmt.executeQuery("SELECT STOPID FROM STOPSINLINES WHERE LINEID=" + lineId); **//first resultset dies**
List<Stop> lineStops = new ArrayList<Stop>();
while(stops.next()){
Stop stop = StopStorage.getByID(stops.getInt(1));
lineStops.add(stop);
}
stops.close();
Line aLine = null;
ResultSet emergencyLine = stmt.executeQuery("SELECT CAUSE, STARTTIME, ENDTIME FROM EMERGENCYLINES WHERE LINEID =" + lineId);
if(emergencyLine.next()){
String cause = emergencyLine.getString(1);
Time startTime = emergencyLine.getTime(2);
Time endTime = emergencyLine.getTime(3);
aLine = new EmergencyLine(ss, es, cause, startTime, endTime, (Stop[]) lineStops.toArray(new Stop[lineStops.size()]));
} else {
aLine = new Line(ss, es, (Stop[]) lineStops.toArray(new Stop[lineStops.size()]));
}
emergencyLine.close();
LineRepository.getInstance().addLine(aLine);
}
lines.close();
The reason is not that you are using two resultsets on the same connection, but you are re-using the same Statement object for a new query. When you run executeQuery() on a Statement instance, any previous result will be closed (I'm surprised that your code did work with Oracle...)
Simply create a new Statement object before executing the second query:
Statement stmt = conn.createStatement();
Statement nestedStmt = conn.createStatement();
ResultSet lines = stmt.executeQuery("SELECT LINEID,STARTSTOPID,ENDSTOPID FROM LINES"); **//first resultset is active**
while (lines.next()){
...
ResultSet stops = nestedStmt.executeQuery("SELECT STOPID FROM STOPSINLINES WHERE LINEID=" + lineId); **//first resultset dies**
List lineStops = new ArrayList();
while(stops.next()){
Stop stop = StopStorage.getByID(stops.getInt(1));
lineStops.add(stop);
}
stops.close();
...
ResultSet emergencyLine = nestedStmt.executeQuery("SELECT CAUSE, STARTTIME, ENDTIME FROM EMERGENCYLINES WHERE LINEID =" + lineId);
if(emergencyLine.next()){
String cause = emergencyLine.getString(1);
....
}
emergencyLine.close();
And don't for get to properly close all Statements and ResultSets !

Returning a DataTable using Entity Framework ExecuteStoreQuery

I am working with a system that has many stored procedures that need to be displayed. Creating entities for each of my objects is not practical.
Is it possible and how would I return a DataTable using ExecuteStoreQuery ?
public ObjectResult<DataTable> MethodName(string fileSetName) {
using (var dataContext = new DataContext(_connectionString))
{
var returnDataTable = ((IObjectContextAdapter)dataContext).ObjectContext.ExecuteStoreQuery<DataTable>("SP_NAME","SP_PARAM");
return returnDataTable;
}
Yes it's possible, but it should be used for just dynamic result-set or raw SQL.
public DataTable ExecuteStoreQuery(string commandText, params Object[] parameters)
{
DataTable retVal = new DataTable();
retVal = context.ExecuteStoreQuery<DataTable>(commandText, parameters).FirstOrDefault();
return retVal;
}
Edit: It's better to use classical ADO.NET to get the data model rather than using Entity Framework because most probably you cannot use DataTable even if you can run the method: context.ExecuteStoreQuery<DataTable>(commandText, parameters).FirstOrDefault();
ADO.NET Example:
public DataSet GetResultReport(int questionId)
{
DataSet retVal = new DataSet();
EntityConnection entityConn = (EntityConnection)context.Connection;
SqlConnection sqlConn = (SqlConnection)entityConn.StoreConnection;
SqlCommand cmdReport = new SqlCommand([YourSpName], sqlConn);
SqlDataAdapter daReport = new SqlDataAdapter(cmdReport);
using (cmdReport)
{
SqlParameter questionIdPrm = new SqlParameter("QuestionId", questionId);
cmdReport.CommandType = CommandType.StoredProcedure;
cmdReport.Parameters.Add(questionIdPrm);
daReport.Fill(retVal);
}
return retVal;
}
No, I don't think that'll work - Entity Framework is geared towards returning entities and isn't meant to return DataTable objects.
If you need DataTable objects, use straight ADO.NET instead.
This method uses the connection string from the entity framework to establish an ADO.NET connection, to a MySQL database in this example.
using MySql.Data.MySqlClient;
public DataSet GetReportSummary( int RecordID )
{
var context = new catalogEntities();
DataSet ds = new DataSet();
using ( MySqlConnection connection = new MySqlConnection( context.Database.Connection.ConnectionString ) )
{
using ( MySqlCommand cmd = new MySqlCommand( "ReportSummary", connection ) )
{
MySqlDataAdapter adapter = new MySqlDataAdapter( cmd );
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.SelectCommand.Parameters.Add( new MySqlParameter( "#ID", RecordID ) );
adapter.Fill( ds );
}
}
return ds;
}
Yes it can easily be done like this:
var table = new DataTable();
using (var ctx = new SomeContext())
{
var cmd = ctx.Database.Connection.CreateCommand();
cmd.CommandText = "Select Col1, Col2 from SomeTable";
cmd.Connection.Open();
table.Load(cmd.ExecuteReader());
}
By the rule, you shouldn't use a DataSet inside a EF application. But, if you really need to (for instance, to feed a report), that solution should work (it's EF 6 code):
DataSet GetDataSet(string sql, CommandType commandType, Dictionary<string, Object> parameters)
{
// creates resulting dataset
var result = new DataSet();
// creates a data access context (DbContext descendant)
using (var context = new MyDbContext())
{
// creates a Command
var cmd = context.Database.Connection.CreateCommand();
cmd.CommandType = commandType;
cmd.CommandText = sql;
// adds all parameters
foreach (var pr in parameters)
{
var p = cmd.CreateParameter();
p.ParameterName = pr.Key;
p.Value = pr.Value;
cmd.Parameters.Add(p);
}
try
{
// executes
context.Database.Connection.Open();
var reader = cmd.ExecuteReader();
// loop through all resultsets (considering that it's possible to have more than one)
do
{
// loads the DataTable (schema will be fetch automatically)
var tb = new DataTable();
tb.Load(reader);
result.Tables.Add(tb);
} while (!reader.IsClosed);
}
finally
{
// closes the connection
context.Database.Connection.Close();
}
}
// returns the DataSet
return result;
}
In my Entity Framework based solution I need to replace one of my Linq queries with sql - for efficiency reasons.
Also I want my results in a DataTable from one stored procedure so that I could create a table value parameter to pass into a second stored procedure. So:
I'm using sql
I don't want a DataSet
Iterating an IEnumerable probably isn't going to cut it - for efficiency reasons
Also, I am using EF6, so I would prefer DbContext.SqlQuery over ObjectContext.ExecuteStoreQuery as the original poster requested.
However, I found that this just didn't work:
_Context.Database.SqlQuery<DataTable>(sql, parameters).FirstOrDefault();
This is my solution. It returns a DataTable that is fetched using an ADO.NET SqlDataReader - which I believe is faster than a SqlDataAdapter on read-only data. It doesn't strictly answer the question because it uses ADO.Net, but it shows how to do that after getting a hold of the connection from the DbContext
protected DataTable GetDataTable(string sql, params object[] parameters)
{
//didn't work - table had no columns or rows
//return Context.Database.SqlQuery<DataTable>(sql, parameters).FirstOrDefault();
DataTable result = new DataTable();
SqlConnection conn = Context.Database.Connection as SqlConnection;
if(conn == null)
{
throw new InvalidCastException("SqlConnection is invalid for this database");
}
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddRange(parameters);
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
result.Load(reader);
}
return result;
}
}
The easiest way to return a DataTable using the EntityFramework is to do the following:
MetaTable metaTable = Global.DefaultModel.GetTable("Your EntitySetName");
For example:
MetaTable metaTable = Global.DefaultModel.GetTable("Employees");
Maybe your stored procedure could return a complex type?
http://blogs.msdn.com/b/somasegar/archive/2010/01/11/entity-framework-in-net-4.aspx