postgresql custom cast to match different case types - entity-framework-6

Some backgrounds, I am using:
Entity framework 6.1
Devart.Data.PostgreSql 7.11.1229
Devart.Data 5.0.2021
Postgresql 9.2
Because of some abstract OO modeling, EntityFramework is forced to perform some unions over different tables, some of which might contain a JSONB column, others not. Essentially this looks as follows:
select
c1,
c2,
case when
c4 = true then c3
else cast(null as varchar)
end as c3,
c4
from (
select
id as c1,
name as c2,
attributes as c3,
true as c4
from
myjsonbtable
union all
select
id as c1,
name as c2,
null as c3,
false as c4
from
mynormaltable
) as union_all
When you run this query, Postgresql will provide the following error;
ERROR: CASE types character varying and jsonb cannot be matched
this results from the following line of code:
case when c4 = true then c3 else cast(null as varchar) end as c3
which we could fix like this:
case when c4 = true then c3 else cast(null as jsonb) end as c3
Provided we hit EF with a stick. More easy would be if we could tell Postgresql how to cast these types:
something like:
create cast (varchar as jsonb) with function to_jsonb(anyelement) as implicit;
But I am wondering, would a CASE actually be able to perform an implicit CAST from character varying to jsonb to match types?
Or perhaps anybody as another a brillaint idea to either, either from a Postgresql or EntityFramework standpoint.
Edit: see the submitted code example
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DevartExample
{
[Table("BaseClass")]
public class BaseClass
{
[Index]
public int Id { get; set; }
}
[Table("ClassWithJsonb")]
public class ClassWithJsonB : BaseClass
{
[Column(TypeName = "jsonb")]
public string Json { get; set; }
}
[Table("ClassWithoutJsonb")]
public class ClassWithoutJsonB : BaseClass
{
public int MyProperty { get; set; }
}
public class DevartDbProvider : DbContext
{
public IDbSet<BaseClass> BaseClass
{
get;
set;
}
public IDbSet<ClassWithJsonB> ClassWithJsonB
{
get;
set;
}
public IDbSet<ClassWithoutJsonB> ClassWithoutJsonB
{
get;
set;
}
public DevartDbProvider() : base("DevartExample")
{
Database.Log = s => { System.Diagnostics.Debug.WriteLine(s); };
}
}
class Program
{
static void Main(string[] args)
{
DevartDbProvider devartDbProvider = new DevartDbProvider();
devartDbProvider.BaseClass.FirstOrDefault();
}
}
}
Outputs this query:
SELECT
"Limit1"."C1",
"Limit1"."Id",
"Limit1"."C2",
"Limit1"."C3"
FROM ( SELECT
"Extent1"."Id",
CASE WHEN ( NOT (("Project2"."C1" = true) AND ("Project2"."C1" IS NOT NULL))) AND ( NOT (("Project1"."C1" = true) AND ("Project1"."C1" IS NOT NULL))) THEN '0X' WHEN ("Project2"."C1" = true) AND ("Project2"."C1" IS NOT NULL) THEN '0X0X' ELSE '0X1X' END AS "C1",
CASE WHEN ( NOT (("Project2"."C1" = true) AND ("Project2"."C1" IS NOT NULL))) AND ( NOT (("Project1"."C1" = true) AND ("Project1"."C1" IS NOT NULL))) THEN CAST(NULL AS varchar) WHEN ("Project2"."C1" = true) AND ("Project2"."C1" IS NOT NULL) THEN "Project2"."Json" END AS "C2",
CASE WHEN ( NOT (("Project2"."C1" = true) AND ("Project2"."C1" IS NOT NULL))) AND ( NOT (("Project1"."C1" = true) AND ("Project1"."C1" IS NOT NULL))) THEN CAST(NULL AS int) WHEN ("Project2"."C1" = true) AND ("Project2"."C1" IS NOT NULL) THEN CAST(NULL AS int) ELSE "Project1"."MyProperty" END AS "C3"
FROM "BaseClass" AS "Extent1"
LEFT OUTER JOIN (SELECT
"Extent2"."Id",
"Extent2"."MyProperty",
true AS "C1"
FROM "ClassWithoutJsonb" AS "Extent2" ) AS "Project1" ON "Extent1"."Id" = "Project1"."Id"
LEFT OUTER JOIN (SELECT
"Extent3"."Id",
"Extent3"."Json",
true AS "C1"
FROM "ClassWithJsonb" AS "Extent3" ) AS "Project2" ON "Extent1"."Id" = "Project2"."Id"
LIMIT 1
) AS "Limit1"
Thanks in advance.

