The return value of an ObjectParameter is null - entity-framework-4.1

First of all I apologize for my bad English.
I am using Entity Framework to map stored procedures on an Oracle database.
The following code is what I have written to call the stored procedure and return values ​​using the ObjectParameter.
ObjectParameter p_TotalCount = new ObjectParameter("TOTALCOUNT", typeof(int));
ObjectParameter p_TotalCountPos = new ObjectParameter("TOTALCOUNT_POS", typeof(int));
ObjectResult<EVENT_FULLTEXT_RESULT> res = ctx.GET_EVENTS_FULLTEXT2(p1, p2, ..., p_TotalCount, p_TotalCountPos);
totalCount = Convert.ToInt32(p_TotalCount.Value);
totalCountPos = Convert.ToInt32(p_TotalCountPos.Value);
unlike what returns when I call the stored procedure from PLSQL developer, in this case p_TotalCount.Value return null.
Following the mapping that generated of entity framework for this stored procedure
<FunctionImport Name="GET_EVENTS_FULLTEXT2" ReturnType="Collection(Model.EVENT_FULLTEXT_RESULT)">
<Parameter Name="p1" Mode="In" Type="String" />
...
...
<Parameter Name="TOTALCOUNT" Mode="Out" Type="Decimal" />
<Parameter Name="TOTALCOUNT_POS" Mode="Out" Type="Decimal" />
</FunctionImport>

Related

How do I return my auto-generated ID in my MyBatis object?

