npgsql to PostGIS adds ::text, making st_intersects fail - c#-4.0

I've seen numerous old posts about this but no clear solution.
We use PostGreSQL 9.3 with PostGIS 2; NHibernate 3.2.0-GA with Npgsql 2.1.2.
We have an ASP.NET website witch uses MySQL Spatial and we are now in the progress of switching to PostGIS.
My query that fails is send to NHibernate using this code:
string hql = string.Format("select item from {0} item
where NHSP.Intersects(item.Polygon,:boundary)
and item.Layer = :layer", typeof(Data.Item).Name);
IQuery query = CurrentSession.CreateQuery(hql);
query.SetParameter("boundary", boundary, GeometryType);
query.SetParameter("layer", layer);
return query.List<Data.Item>();
This should generate a query like this:
select * from fields
where layer = 'tst'
and st_intersects(polygon,
'0103000020000000000100000005000000F[..]4A40');
But it generates a query like this:
select * from fields
where layer = 'tst'
and st_intersects(polygon,
'0103000020000000000100000005000000F[..]4A40'::text);
Notice the ::text at the end. This results in the following exception:
Npgsql.NpgsqlException: ERROR: 42725: function st_intersects(geometry, text) is not unique
The reason is because the second argument is send as text to PostGIS instead of a geometry.
I've change some code in the NH Spatial library, as suggested elsewhere:
I added these lines to GeometryTypeBase.cs (NHibernate.Spatial)
protected GeometryTypeBase(NullableType nullableType, SqlType sqlTypeOverwrite)
: this(nullableType)
{
this.sqlType = sqlTypeOverwrite;
}
And changed
public PostGisGeometryType()
: base(NHibernateUtil.StringClob)
{
}
into
public PostGisGeometryType()
: base(NHibernateUtil.StringClob, new NHibernate.SqlTypes.SqlType(System.Data.DbType.Object))
{
}
in PostGisGeometryType.cs (PostGIS driver)
When I run my application I now get a cast exception on
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
this.nullableType.NullSafeSet(cmd, this.FromGeometry(value), index);
}
also in GeometryTypeBase.cs (NHibernate.Spatial):
System.InvalidCastException: Can't cast System.String into any valid DbType.
Any suggestion how to fix this is much appreciated.

I've kept on searching and altering my search string I've found the answer in https://github.com/npgsql/Npgsql/issues/201
In NpgsqlTypes.NpgsqlTypesHelper.cs
nativeTypeMapping.AddType("text_nonbinary", NpgsqlDbType.Text, DbType.Object, true);
nativeTypeMapping.AddDbTypeAlias("text_nonbinary", DbType.Object);
needs to be changed to
nativeTypeMapping.AddType("unknown", NpgsqlDbType.Text, DbType.Object, true);
nativeTypeMapping.AddDbTypeAlias("unknown", DbType.Object);
And also my earlier fix to PostGisGeometryType needs to be done.
Now I finally can get my geometry data from PostGIS.

Related

Linq queries and optionSet labels, missing formatted value

