How to use composite types as parameters with Npgsql and Telerik OpenAccess - entity-framework

I have a PostgreSQL 9.1 stored procedure:
FUNCTION g_savedx(_cpatient text, o view_dx, n view_dx) RETURNS void
where view_dx is a table. If I understand the PostgreSQL manual correctly, when used as a parameter to a stored procedure, only the TYPE is used. Hence, the table will be seen here as if I used CREATE TYPE.
Now Telerik EntityFramework compiles the following definition in the Domain Model:
public int SaveDx(string cpatient, Object o, Object n)
{
OAParameter parameterCpatient = new OAParameter();
parameterCpatient.ParameterName = "_cpatient";
parameterCpatient.Size = -1;
if(cpatient != null)
{
parameterCpatient.Value = cpatient;
}
else
{
parameterCpatient.DbType = DbType.String;
parameterCpatient.Value = DBNull.Value;
}
OAParameter parameterO = new OAParameter();
parameterO.ParameterName = "o";
parameterO.Value = o;
OAParameter parameterN = new OAParameter();
parameterN.ParameterName = "n";
parameterN.Value = n;
int queryResult = this.ExecuteNonQuery("SELECT * FROM \"public\".\"g_savedx\"(_cpatient, o, n)", CommandType.Text, parameterCpatient, parameterO, parameterN);
return queryResult;
}
Upon compiling, Telerik complains with:
The specified clr type 'Object' is not valid for the parameter n of the procedure 'g_savedx'.
Attempts at "Fixing" the error by telling Telerik to use CLR Type System.Object consistently fail.
This ultimately is leading me to suspect that Telerik is using Npgsql as the interface to the PostgreSQL database and that Telerik is looking for a DBType of View_dx as reported by the PostgreSQL database. This type, ofcourse, does not exist in either the NpgsqlDbType enumerations nor System.Data DbType enumeration.
Somebody please tell me I'm wrong! Or, how to fix this, as I'd really like to use the PostgreSQL stored procedure with a composite type as a parameter.
Any help is most appreciated.

Related

Porting Oracle associative arrays to Postgres and wrapping the data access using npgsql