I’m using MySQL 5.5.46 with MyBatis 3.3 and Spring 4.2. I have a table with a primary key column that is of type VARCHAR(32). In my mapping file, I have this statement. Notice that I use MySql’s uuid() to auto-generate an ID. How do I then take this ID and populate it in the returned object? I’m trying LAST_INSERT_ID() but that isn’t doing it …
<insert id="insertMyObject" parameterType="org.mainco.myproject.domain.MyObject" keyProperty="id"  keyColumn="id">
insert into cb_myproject_sync_entity_link(id, links_self, links_schools, links_teachers, links_students, links_sections, links_grade_levels, links_contacts)
values(replace(uuid(), '-', ''),#{linksSelf}, #{linksSchools}, #{linksTeachers}, #{linksStudents}, #{linksSections}, #{linksGradeLevels}, #{linksContacts})
<selectKey keyProperty="id" resultType="String" order="AFTER">SELECT LAST_INSERT_ID();</selectKey>
</insert>
Try to use this setting in mybatis configuration:
<settings>
<setting name="useGeneratedKeys" value="true"/>
</settings>

Returning two recordsets from a method with returntype of JSON?

I have a CFC, which contains a method/function that runs one stored procedure and then outputs two results sets. Like such (shorted the code for speed of reading):
<cffunction name="OrdersandRegions" returntype="query" returnformat="JSON">
<cfstoredproc procedure="GetUserInfo">
<cfprocresult name = "UserOrders" resultset="1">
<cfprocresult name = "UserRegions" resultset="2">
</cfstoredproc>
<!--- Currently only returning 1 resultset as JSON --->
<cfreturn UserOrders>
</cffunction>
Firstly can I return both UserOrders and UserRegions result sets from a single method?
If this was not in a CFC and was within a CFM page, then I was able to easily access both results by using #UserOrders.OrderID# or #UserRegions.UserID# for example.
Because its being returned from a CFC and that too as JSON data, how do I achieve what I'm trying to achieve?
A function can only return a single object. Just put the queries inside another object, like an array or structure, and return that object instead of a query. I would use a structure as they are more intuitive than arrays. Then in your JSON, you can access each resultset by key name: "orders" or "regions". I cannot test this right now, but something along these lines:
<cffunction name="OrdersandRegions" returntype="struct" returnformat="JSON">
<cfstoredproc procedure="account_customer_EnrolmentsSEL" ...>
<cfprocresult name = "Local.UserOrders" resultset="1">
<cfprocresult name = "Local.UserRegions" resultset="2">
</cfstoredproc>
<cfset Local.result = {}>
<cfset Local.result["orders"] = Local.UserOrders>
<cfset Local.result["regions"] = Local.UserRegions>
<cfreturn Local.result>
</cffunction>
(Side note, be sure to Local/var scope all function local variables.)

Multiple Datasources with CFTRANSACTION

I have run into the following error:
Datasource names for all the database tags within the cftransaction tag must be the same.
This has come about from the following code:
transaction action="begin" {
try {
var data = {};
data.time = getTickCount();
addToLog("Persist", "Started persist operations");
doClientPersist();
cleanUp(arguments.importId);
addToLog("Persist", "Completed the persist operations successfully", ((getTickCount()-data.time)/1000));
return true;
} catch (any e) {
transactionRollback();
data.error = e;
}
}
The transaction is effectively wrapping allot of lower level methods within doClientPersist(). One such call, which is deep within our frameworks database abstraction layer, fetches (SELECTs) longitude and latitude information from a separate datasource (lets say the Postcode data source) - This datasource is strictly read only.
<cffunction name="getLatitudeAndLongitude" access="package" returntype="query" output="false">
<cfargument name="postcode" type="string" required="true" />
<cfset var qPostcode = ''/>
<cfquery name="qPostcode" datasource="postcodesDatasource">
SELECT
a.latitude,
a.longitude
FROM
postcodes AS a
WHERE
a.postcode = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#postcode#"/>
</cfquery>
<cfreturn qPostcode/>
</cffunction>
<cffunction name="getPostcodeCoordinates" access="public" returntype="struct" output="false">
<cfargument name="postcode" type="string" required="true"/>
<cfscript>
var data = {};
data.postcode = getFormattedPostcode(arguments.postcode);
data.valid = isValidPostcode(data.postcode);
data.coords = {};
if (data.valid) {
data.query = getLatitudeAndLongitude(data.postcode);
if (isQuery(data.query) && data.query.recordCount) {
data.coords["latitude"] = data.query["latitude"][1];
data.coords["longitude"] = data.query["longitude"][1];
} else if (data.valid == 2) {
/** No match, try short postcode (RECURSIVE) **/
data.coords = getPostcodeCoordinates(trim(left(data.postcode, len(data.postcode)-3)));
}
}
return data.coords;
</cfscript>
</cffunction>
Reading into the issue, the docs say the following:
In a transaction block, you can write queries to more than one database, but you must commit or roll back a transaction to one database before writing a query to another.
Unfortunately, as mentioned above, the code fetching this postcode data is completely unrelated to the actual persist operation, because it executes a web of lower level methods that cannot be changed I am unable to commit the "top level" transaction before making the call to the remote datasource.
Is there anyway that I can wrap the "top level" method within a transaction and still have the call to the "postcode" datasource - It would be silly for us to have to duplicate the postcode information for each client, however the operation MUST be rolled back if something goes wrong.
Thanks in advance.
As I can see it, you have basically two choices.
1) Query your data outside of the transaction. Depending on the specifics of your application, this could be moving that method before the transaction block, splitting up the method and moving part of it before the transaction block, pre-fetching the data into RAM (holding the data perhaps as a query in a variable) and then having your method use this pre-fetched data rather than querying the database directly.
The upshot of all of these solutions, however, is the same. That is that the SELECT query itself is performed outside of the transaction.
If that is impractical for whatever reason, then on to...
2) Use the same datasource. Note that you do not have to use the same database, just the same datasource. So, you can database.tablename syntax in MySQL.
With just quick searching, I found a good example of this:
Querying multiple databases at once
Someone with better Google-fu than I could probably come up with better examples pretty quickly.
The basics though is that you use FROM database.tablename instead of just FROM tablename in your query.
I believe this would require the databases to be on the same MySQL server however.
So I've been a bit confused on how to resolve this one. I've accepted Steve's answer as he gave me the idea (thanks), but added the code below to show a simple example of the solution.
For me the datasource data could not be duplicated and the code above needed its wrapping transaction.
So it only really left me with one solution, a half baked one in my opinion...
<cffunction name="methodA" access="public" returntype="query" output="false">
<cfset var q = ""/>
<cfquery name="q" datasource="test_1">
SELECT id, name FROM table_a
</cfquery>
<cfset methodB = methodB()/>
<cfreturn q/>
</cffunction>
<cffunction name="methodB" access="public" returntype="query" output="false">
<cfset var q = ""/>
<cfquery name="q" datasource="test_1">
SELECT id, name FROM table_b
</cfquery>
<cfset methodC = methodC()/>
<cfreturn q/>
</cffunction>
<cffunction name="methodC" access="public" returntype="void" output="false">
<cfset var q = ""/>
<!---
This will error with the following:
Datasource test_2 verification failed.
The root cause was that: java.sql.SQLException:
Datasource names for all the database tags within the cftransaction tag must be the same.
<cfquery name="q" datasource="test_1">
INSERT INTO test_2.table_z (`id`, `name`) VALUES ('1','test');
</cfquery>
--->
<!--- This is the same query, however I have reused the datasource test_1
and specified the DATABASE test_2 --->
<cfquery name="q" datasource="test_1">
INSERT INTO test_2.table_z (`id`, `name`) VALUES ('1','test');
</cfquery>
</cffunction>
<cftransaction action="begin">
<cfset data = methodA()/>
<cftransaction action="commit"/>
</cftransaction>
So if it is not clear, the solution for me, was to remove the reference to the second datasource test_2 and use test_1 datasource instead. This means hard coding the
second test_2 database name within the query. Obviously this can be done dynamically, however it does cause problems for existing queries as they need to be changed. In addition to this, should the second datasource be a different database platform, like MSSQL, this wont work. Thankfully, this wasn't the case for me.
Had the same issue and just moved my cftransaction tag out of the second (or first) datasource cfquery. This includes CFCs if you are using them throughout the code.
I see that this question is old, but it's still an "issue" on Lucee, and probably Adobe, in 2021. Here's the solution that I devised.
I wanted a way to send myself debug messages to a phone application I've written. They share access to a database.
Here is an adaptation of my solution. My actual code does some other things, so this hasn't directly been tested
public numeric function QueryQueue(required string sql, struct options = {}, struct params = {}, queryset = "default") {
param name="request.queryQueue" default="#{}#";
if (!StructKeyExists(request.queryQueue, arguments.queryset)) {
request.queryQueue[arguments.queryset] = []
}
request.queryQueue[arguments.querySet].append({sql: "#arguments.sql#",
options: arguments.options,
params: arguments.params,
removed: false});
return request.queryQueue[arguments.queryset].len();
// returning the length, and thus the position of the query,
// so it can be specifically called if desired.
// This is query QueryQueueExecute doesn't actually
// delete elements, but marks them.
}
public any function QueryQueueExecute(required numeric pos, boolean remove = true, string queryset = "default") {
if (!request.queryQueue[arguments.queryset][arguments.pos].removed) {
var theQuery = QueryExecute(sql = request.queryQueue[arguments.queryset][arguments.pos].sql,
options = request.queryQueue[arguments.queryset][arguments.pos].options,
params = request.queryQueue[arguments.queryset][arguments.pos].params);
if (arguments.remove) {
request.queryQueue[arguments.queryset][arguments.pos].removed = true;
}
return theQuery;
} else {
return {recordcount: -1}; // a flag to show that the query wasn't executed, because it's already been "removed"
}
}
public array function QueryQueueExecuteAll(boolean remove = true, string queryset = "default") {
var queryArray = [];
for (i = 1; i <= request.queryQueue[arguments.queryset].len(); i++) {
queryArray.append(QueryQueueExecute(i, false));
// false is deliberately set here, rather than passing the remove argument
// since the array will be cleared after the loop.
}
if (arguments.remove) {
request.queryQueue[arguments.queryset].clear();
}
return queryArray;
}
These functions let me queue the queries, and execute specific ones, or execute them all. There's also a flag to remove if desired or not, though I can't imagine why it wouldn't be.
In my case, I can execute run this in OnRequestEnd and in my ErrorHandler, since my use is for debugging.