I'm doin a simple query linq to retrieve a label from an optionSet. Looks like the formatted value for the option set is missing. Someone knows why is not getting generated?
Best Regards
Sorry for the unclear post. I discovered the problem, and the reason of the missing key as formattedvalue.
The issue is with the way you retrieve the property. With this query:
var invoiceDetails = from d in xrmService.InvoiceSet
where d.InvoiceId.Value.Equals(invId)
select new
{
name = d.Name,
paymenttermscode = d.PaymentTermsCode
}
I was retrieving the correct int value for the option set, but what i needed was only the text. I changed the query this way:
var invoiceDetails = from d in xrmService.InvoiceSet
where d.InvoiceId.Value.Equals(invId)
select new
{
name = d.Name,
paymenttermscode = d.FormattedValues["paymenttermscode"]
}
In this case I had an error stating that the key was not present. After many attempts, i tried to pass both the key value and the option set text, and that attempt worked just fine.
var invoiceDetails = from d in xrmService.InvoiceSet
where d.InvoiceId.Value.Equals(invId)
select new
{
name = d.Name,
paymenttermscode = d.PaymentTermsCode,
paymenttermscodeValue = d.FormattedValues["paymenttermscode"]
}
My guess is that to retrieve the correct text associated to that option set, in that specific entity, you need to retrieve the int value too.
I hope this will be helpful.
Best Regards
You're question is rather confusing for a couple reasons. I'm going to assume that what you mean when you say you're trying to "retrieve a label from an OptionSet" is that you're attempting to get the Text Value of a particular OptionSetValue and you're not querying the OptionSetMetadata directly to retrieve the actual LocalizedLabels text value. I'm also assuming "formatted value for the option set is missing" is referring to the FormattedValues collection. If these assumptions are correct, I refer you to this: CRM 2011 - Retrieving FormattedValues from joined entity
The option set metadata has to be queried.
Here is an extension method that I wrote:
public static class OrganizationServiceHelper
{
public static string GetOptionSetLabel(this IOrganizationService service, string optionSetName, int optionSetValue)
{
RetrieveOptionSetRequest retrieve = new RetrieveOptionSetRequest
{
Name = optionSetName
};
try
{
RetrieveOptionSetResponse response = (RetrieveOptionSetResponse)service.Execute(retrieve);
OptionSetMetadata metaData = (OptionSetMetadata)response.OptionSetMetadata;
return metaData.Options
.Where(o => o.Value == optionSetValue)
.Select(o => o.Label.UserLocalizedLabel.Label)
.FirstOrDefault();
}
catch { }
return null;
}
}
RetrieveOptionSetRequest and RetrieveOptionSetResponse are on Microsoft.Xrm.Sdk.Messages.
Call it like this:
string label = service.GetOptionSetLabel("wim_continent", 102730000);
If you are going to be querying the same option set multiple times, I recommend that you write a method that returns the OptionSetMetadata instead of the label; then query the OptionSetMetadata locally. Calling the above extension method multiple times will result in the same query being executed over and over.

Spring Data JPA Pagination (Pageable) with Dynamic Queries

I have a simple query as follows "select * from USERS". I also use Pageable to enable pagination.
This query may have optional predicates based on the given parameters being null or not.
For example if "code" parameter is given and not null, then the query becomes
"select * from USERS where code = :code";
As far as I know I cannot implement this using #Query annotation. I can implement a custom repository and use EntityManager to create a dynamic query.
However, I am not sure how I can integrate "Pageable" with that to get back paginated results.
How can I achieve this?
This is very easy to do in Spring Data using QueryDSL (as alternative to the criteria API). It is supported out of the box with the following method of QueryDSLPredicateExecutor where you can just pass null as the Predicate if no restrictions are to be applied:
Page<T> findAll(com.mysema.query.types.Predicate predicate,
Pageable pageable)
Using QueryDSL may not be an option for you however if you look at the following series of tutorials you might get some ideas.
http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-nine-conclusions/
The scenario you have is actually discussed by the author in the comments to part 9 of his guide.
Getting page results for querydsl queries is somehow complicated since you need two queries: one for the total number of entries, and one for the list of entries you need in the page.
You could use the following superclass:
public class QueryDslSupport<E, Q extends EntityPathBase<E>> extends QueryDslRepositorySupport {
public QueryDslSupport(Class<E> clazz) {
super(clazz);
}
protected Page<E> readPage(JPAQuery query, Q qEntity, Pageable pageable) {
if (pageable == null) {
return readPage(query, qEntity, new QPageRequest(0, Integer.MAX_VALUE));
}
long total = query.clone(super.getEntityManager()).count(); // need to clone to have a second query, otherwise all items would be in the list
JPQLQuery pagedQuery = getQuerydsl().applyPagination(pageable, query);
List<E> content = total > pageable.getOffset() ? pagedQuery.list(qEntity) : Collections.<E> emptyList();
return new PageImpl<>(content, pageable, total);
}
}
You have to use querydsl and build your where depending on not null parameter for example
BooleanBuilder where = new BooleanBuilder();
...
if(code != null){
where.and(YOURENTITY.code.eq(code));
}
and after execute the query
JPAQuery query = new JPAQuery(entityManager).from(..)
.leftJoin( .. )
...
.where(where)
and use your own page
MaPage<YOURENTITY> page = new MaPage<YOURENTITY>();
page.number = pageNumber+1;
page.content = query.offset(pageNumber*pageSize).limit(pageSize).list(...);
page.totalResult = query.count();
I create MyPage like that
public class MaPage<T> {
public List<T> content;
public int number;
public Long totalResult;
public Long totalPages;
...
}
it works but if in your query you got a fetch then you gonna have this warning
nov. 21, 2014 6:48:54 AM org.hibernate.hql.internal.ast.QueryTranslatorImpl list
WARN: HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
and it will slow down your request So the solution is to get ride of the fetch and define a #BatchSize(size=10) and use Hibernate.initialize(....) to fetch data in collections and other object type.
Display data from related entities to avoid the lazy initialization exception with setting up #BatchSize
How to execute a JPAQuery with pagination using Spring Data and QueryDSL
The information here is obsolete. Have your Repository implement the QueryDslPredicateExecutor and paging comes for free.