You are using fluent mappping (not XML mapping), aren't you? Make sure that the column type for the corresponding properties is set to jsonb: .HasColumnType("jsonb");
If this doesn't help, send us a small test project for reproducing the issue.

Related

Neo4j SDN - OGM nodes equality

I have a problem with Spring Data Neo4j and OGM. When I create a node for first time, it`s ok but if I refresh that page I get this error:
Cypher execution failed with code 'Neo.ClientError.Schema.ConstraintValidationFailed': Node(126) already exists with label Country and property name = 'Country-1'.
I searched the web and read so many documents about equals and hashCode but none of them is helping. Here are my classes:
public abstract class Place {
#Id
#GeneratedValue(strategy = UuidStrategy.class)
private String id ;
private String name ;
public String getId(){return id ;}
}
#Getter
#Setter
#NodeEntity(label = "Country")
#CompositeIndex(unique = true , properties = "name")
public class Country extends Place {
private String label = "Country";
public Country() {}
#Override
public int hashCode() {
int result = label == null ? 1 : label.hashCode();
result = 31 * result + this.getName() == null ? 0 : this.getName().hashCode();
result = 31 * result + this.getId() == null ? 0 : this.getId().hashCode();
return result;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Country)) {
return false;
}
Country that = (Country) o ;
return this.getName().equals(that.getName())
&& this.getId().equals(that.getId())
&& this.getLabel().equals(that.getLabel());
}
}
Repository is default. As I know it`s a problem of equality check but how can I fix this?
You created a constraint on your Country entity by defining
#CompositeIndex(unique = true , properties = "name")
and probably you also enabled the auto index manager feature in the Neo4j-OGM SessionFactory configuration.
This is not related to any implementation of hashCode or equals.
This will also explain the behaviour you are facing: First run succeeds but the very same action repeated failed.

Dynamic DBSet lookup and query

I have a partial result view that takes in the name of the table and a value for a particular column to query. I read the DBContext API and found that Set(Type) should return a DBSet that you can do CRUD operations on. I don't know how exactly to query the DBSet without a PK since the user don't know the PK to look up.
May be using Classic ADO would be easier?
EDIT: I figure out how to use DbSet.SQLQuery function but have no clue to store the results. I inspected the element in debugger and the SQLQuery does work as it found all the rows inside the table.
public class SF1DB : DbContext
{
//List of table names that feeds a DropDownList
public DbSet<tablelist> tables { get; set; }
//Data table
public DbSet<dataTable1> dataTable1 { get; set; }
public DbSet<dataTable2> dataTable2 { get; set; }
//...list of other tables
}
public PartialViewResult GetFeatures(String tablelist, String[] countyfp)
{
String type = "MvcApplication1.Models." + tablelist;
Type dbType = Type.GetType(type);
DbSet set = _db.Set(dbType);
String sql = "select * from " + tablelist;
//How do I store the result in a variable?
set.SqlQuery(sql);
return PartialView();
}
I figured it out by creating a List that have the same type as the DbSet that the user selected. Then I use the SQLQuery's GetEnumerator method and iterate thru the result and add to the new list. Finally, pass the list to the partial view.
public PartialViewResult GetFeatures(String tablelist, String[] countyfp)
{
String type = "MvcApplication1.Models." + tablelist;
Type dbType = Type.GetType(type);
DbSet set = _db.Set(dbType);
String sql = "select * from " + tablelist + " where ";
Type listType = typeof(List<>).MakeGenericType(dbType);
IList list = (IList)Activator.CreateInstance(listType);
for (int i = 0; i < countyfp.Length; i++)
{
sql += "cntyidfp like '%" + countyfp[i] + "'";
if (i < (countyfp.Length - 1))
{
sql += " or ";
}
}
IEnumerator result = set.SqlQuery(sql).GetEnumerator();
while (result.MoveNext())
{
list.Add(result.Current);
}
return PartialView(list);
}