Cannot get data out of a WCF Data Service

I set up a WCF Data Service http://localhost:65432/YeagerTechWcfService.svc and when I run it, I get the expected output below:
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<service xmlns="http://www.w3.org/2007/app" xmlns:app="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="http://localhost:65432/YeagerTechWcfService.svc/">
<workspace>
<atom:title>Default</atom:title>
<collection href="Categories">
<atom:title>Categories</atom:title>
</collection>
<collection href="Customers">
<atom:title>Customers</atom:title>
</collection>
<collection href="Priorities">
<atom:title>Priorities</atom:title>
</collection>
<collection href="Projects">
<atom:title>Projects</atom:title>
</collection>
<collection href="Status">
<atom:title>Status</atom:title>
</collection>
<collection href="TimeTrackings">
<atom:title>TimeTrackings</atom:title>
</collection>
</workspace>
</service>
However, after executing the below method, I'm getting a js runtime error in the script: httpErrorPagesScripts.js when testing it out via the browser:
var bElement = document.createElement("A");
bElement.innerText = L_GOBACK_TEXT ;
bElement.href = "javascript:history.back();";
goBackContainer.appendChild(bElement);
The method that is executing is below after I put in the following query:
http://localhost:65432/YeagerTechWcfService.svc/Customers
public QueryOperationResponse<Customer> GetCustomers()
{
YeagerTechEntities DbContext = new YeagerTechEntities();
YeagerTechModel.YeagerTechEntities db = new YeagerTechModel.YeagerTechEntities();
DataServiceQuery<Customer> query = (DataServiceQuery<Customer>)
from customer in db.Customers
where customer.CustomerID > 0
select customer;
QueryOperationResponse<Customer> items = (QueryOperationResponse<Customer>)query.Execute();
db.Dispose();
return items;
}
Even if I set a breakpoint in the above method, it doesn't stop there. I just know that after I submit the query on the address bar, it goes into this method, and then pops out and executes that js error. I'm sure that I'm missing something..... Can someone help?
There is only 1 record coming back from the database, so the number of rows fetched is not an issue...
Note that this same type of query is successfully executed against an EF ORM model with a regular WCF Application Service. It's just that when I try to apply the same query using a WCF Data Service, I'm getting the error.