How to get real T-SQL generated by Entity Framework with ToTraceString() not working

//get full list of active employees
public static object EmployeeList()
{
string traceFile = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var empList = (from emp in dbViews.EmployeeList
where emp.StatusID == 7
orderby emp.EmpNo
select new
{
emp.id,
emp.Name,
emp.EmpNo,
emp.Telephone,
emp.EmployeeType,
emp.DepartmentName,
emp.Supervisor,
emp.ImmediateSupervisor,
emp.StatusID
});
File.AppendAllText(traceFile, ((ObjectQuery)empList).ToTraceString());
return empList.ToList();
}
When executing the above code, am getting the error below and am not sure why
*
Unable to cast object of type
'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType09[System.Int32,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Nullable`1[System.Int32]]]'
to type 'System.Data.Objects.ObjectQuery'.
*
I just want to get the real T-SQL generated by the above Entity Framework code.
The error is at line below
File.AppendAllText(traceFile, ((ObjectQuery)empList).ToTraceString());
You are using DbContext API so you can use just empList.ToString().

Two DataReader from different classes but I get an error?

I have a project class, and related to it I have a categories class and responsible class. When I want to add a project object to database, I call a method from the categories class to get the categories id, and do the same for the responsible class. I mean:
int categoryId = getCategoryId("Beverages");
int responsibleId = getResponsibleId("An Example Name");
These two methods are in different classes, but use similar code. When I run the program I get the error:
"There is already an open DataReader associated with this Command which must be closed first."
You must either fix your code, so that only one active (open) DataReader-object its active on a single DbConnection or enable MARS in your connection string (MultipleActiveResultSets=True).
I do agree with Mithrandir here, but there is another workaround for your question; I suppose methods you listed access to the database (silly, I know , but bare it with me ), and they all open connection to same database. What you should add in your code is an final block,which closes database connection, after transaction is executed:
try
{
string sql="select * from ...."
connection.Open();
comand.CommandText = sql;
comand.ExecuteNonQuery();
}
catch(SQLException)
{
}
finally
{
if (connection!=null)
{
connection.Close();
}
}

Why no SQL for NHibernate 3 Query?

Why is no SQL being generated when I run my Nhibernate 3 query?
public IQueryable<Chapter> FindAllChapters()
{
using (ISession session = NHibernateHelper.OpenSession())
{
var chapters = session.QueryOver<Chapter>().List();
return chapters.AsQueryable();
}
}
If I run the query below I can see that the SQL that gets created.
public IQueryable<Chapter> FindAllChapters()
{
using (ISession session = NHibernateHelper.OpenSession())
{
var resultDTOs = session.CreateSQLQuery("SELECT Title FROM Chapter")
.AddScalar("Title", NHibernateUtil.String)
.List();
// Convert resultDTOs into IQueryable<Chapter>
}
}
Linq to NHibernate (like Linq to entities) uses delayed execution. You are returning IQueryable<Chapter> which means that you might add further filtering before using the data, so no query is executed.
If you called .ToList() or .List() (i forget which is in the API), then it would actually produce data and execute the query.
In other words, right now you have an unexecuted query.
Added: Also use Query() not QueryOver(). QueryOver is like detached criteria.
For more info, google "delayed execution linq" for articles like this

Resources