retrieve distinct field value

What is the most efficient way to retrieve distinct column values. I have a table with fields FormsID and ProductLineDescription along with other columns. ProductLine table has a one to many relationship, the sample data in the table would be like:
FormID ProdculineDesc
1 abc
2 abc
1 xyz
2 def
3 abc
3 xyz
I want the dropdown to have just the distinct values of ProductLineDesc. Here is the code,
private void LoadProductLines(Models.SearchModel Model, xyzEntities Context)
{
Model.ProductLine = Context.PRODUCTLINEs
.OrderBy(T => T.FormsGuid).ToSelectList().Distinct();
}
This still gives me every ProductLineDesc, how do I retrieve just the Distinct values.
Here's a version with a custom quality comparer (with example):
public class UniqueProductLineDesc : IEqualityComparer<Product>
{
public Boolean Equals(Product a, Product b)
{
if (Object.ReferenceEquals(a, b))
return true;
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(b, null))
return false;
return String.Compare(a.ProductLineDesc, b.ProductLineDesc, true) == 0;
}
public Int32 GetHashCode(Product product)
{
if (Object.ReferenceEquals(product, null))
return 0;
return product.ProductLineDesc == null ? 0 : product.ProductLineDesc.GetHashCode();
}
}
And to implement:
private void LoadProductLines(Models.SearchModel Model, xyzEntities Context)
{
Model.ProductLine = Context.PRODUCTLINEs
.OrderBy(T => T.FormsGuid)
.ToSelectList()
.Distinct(new UniqueProductLineDesc());
}
How about something like:
var distinctInfo = (from c in Context.PRODUCTLINEs
group by c.ProductLineDesc into result
select new Model.ProductLine {
ProductLineDesc = result.Key,
FormID = result.Min(d=>d.FormID)
});

Converting flattened hierarchical data from SQL Server into a structured JSON object with C#/Linq

