In EF Core how can I efficiently check if text column of Json array contains any number from passed integer array using LINQ?
Example of table, where Id is integer type and Name and TypeJson are text
| Id | Name | TypeJson |
| --- | ---------- | -------- |
| 1 | Name One | [1,2] |
| 2 | Name Two | [2,3] |
| 3 | Name Three | [4,7] |
In Postgresql I would have written something like this
SELECT *
FROM "Table"
WHERE translate("TypeJson", '[]','{}')::int[] && ARRAY[1, 7]
where the select would return 1 and 3 rows.
I'd like to achieve same result by using LINQ functions.
I tried using EF.Functions but didn't achieve much. My attempt
await _dbContect.Table
.Where(x => !string.IsNullOrEmpty(x.TypeJson ) &&
EF.Functions.JsonContains(x.TypeJson , "[1]")
.ToListAsync();
But it produces error as column is type of text and not Json
System.InvalidOperationException: The EF JSON methods require a JSON parameter and none was found.
The entity:
public class Table
{
public int Id { get; set; }
public string Name { get; set; }
public string TypeJson { get; set; }
}
Using FromSqlRaw() is not possible because there is already written code and would be preferable if I didn't have to rewrite whole code block.
As I found out my code had three main problems
await _dbContect.Table
.Where(x => !string.IsNullOrEmpty(x.TypeJson) &&
EF.Functions.JsonContains(x.TypeJson , "[1]")
.ToListAsync();
First of all I was using EF.Functions.JsonContains() function on
text column which is not valid, json functions are deliberately
written for jsonb type.
After altering column type to jsonb, the
second problem was using string function to check if jsonb column
was null or empty which doesn't make sense and produces exception. Link to github issue
The third problem was the parameter I tried to filter with "[1]", integer needs to be passed as a JsonElement JsonSerializer.SerializeToElement(value); Link to github issue by ahanusa
Credits to #GuruStron for directing me to correct direction
I have a list of 'quests' with a quest that is 'granted' when one 'ends'. The quests are not stored in sequential order so I'm attempting to find two things.
class quests
{
public string questGrantID { get; set; }
public string questEndID { get; set; }
}
Example
+--------------+--------------+
| grants | ends |
+--------------+--------------+
| quest234 | quest567 |
| quest987 | quest234 |
| quest654 | quest987 |
+--------------+--------------+
Parent Quests: In this example I would know the 'Parent' quest is quest567 because the parent is found under questEndID but not under questGrantID.
Children Quests: In this example, after quest567 is completed, the user is granted quest234. When quest quest234 ends, quest987 is granted and so on.
I don't know where to start, i've looked up example of joining two separate list, not the same. I assume this will be two different functions, one to find the parents and the other finding the children. From there i will create a new list.
Example
+--------------+--------------+
| order | quest |
+--------------+--------------+
| 1 | quest567 |
| 2 | quest234 |
| 3 | quest987 |
| 4 | quest654 |
+--------------+--------------+
Finding the parent quests is easy, simply get a list of IDs not in any Grant.
Using a convenient extension method:
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
You can gather all the GrantIDs and find the EndIDs that aren't there:
var grants = questsList.Select(q => q.questGrantID).ToHashSet();
var parents = questsList.Select(q => q.questEndID).Where(q => !grants.Contains(q));
Now since your chain is recursive, you need a loop to follow the chain. Write another extension method to produce the quest chain from a starting point:
public static IEnumerable<string> questChain(this List<quests> allQuests, string curQuestID) {
yield return curQuestID;
var nextQuest = allQuests.ToDictionary(q => q.questEndID, q => q.questGrantID);
for (; nextQuest.TryGetValue(curQuestID, out var nextQuestID); curQuestID = nextQuestID)
yield return nextQuestID;
}
Now you can call that on all the parents to get all the chains:
var questChains = parents.Select(pq => questsList.questChain(pq));
Now if you really want the order,quest pair, you can number each chain:
var orderedQuests = questChains.Select(qc => qc.Select((quest, i) => new { order = i+1, quest } ));
If you want the numbering to be continuous across the chains, you will need to track it outside the query:
var curOrder = 1;
var orderedQuestsContinuous = questChains.Select(qc => qc.Select(quest => new { order = curOrder++, quest }));
I've been trying to parse an excel file using the LinqToExcel library. My excel file has the following "design":
Property1 | Property2 | HasExtraProperty1 | HasExtraProperty2 | HasExtraProperty3
------------|---------------|-----------------------|-----------------------|-------------------
foo | bar | yes | yes | no
barfoo | foobar | no | no | yes
barbar | foofoo | no | yes | no
An abstraction of my model looks like this:
class MyModel
{
List<ExtraProperties> extraProperties;
String property1;
String property2;
}
I used the mappings from the library to easily map the (in this example) String properties to the columns. This all works fluently, but now I'm stuck parsing the other properties. Only the the extra properties with a "yes" should be added to the list. Any ideas on how to solve this with a linq query?
Note 1: For future-proofness, the number of extra properties should be able to vary.
Note 2: I've considered using another library, but I'm already using LinqToExcel somewhere in my project, and I'm trying to keep the dependencies at a minimum.
Seems like the easiest solution was to abandon the convenient mapping provided by LinqToExcel, and just looping through every columnname/row.
IExcelQueryFactory fact = new ExcelQueryFactory(path);
var query = from r in fact.Worksheet(0)
select r;
IList<MyModel> models = new List<MyModel>();
foreach(var row in query){
MyModel m = new MyModel();
foreach(String colName in MyColMapping.Keys){
p.GetType().GetProperty(colName).SetValue(p, row[ColMapping[colName]]);
}
foreach(ExtraProperty p in PMapping.Keys){
if(row[PMapping[p]].Equals("yes"))
m.ExtraProperties.Add(p);
}
models.add(m);
}
Note: ColMapping is a dictionary which maps names of excel-columns with model-properties. PMapping is a dictionary which maps excel-columns with the right object of the extra properties.
I would like to know is if I have an excel file and I am trying to get data via linq query will this be posible?
Excel file format
+-----------------+-------------+
| Inputlocation | Inputvalue |
+-----------------+-------------+
| 1 | Hello |
+-----------------+-------------+
| 2 | World!!! |
+-----------------+-------------+
Now If I am using Linq query given below is it possible to get Inputvalue data
var fileName = #"C:\Users\jshah\Documents\Visual Studio 2013\Projects\QA_ExpScript\QA_ExpScript\Excel\Inputdata.xls";
string sheetName = "Input";
var book = new LinqToExcel.ExcelQueryFactory(fileName);
var users = from x in book.Worksheet(Input) select x;
foreach (var x in users)
{
Console.WriteLine(x["1"]);
}
I am trying to do here is where inputlocation is "1" give me Inputvalue which is "Hello". Am I on a correct way to specify the query?. Also I am using this again and again later in my code. So please give better solution for this.
You can use where clause to filter the data like this:-
var users = from x in book.Worksheet()
where x["Inputlocation"].Cast<int>() == 1
select x["Inputvalue"].Cast<string>();
Then you can simply iterate through users:-
foreach (var item in users)
{
Console.WriteLine(item.Inputvalue); //This will print "Hello"
}
Although IMHO its always better to create a matching type to avoid any exceptions that can occur by typos when specifying the column names.
public class User
{
public int Inputlocation { get; set; }
public string Inputvalue { get; set; }
}
and here is the query:-
var users = from x in book.Worksheet<User>()
where x.Inputlocation == 1
select x;
This is kinda same as I am doing but somewhat answering the question. But don't what which #RahulSingh is saying.
Also related to #Chris 1 in the code. And creator of this post is i think saying that he does not want to change anythhing in his function. The only change he wants is Console.WriteLine(x["1"]); or Console.WriteLine(x["2"]); to get Inputvalue
Reference to my post
So I have a simple key-value table BillTypes in my SQL Server. It looks like this:
+----+-------------+
| Id | Description |
+----+-------------+
| 1 | OK |
| 2 | Missing |
| 3 | Maximum |
| 4 | Minimum |
| 5 | Error |
+----+-------------+
The table holds different types of bill states. I have another table Items with a foreign key BillType, referring to Id in the previous table.
+----------+------+--------+
| IdItem | Cash |BillType|
+----------+------+--------+
| * | * | * |
| * | * | * |
| * | * | * |
| ... |... | ... |
+----------+------+--------+
Then, in my application I get data from the second table via a stored procedure. In this sp, I am not sure if should select the BillType as it is, a tinyint, or use an innerjoin to return the description instead. The thing is, my application has some logic depending on the BillType, i.e. rows will be processed differently depending on the BillType. Should my logic depend on the Id or the description??
In code, should I have something like this?
DataTable dt = ...//call stored procedure that selects the BillType id
foreach(Datarow r in dt)
{
if(r["BillType"] == 3) ... //use logic
}
or this?
DataTable dt = ...//call stored procedure that selects the BillType description
foreach(Datarow r in dt)
{
if(r["BillType"] == "Maximum") ... //use logic
}
I am currently using the second way, with descriptions, because I think it makes code more readable. I am however worried about the possibility that someone might change the descriptions and break my application logic. On the other hand, using ids would be less readable(though i could work around this with an enum) and would be invulnerable to description changes, but still if someone changed an id, things would break.
To be honest, we don't expec anyone to mess around with the first table, since we made it specially for this application. Still, I am new to SQL and I am trying to learn the best practices of application-SQL interaction, so I would like to know how experienced people do it
Please consider this a comment rather than an answer (I don't yet have enough rep to comment).
I prefer the latter (as you are doing), where I rely on the description.
If your IDs are SQL indentity fields (which auto-increment) Consider the following scenario:
You build your app and database in DEV environment
The production deployment gets back out and re-run
Now, rather than IDs 1,2,3 you have 4,5,6
Generally speaking, it makes more sense to rely on IDs instead of values. After all, IDs should be unique, whereas values can have duplicates/change. Although you are not expecting any changes to the BillTypes table, I wouldn't use the string values, as you suggested.
Since updates/deletes/inserts can still happen in the BillTypes table and you want to be prepared as well as have readable code, the easiest thing to do would be if you define your own enum as such:
public Enum BillTypesEnum
{
OK = 1,
Missing = 2,
Maximum = 3,
Minimum = 4,
Error = 5
};
If something happens and you need to add something to the database, you do it and then change the Enum. It would also work if you delete the "Error" entry and insert "Average". You would not get Average=5 but probably Average=6 or something higher. The enum structure lets you define the values with no problem.
Then you can use it all over your code, not just in this single instance. Your code is modified below:
DataTable dt = ...//call stored procedure that selects the BillType id
foreach(Datarow r in dt)
{
if(r["BillType"] == (int)BillTypesEnum.Maximum) ... //use logic
}
This makes the code more readable then before, you dont have to hard-code the values all over your code but just in one place. And if it happens,that you will not be changing the database, then it's just a plus.
Another option would be to do the spGetAllBillTypes (select * from tblBillTypes) and then internally create a dictionary.
Dictionary<string,int> dictBillTypes = new Dictionary<string,int>();
int = billTypeId
string = billTypeText
When you retrieve your data from the child table (the one with the foreign key), you retrieve your billtypetext.
You can then do it like this:
DataTable dt = ...//call stored procedure that selects the BillType id
foreach(Datarow r in dt)
{
if(r["BillType"] == dictBillTypes["Maximum"]) ... //use logic
}
...but this is still an incomplete solution, because here, you rely on two things:
1. that the billTypeText will not change (if it does, you have to change it in the code)
2. that the billTypeText will not have duplicates (otherwise you will get an exception about duplicate key value in the dictionary)
Another option is to turn it around:
Dictionary<int,string> dict = new Dictionary<int,string>();
and do the search based on the value instead of the key. But that makes the code less readable and you dont really gain a lot from this.
If you are worried about the ID changing, I would recommend creating an Abstract Class to hold the ID values. This can be referenced like an Enum, and it will look up the ID on the first call, and cache the result:
public abstract class BillType
{
private static readonly string c_ok = "ok";
private static readonly string c_missing = "missing";
private static readonly string c_maximum = "maximum";
private static readonly string c_minimum = "minumum";
private static readonly string c_error = "error";
private static int? _ok = null;
private static int? _missing = null;
private static int? _maximum = null;
private static int? _minimum = null;
private static int? _error = null;
public static int OK
{
get
{
if (_ok == null)
_ok = GetBillTypeID(c_ok);
return (int)_ok;
}
}
public static int Missing
{
get
{
if (_missing == null)
_missing = GetBillTypeID(c_missing);
return (int)_missing;
}
}
public static int Maximum
{
get
{
if (_maximum == null)
_maximum = GetBillTypeID(c_maximum);
return (int)_maximum;
}
}
public static int Minimum
{
get
{
if (_minimum == null)
_minimum = GetBillTypeID(c_minimum);
return (int)_minimum;
}
}
public static int Error
{
get
{
if (_error == null)
_error = GetBillTypeID(c_error);
return (int)_error;
}
}
private static int GetBillTypeID(string identifier)
{
using (SqlConnection conn = new SqlConnection("your connection string")
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("spGetBillTypeId", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("Description", identifier);
return Convert.ToInt32(cmd.ExecuteScalar());
}
}
}
}
I would also have a Stored Procedure on your database to do a lookup for that ID:
Create Procedure spGetBillTypeId (#Description Varchar (20))
As Begin
Select Id
From BillTypes
Where Description = #Description
End
With this, you can simply call BillType.Missing and it will pull the Id of missing, cache it, and return 2.
(Note, most of this was copy/paste, so mind random errors I didn't catch).
If you don't expect the values to change, however, and you are okay with making code changes if that does happen, you can simply make an Enum:
public enum BillType
{
OK = 1,
Missing = 2,
Maximum = 3,
Minimum = 4,
Error = 5
}