Return related objects in select linq to sql

I have 2 tables Account and Address which has 1:1 relationsip; account has addressid in it. I have created the association in the dbml file. Now I want to write a query to select the account for given accountid and the resulting account should contain the address object in it as well.
using (var context = new SalesLogixDataClassesDataContext())
{
var query = context.ACCOUNTs
.Where(a => a.ACCOUNTID == id)
.Select(a => new Account {AccountId = id, AccountName = a.ACCOUNT1, Address=a.ADDRESS});
return query.FirstOrDefault();
}
But the address is null in the returned object. So what do i have to do in the query so that the address object is also retrieved.
EDIT: Sorry guys for leading you to a wild goose chase. I had the association mapped wrong(Address.AddressId as mapped to Account.AccountId). I fixed that and its working fine now. Thank you all for the help.
Why are you creating a new Account object in the select instead of just letting LINQ->SQL handle it for you? If you let the framework select the object it should properly set the EntityRef for the property and the association should work.
using (var context = new SalesLogixDataClassesDataContext())
{
var query = context.ACCOUNTs
.Where(a => a.ACCOUNTID == id);
return query.FirstOrDefault();
}
Just an Edit, if you are selecting to change the property names from generated names you can also do this through the DBML designer, so the object you get is friendly but still maintains the relationship to the table via the actual column names.
Upon second review of your code (sorry I misread the setting of the Entity) I removed some invalid text from original answer.
Here is some example XML of how you could make your model objects more friendly rather then manually translating them in your queries.
<Table Name="dbo.ACCOUNT" Member="Accounts">
<Type Name="Account">
<Column Name="ACCOUNTID" Member="AccountId" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
<Column Name="ACCOUNT1" Member="AccountName" Type="System.String" DbType="VarChar(...) NOT NULL" CanBeNull="false" />
<Association Name="Address_Account" Member="Address" ThisKey="ThisTablesFKIDToAddress" OtherKey="AddressTablePKID" Type="Address" IsForeignKey="true" />
</Type>
</Table>
You could do this through the designer which would be much easier and also make your address object nicer to work with.
Anyway I hope any of this helps, I can't say for sure what is going on with setting the entity, if you want to post the code for the account object it might help but it will give us somewhere to go from here.
Of course if you were to change your modeled object the select code would look more like this:
using (var context = new SalesLogixDataClassesDataContext())
{
return context.Accounts.FirstOrDefault(account => account.AccountId == id);
}
Take a closer look at the object relation you have configured, specially the keys involved. Regardless of wanting/needing to use custom objects, the query as you provided will get you the related address, as long as the relations are configured correctly and the data is in the db.
Here is a sample real life config, where StateMaster has a CustomerMasters1 property (naming is being cleaned up :)