It is great to get prompt replies on the npgsql queries. Thanks to its owner! I am trying to port a sproc that took in array valued parameters to postgres with similar abstraction/semantics. I am wondering how one would shape the pgsql sproc and use npgsql api to call that. I have another layer of application abstraction on top of our dal abstraction. We use data adapters to go to sql server, assoc arrays to oracle and trying to figure out what we could map this to for postgres using npgsql. We have some room in shaping the sproc but still keep the number of input params the same. we could certainly build this sproc much different but we still need it behind the same app api which supplies some set of typed arrays as shown below
public static void Flush2OraWithAssocArrayInsnetworkdatabatch(string dbKey ,int?[] ENDPOINTID,DateTime?[] INSERTEDDATETIME,int?[] RECORDTYPEID,long?[] RECORDVALUE,int?[] PACKETSIZE)
{
Database db = Helper.GetDatabase(dbKey);
using (DbConnection con = db.CreateConnection()){
con.Open();
using (DbCommand cmd = con.CreateCommand()){
cmd.CommandText = "Insnetworkdatabatch";
Helper.InitializeCommand(cmd, 300, "Insnetworkdatabatch");
BuildInsnetworkdatabatchOracleAssocArrayCommandParameters(cmd ,ENDPOINTID,INSERTEDDATETIME,RECORDTYPEID,RECORDVALUE,PACKETSIZE);
try {
Helper.ExecuteNonQuery(cmd, cmd.CommandText);
con.Close();
} catch (DALException ) {
throw;
}
}
}
}
I have a oracle sproc written as follows
create or replace PROCEDURE InsNetworkDataBatch2
(
-- Add the parameters for the stored procedure here
v_endPointID IN arrays.t_number ,
v_insertedDateTime IN arrays.t_date ,
v_recordTypeID IN arrays.t_number ,
v_recordValue IN arrays.t_number ,
v_packetSize IN arrays.t_number )
AS
BEGIN
DECLARE
BEGIN
FORALL i IN v_endpointID.FIRST..v_endpointID.LAST SAVE EXCEPTIONS
INSERT
INTO STGNETWORKSTATS
(
INSERTEDDATE,
ENDPOINTID,
RECORDTYPEID,
RECORDVALUE,
PACKETSIZE
)
VALUES
(
v_insertedDateTime(i),
v_endPointID(i),
v_recordTypeID(i),
v_recordValue(i),
v_packetSize(i)
);
END;
END;
-- END PL/SQL BLOCK (do not remove this line) ----------------------------------
Here is the assoc array package in oracle
create or replace PACKAGE Arrays AS
type t_number is table of number index by binary_integer;
type t_date is table of date index by binary_integer;
END Arrays;
Here is how we build the oracle parm and wondering what its equivalency if at all possible in postgres and trying to see how npgsql will support it
public override void CreateAssociativeArrayParameter(DbCommand cmd, string parameterName, object parameterValue, string dbType, ParameterDirection direction)
{
OracleDbType oracleDbType = dbSpecificTypesMap[dbType];
OracleParameter param = new OracleParameter(parameterName, oracleDbType, direction);
param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
param.Value = parameterValue;
cmd.Parameters.Add(param);
}
I don't know anything about Oracle arrays or associative arrays. However, PostgreSQL has a rich support for complex types. PostgreSQL arrays are a good way to store an array of values in a column, and PostgreSQL even provides indexing and database-side functions to work with arrays.
If you're looking for a dictionary type (associative array?), take a look at hstore or json.
EDITED: If your associative array has a fixed schema (i.e. the fields don't change), you can also consider PostgreSQL composite.
Here is an attempt with Postgres stored procedure. This is now working. I got around some casting issues thrown from inside the npgsql which was a result of my .net type not being compatible with the sproc parameter data type in postgres.
Here is how i am trying to add the param value
create or replace FUNCTION InsNetworkDataBatch
(
-- Add the parameters for the stored procedure here
v_endPointID IN int[] ,
v_insertedDateTime IN timestamp[] ,
v_recordTypeID IN int[] ,
v_recordValue IN bigint[] ,
v_packetSize IN int[] ) RETURNS void
LANGUAGE 'plpgsql'
AS $$
BEGIN
DECLARE
BEGIN
FOR i IN array_lower(v_endPointID, 1) .. array_upper(v_endPointID, 1)
loop
INSERT INTO STGNETWORKSTATS
(
INSERTEDDATE,
ENDPOINTID,
RECORDTYPEID,
RECORDVALUE,
PACKETSIZE
)
VALUES
(
v_insertedDateTime[i],
v_endPointID[i],
v_recordTypeID[i],
v_recordValue[i],
v_packetSize[i]
);
end loop;
END;
END;
$$
Here is how i am trying to bind the app to the command params
public override void CreateAssociativeArrayParameter(DbCommand cmd, string parameterName, object parameterValue, string dbType, ParameterDirection direction)
{
NpgsqlDbType npgsqlDbType;
if (dbSpecificTypesMap.ContainsKey(dbType))
{
npgsqlDbType = dbSpecificTypesMap[dbType];
}
else
{
throw new ApplicationException($"The db type {dbType} could not be parsed into the target NpgsqlDbType. Please check the underlying type of the parameter");
}
NpgsqlParameter param = new NpgsqlParameter(parameterName.ToLower(), NpgsqlDbType.Array | npgsqlDbType);
param.Value = parameterValue;
cmd.Parameters.Add(param);
}

How to file a bug against Salesforce Apex?

Apex Workbench reports compile error on the code
double d = null;
system.debug(d instanceof double);
COMPILE ERROR: Operation instanceof is always true since an instance of Double is always an instance of Double
This is clearly wrong, because null is not an instance of double:
object d = null;
system.debug(d instanceof double);
19:32:24.3 (4343472)|USER_DEBUG|[2]|DEBUG|false
Where can I file a bug report?
When we use Primitive data type we always know what type we declared, either we create a variable or we pass to any function (in both scenario).
So salesforce is not allowing you to check for what you have manually declared, You can only check when data type is assigned dynamically, Below example will clear the thought :
decimal doubleVariable = 0;
Object obj = doubleVariable;
system.debug(obj instanceof decimal);
String stringVariable = 'teststring';
obj = stringVariable;
system.debug(obj instanceof decimal);
system.debug(obj instanceof string);
Output :
true
false
true

Conditionally add query operator on properties defined in non-EDM base type, if inheriting

(C# code at end of question)
I have the following inheritance chain:
PreRecord <- Record <- (multiple entity types)
Record declares a property ID As Integer.
PreRecord and Record are not EDM types, and do not correspond to tables in the database.
I have a method that takes a generic parameter constrained to PreRecord and builds an EF query with the generic parameter as the element type. At runtime, in the event that T inherits not just from PreRecord but from Record, I would like add an OrderBy operator on ID:
'Sample 1
Function GetQuery(Of T As PreRecord)(row As T) As IQueryable(Of T)
Dim dcx = New MyDbContext
Dim qry = dcx.Set(Of T).AsQueryable
If TypeOf row Is RecordBase Then
'modify/rewrite the query here
End If
Return qry
End Function
If the parameter constraint were to Record I would have no problem applying query operators that use the ID property. How can I make use of a different (narrowing) generic constraint mid-method and still return an IQueryable(Of T) / IQueryable<T>, where T is still constrained to PreRecord?
I tried this:
'Sample 2
qry = dcx.Set(Of T).Cast(Of Record).OrderBy(Function(x) x.ID).Cast(Of PreRecord)()
which doesn't work:
LINQ to Entities only supports casting EDM primitive or enumeration types.
C# equivalent:
//Sample 1
public IQueryable<T> GetQuery<T>(T row) where T : PreRecord {
var dcx = new MyDbContext();
var qry = dcx.Set<T>.AsQueryable();
if (row is RecordBase) {
//modify/rewrite the query here
}
return qry;
}
and this doesn't work:
//Sample 2
qry = dcx.Set<T>.Cast<Record>.OrderBy(x => x.ID).Cast<PreRecord>()
The problem here is the fact that compiler checks queries already at compile time and PreRecord class does not have ID property. We cannot use simply Cast, because when it is used in definition of the query parser tries to convert it to sql - but there is no such thing that exists in sql. Sql supports only conversion of one column type to another - so on the .NET side it is supported only for primitive and enum types. To overcome compiler query checking we may use Expression class to build dynamic queries:
ParameterExpression e = Expression.Parameter(typeof(Record));
Expression body = Expression.Property(e, "ID");
Expression<Func<PreRecord, int>> orderByExpression = Expression.Lambda<Func<PreRecord, int>>(body, e);
And use your expression in the query:
qry = dcx.Set<T>.OrderBy(orderByExpression);
This way your linq query will not be validated during compile time but execution time. Here I assumed ID is of type int, if the type is different change it accordingly.

EF4.1 Code First: Stored Procedure with output parameter

I use Entity Framework 4.1 Code First. I want to call a stored procedure that has an output parameter and retrieve the value of that output parameter in addition to the strongly typed result set. Its a search function with a signature like this
public IEnumerable<MyType> Search(int maxRows, out int totalRows, string searchTerm) { ... }
I found lots of hints to "Function Imports" but that is not compatible with Code First.
I can call stored procedures using Database.SqlQuery(...) but that does not work with output parameters.
Can I solve that problem using EF4.1 Code First at all?
SqlQuery works with output parameters but you must correctly define SQL query and setup SqlParameters. Try something like:
var outParam = new SqlParameter();
outParam.ParameterName = "TotalRows";
outParam.SqlDbType = SqlDbType.Int;
outParam.ParameterDirection = ParameterDirection.Output;
var data = dbContext.Database.SqlQuery<MyType>("sp_search #SearchTerm, #MaxRows, #TotalRows OUT",
new SqlParameter("SearchTerm", searchTerm),
new SqlParameter("MaxRows", maxRows),
outParam);
var result = data.ToList();
totalRows = (int)outParam.Value;

Using a flag in an Entity Framework Where clause

I have a class (built by EF from my database) that has a field that is a flag. The field is stored in the database as an int in a column named CategoryEnum. I have an enum that specifies the permissible values of the flag:
[Flags]
public enum RuleCategories
{
None = 0x0000,
ApplicantBased = 0x0001,
LocationBased = 0x0002,
PolicyBased = 0x0004,
PropertyBased = 0x0008
}
When I try to retrieve the objects using LINQ to Entities
var allRules = from r in context.Rules
where ((r.CategoryEnum & (int)categories) != 0)
select r;
I get this error:
Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
or, if I try to cast the entity value to the enum
var allRules = from r in context.Rules
where (((RuleCategories)r.CategoryEnum & categories) != 0)
select r;
I get a different error:
Unable to cast the type 'System.Int32' to type RuleCategories'. LINQ to Entities only supports casting Entity Data Model primitive types.
How do I select entities based on a flag?
Thanks
I am going to guess and say that you are using the good old EF 3.5. This works without problems with EF 4.0 in VS2010. There is a problem with the 3.5 version, however, and you will have to use a workaround. Cast the categories variable to int before the query, and then use your int variable inside the query itself:
int preCastCategories = (int)categories;
var allRules = from r in context.Rules
where ((r.CategoryEnum & preCastCategories) != 0)
select r;