I am developing an MVC app that retrieves data from a table in SQL Server that is structured like so:
+-----------------------------------+
| Id | Name | Hierarchy | Depth |
|-----------------------------------|
| 01 | Justin | / | 0 |
| 02 | Chris | /1 | 1 |
| 03 | Beth | /1/1 | 2 |
+-----------------------------------+
The example data in the Hierarchy column is the string representation of the hierarchyid datatype, and the Depth column is computed using the hierarchyid::GetLevel() method.
Using Entity Framework 4.1, I have mapped the above table to this class:
public class Node {
public int Id { get; set; }
public string Name { get; set; }
public string HierarchyPath { get; set; } // String representation of the hierarchyid
public int Depth { get; set; }
}
I want to use this information to display a graphical representation of the hierarchy to the user using the JS Visualizations Toolkit, which requires the data to be structured:
var node = {
id: 1,
name: 'Justin'
children: [{
id: 2,
name: 'Chris',
children: [{
id: 3,
name: 'Beth',
children: []
}]
}]
}
I'm having trouble developing the logic to convert a list of my models into a structured JSON object. Any suggestions?
EDIT: I don't have time to fix the answer below right now, but given the extra information in the question, I suspect you want to keep a Dictionary<int, HierarchicalNode> rather than a List<HierarchicalNode> so that you're not relying on any ordering...
I would forget about the JSON representation to start with, and concentrate on building an in-memory POCO representation of the hierarchy. To do that, I'd use something like this:
class HierarchicalNode
{
private readonly List<HierarchicalNode> children =
new List<HierarchicalNode>();
public List<HierarchicalNode> Children { get { return children; } }
private readonly string name;
public string Name { get { return name; } }
private readonly int id;
public int Id { get { return id; } }
public HierarchicalNode(string name, int id)
{
this.name = name;
this.id = id;
}
}
Then build up the tree like this:
// Make sure we get everything in a sensible order, parents before children
var query = context.Nodes.OrderBy(x => x.Depth);
var root = new HierarchicalNode("Root", 0);
foreach (var node in query)
{
var current = root;
foreach (string part = node.HierarchyPath.Split(new[] {'/'},
StringSplitOptions.RemoveEmptyEntries))
{
int parsedPart = int.Parse(part);
current = current.Children[parsedPart - 1];
}
current.Children.Add(new HierarchicalNode(node.Name, node.Id));
}
I know this question is old but I found myself with this problem and did not find the exact solution anywhere. I was able to build on #Jon's code and create the JSON like the OP posted by converting into POCO first. Posting here in case it helps someone.
The pre-requisite of this solution is that you know the maximum number of levels/depth your data has then you could do something like this:
var nodesList = context.Nodes.OrderBy(x => x.Depth).ToList();
var hierarchalData = new HierarchicalNode();
foreach (var node in nodesList)
{
if (node.Depth == 1)
{
// create the parent node
hierarchalData = new HierarchicalNode(node.Name, node.Id);
}
else
{
// create the child node object
var childNode = new HierarchicalNode(node.Name, node.Id);
// split the hierarchy into an array to get parent indexes
var splitHierarchy = node.HierarchyPath.Split("/").Skip(1).SkipLast(1).ToArray();
switch (node.Depth)
{
case 2:
hierarchalData.Children.Add(childNode);
break;
case 3:
var lastParentIndex = Convert.ToInt32(splitHierarchy[splitHierarchy.Length - 2]) - 1;
hierarchalData.Children[lastParentIndex].Children.Add(childNode);
break;
case 4:
var firstParentIndex = Convert.ToInt32(splitHierarchy[splitHierarchy.Length - 3]) - 1;
var lastParentIndex1 = Convert.ToInt32(splitHierarchy[splitHierarchy.Length - 2]) - 1;
hierarchalData.Children[firstParentIndex].Children[lastParentIndex1].Children.Add(childNode);
break;
default:
break;
}
}
}
I know this approach is probably brute force but did the job for me.
Number of levels in my case was 7.

LINQ Distinct()

I am working on getting the results of this sql query in LINQ
SELECT DISTINCT(Type)
FROM Product
WHERE categoryID = #catID
this is my repository query:
public IQueryable<ProdInfo> GetProdInfo()
{
var data = from u in db.Prod
select new ProdInfo
{
PID = u.PID,
CatID = u.CatID,
LastChanged = u.LastChanged,
ChangedBy = u.ChangedBy,
Type = u.Type,
};
return data;
}
filter:
public static IQueryable<ProdInfo> GetDistinctProdType(this IQueryable<ProdInfo> qry,int CatID)
{
return from p in qry
where p.CatID.Equals(CatID)
select p;
}
I need the filter to return the distinct prod type? How can i do this?
Simply like this:
public static IQueryable<ProdType> GetDistinctProdType(
this IQueryable<ProdInfo> query,
int categoryId)
{
return (from p in query
where p.CatID == categoryId
select p.Type).Distinct();
}
Note that I've changed the return type - it should match whatever the type of ProdInfo.Type is.
You may find it more readable to use the extension methods for the whole query if the query expression itself is reasonably simple:
public static IQueryable<ProdType> GetDistinctProdType(
this IQueryable<ProdInfo> query,
int categoryId)
{
return query.Where(p => p.CatID == categoryId)
.Select(p => p.Type)
.Distinct();
}
return (from p in qry
where p.CatId.Equals(CatID)
select p.Type).Distinct();
This matches what your provided SQL Query should be doing.

Resources