I am (trying to) using the sqlite-net-pcl package version 1.8.116, to create a user defined function in C#.
I tried to start simple by creating a function to reverse a string, like REVERSE
When i try the query: SELECT 1,'abc','def',
I am expecting the output to be: 1:abc:fed
But the output is: 0:abc:System.Char[], So I must have done something wrong...
Can anyone give a tip? because I am pretty clueless right now... 😉
My code:
SQLiteConnection db = new SQLiteConnection("test.db");
// https://sqlite.org/c3ref/create_function.html
sqlite3 dbhandle = db.Handle;
string function = "myReverse";
int narg = 1;
int eTextRep = SQLitePCL.raw.SQLITE_UTF8 | SQLitePCL.raw.SQLITE_DETERMINISTIC;
int ok = SQLitePCL.raw.sqlite3_create_function(dbhandle, function, narg, eTextRep, myReverse);
//Console.WriteLine($"OK:{ok}");
SQLiteCommand cmd = db.CreateCommand("SELECT 1 as id, 'abc' as t, myReverse('def') as revt");
var result = cmd.ExecuteQuery<Record>();
Console.WriteLine($"{result[0].id}:{result[0].t}:{result[0].revt}");
and:
static void myReverse(sqlite3_context ctx, object user_data, sqlite3_value[] args)
{
SQLitePCL.utf8z utf8z = raw.sqlite3_value_text(args[0]);
string input = utf8z.utf8_to_string();
//Console.WriteLine($"INPUT:{input}"); // This shows INPUT:def
char[] charArray = input.ToCharArray();
Array.Reverse(charArray);
//raw.sqlite3_result_text(ctx, utf8z.FromString(charArray.ToString())); // see: comments...
raw.sqlite3_result_text(ctx, utf8z.FromString(new string(charArray)));
}
Record:
public class Record
{
public int id { get; set; }
public string t { get; set; }
public string revt { get; set; }
}
Above was my first try, finally I would like to have an implementation of the function LIKE, like is mentioned in paragraph 5 "The infix LIKE operator is implemented by calling the application-defined SQL functions"
Currently returning a string is working, but I am looking for something like raw.sqlite3_result_boolean, or raw.sqlite3_result_logical, because the return value for the function LIKE is a boolean.
Related
I have the following model:
public class LogData
{
public Guid ID { get; set; }
public string Name { get; set; }
}
I use Entity Framework Core to save those models to an SQLite database, it works well.
I need to delete from the data (it is dynamic, I can't use objects), so I use the following command:
string command="DELETE FROM LogData WHERE ID IN ('ea53b72a-4ab2-4f88-8f1d-0f96baa7cac7')";
context.Database.ExecuteSQLCommand(command);
According to the SQLite syntax, it is valid.
Unfortunately, as a result I get back 0, so no row was affected.
When I remove the WHERE condition, it deletes the contents of the table.
I have a guessing that as the key column is a Guid and it is stored as a BLOB, the plain SQLite engine can't find it.
So I tried to alter the command to this:
string command="DELETE FROM LogData WHERE HEX(ID) IN ('ea53b72a-4ab2-4f88-8f1d-0f96baa7cac7')";
context.Database.ExecuteSqlCommand(command);
Also tried this:
string command="DELETE FROM AuditLog WHERE HEX(ID) = 'ea53b72a-4ab2-4f88-8f1d-0f96baa7cac7'";
context.Database.ExecuteSqlCommand(command);
This, too:
string command="DELETE FROM AuditLog WHERE ID = 'ea53b72a-4ab2-4f88-8f1d-0f96baa7cac7'";
context.Database.ExecuteSqlCommand(command);
None of those helped.
What should I do about this?
The GUIDs are stored in the database as a binary BLOB meaning you need to pass in a binary value to compare against. To do this you use the X'...' notation. Additionally, you need to convert the endianness of the GUID to little endian. Fortunately, there's a handy extension method here to do your conversion:
public static Guid FlipEndian(this Guid guid)
{
var newBytes = new byte[16];
var oldBytes = guid.ToByteArray();
for (var i = 8; i < 16; i++)
newBytes[i] = oldBytes[i];
newBytes[3] = oldBytes[0];
newBytes[2] = oldBytes[1];
newBytes[1] = oldBytes[2];
newBytes[0] = oldBytes[3];
newBytes[5] = oldBytes[4];
newBytes[4] = oldBytes[5];
newBytes[6] = oldBytes[7];
newBytes[7] = oldBytes[6];
return new Guid(newBytes);
}
And you use it like this:
//The source GUID
var source = Guid.Parse("ea53b72a-4ab2-4f88-8f1d-0f96baa7cac7");
//Flip the endianness
var flippedGuid = source.FlipEndian();
//Create the SQL
var command = $"DELETE FROM AuditLog WHERE ID = X'{flippedGuid.ToString().Replace("-", "")}'";
context.Database.ExecuteSqlCommand(command);
I need to make a list like this: List>
And I want to copy contents from a table on this website .
More specificly, I want the word in languange 1 in the first string and the word in languange 2 in the second string, and then do that for every word in that table.
I want to be able to do that by just entering a url because I want to do this for more languanges.
It`s probably pretty easy but I have never done something like this before so sorry if this question is trivial.
Also, please excuse my english scince it`s not my first language.
Thanks in advance.
You can use AngleSharp
public static async Task Main(string[] args)
{
List<WordCls> wordList = new List<WordCls>();
IBrowsingContext context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());
Url url = Url.Create("http://1000mostcommonwords.com/1000-most-common-afrikaans-words");
IDocument doc = await context.OpenAsync(url);
IElement tableElement = doc.QuerySelector("table");
var trs = tableElement.QuerySelectorAll("tr");
foreach (IElement tr in trs.Next(selector: null))
{
var tds = tr.QuerySelectorAll("td");
WordCls word = new WordCls
{
Number = Convert.ToInt32(tds[0].Text()),
African = tds[1].Text(),
English = tds[2].Text()
};
wordList.Add(word);
}
Console.WriteLine(wordList.Count);
}
public class WordCls
{
public int Number { get; set; }
public string African { get; set; }
public string English { get; set; }
}
You can check out HTMLAgility pack for C#. it is a powerful tool to crawl Web content as well. you can find enough information over here https://html-agility-pack.net/from-web
I am using DataAdapter("SDM_Tran_GenerateInvoice") inside DataSet("SDMDAL.xsd") in my project.
Below is the structure of DataAdapter along with the Stored Procedure names in it:
Below is the Table structure Im using for the same:
I am calling this DataAdapter inside Class file named as SDM.InvoiceBLL.cs:
using SDMDALTableAdapters;
public class SDM_Invoice
{
private SDM_Tran_GenerateInvoiceTableAdapter _GenerateInvoiceTableAdapter = null;
protected SDM_Tran_GenerateInvoiceTableAdapter Adapter
{
get
{
if (_GenerateInvoiceTableAdapter == null)
_GenerateInvoiceTableAdapter = new SDM_Tran_GenerateInvoiceTableAdapter();
return _GenerateInvoiceTableAdapter;
}
}
#region GET
//to show data in Invoice Grid
public SDMDAL.SDM_Tran_GenerateInvoiceDataTable SelectInvoice(string _SPID)
{
return Adapter.SelectInvoice(_SPID);
}
//to show data in 1st hidden Grid
public SDMDAL.SDM_Tran_GenerateInvoiceDataTable GetInvoiceBillingBySPID(string _SPID)
{
return Adapter.GetInvoiceBillingBySPID(_SPID);
}
//to fetch InvoiceID for unique key generation
public SDMDAL.SDM_Tran_GenerateInvoiceDataTable GetInvoiceID()
{
return Adapter.GetInvoiceID();
}
//to fetch InvoiceNumber for unique key generation
public SDMDAL.SDM_Tran_GenerateInvoiceDataTable GetInvoiceNumber()
{
return Adapter.GetInvoiceNumber();
}
#endregion
public string Insert(string InvoiceID, string SPfoID, string InvoiceLineNo, string InvoiceNo, string InvoiceType, string BillingIDfoID, string BusinessUnit, string DirectCost, string InvfoStatusID, string Status, DateTime Date, string AccountCode)
{
string query = Convert.ToString(Adapter.Insert1(InvoiceNo, SPfoID, InvoiceLineNo, InvoiceNo, InvoiceType, BillingIDfoID, BusinessUnit, DirectCost, InvfoStatusID, Status, Date, AccountCode));
return query;
}
public SDM_Invoice()
{
}
}
and then calling the "Insert" function of class file inside Default.aspx.cs page, to save records on button click:
protected void btnInvoice_Click(object sender, EventArgs e)
{
generateInvoiceId();
generateInvoiceNumber();
string InvType = rlbInvType.SelectedValue;
string Status = "Draft";
string BillingID;
string DirectCost;
string BusinessUnit;
string StatusID;
string AccCode;
foreach (GridDataItem itm in rgData.Items)
{
BillingID = itm["BillingID"].Text;
DirectCost = itm["DCIDescription"].Text;
BusinessUnit = itm["BUName"].Text;
StatusID = itm["BUfoStatusID"].Text;
Label lb = (Label)itm.FindControl("Label1");
string InvLineNo = lb.Text;
try
{
SDM.Invoice.Insert(lblInvId.Text, _SPID, InvLineNo, lblInvNo, InvType, BillingID, BusinessUnit, DirectCost, StatusID, Status, DateTime.Now, AccCode);
}
catch (Exception ex)
{
}
}
}
I rebuilt my project number of times and when I run my web page "Default.aspx.cs", always it gives me below error:
The best overloaded method match for 'SDM_Invoice.Insert(string, string, string, string, string, string, string, string, string, string, System.DateTime, string)' has some invalid arguments
I searched many articles related to my issue but couldn't find any solution for my problem.
This is the first time I am working with TableAdapter. Please help me what is wrong in my code ? What am I missing in it. Thanks in advance.
All the arguments except for the next to last need to be strings, but you seem to be passing some non-string values, for example lblInvNo, which seems to be a user interface element.
Check the type of each argument aside from the next to last, and make sure they are all strings.
I need to compare 2 strings in SQL that are in the following format:
name_1.2.3
Can someone explain how to compare them correctly? How do you compare non-int value?
I had an issue in the past when you compare strings with numbers: the comparison is done literally. This gets me into problems like where "name_1.5.3" is "bigger" than "name_1.20.3", because it probably compares char by char, but that's not what I'm trying to achieve.
Some SQL or C# example will be really helpful.
Part of my code in C#:
for (int i = 0; i < PDCs.Count(); i++)
{
if (foundInstallOrder)
{
string justNumbers = new String(PDCs[i].Where(Char.IsDigit).ToArray());
a = Convert.ToInt32(justNumbers);
if (projPDCs.Count() <= i)
{
b = 0;
}
else
{
justNumbers = new String(projPDCs[i].Where(Char.IsDigit).ToArray());
b = Convert.ToInt32(justNumbers);
}
if (a > b )//compare the currentPDC with the projfound, if result is 1 => that means current is within range
{
foundInstallOrder = true;
arrayInstallOrder.Add(lines);
break;
}
else if (a == b) //numbers are equal
{
foundInstallOrder = true;
}
else //lower
{
foundInstallOrder = false;
break;
}
}
}
As you can see this is too complex, it is working but I'm trying to find a better solution.
Although I do suggest that you keep those numbers in a separate column in the table for easier/faster querying, you can also solve this by adding regular expression support to SQL server and use it in your query.
SETUP
Create a .NET Class Library, named 'SqlExtensions', containing this code:
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
namespace SqlExtensions
{
public static partial class UserDefinedFunctions
{
[SqlFunction]
public static SqlString RegexGroup(SqlString input, SqlString pattern, SqlString name)
{
Regex regex = new Regex(pattern.Value, RegexOptions.Singleline);
Match match = regex.Match(input.Value);
return match.Success ? new SqlString(match.Groups[name.Value].Value) : SqlString.Null;
}
}
}
Build in Release.
Then, add the class library to SQL server using SQL Server Management Studio:
Connect to database server
Expand Databases
Expand the database you're working on
Expand Programmability
Right-click Assemblies
Add new assembly by browsing to the Class Library DLL created above
Add the Scalar-valued function to the database using SQL Server Management Studio:
Select the database you're working on
Click New Query and execute this query:
CREATE FUNCTION [dbo].[RegexGroup] (#input nvarchar(max), #pattern nvarchar(max), #name nvarchar(max))
RETURNS nvarchar(max)
AS EXTERNAL NAME [SqlExtensions].[SqlExtensions.UserDefinedFunctions].[RegexGroup];
If .NET execution is not enabled, execute a new query:
Select the database you're working on
Click New Query and execute this query
sp_configure #configname=clr_enabled, #configvalue=1;
GO
RECONFIGURE
GO
QUERY
SELECT
*,
dbo.RegexGroup(Name, '^(?<n>[^_]*)_', 'n') AS n,
CONVERT(INT, dbo.RegexGroup(Name, '_(?<major>[0-9]+)\.', 'major')) AS Major,
CONVERT(INT, dbo.RegexGroup(Name, '\.(?<minor>[0-9]+)\.', 'minor')) AS Minor,
CONVERT(INT, dbo.RegexGroup(Name, '\.(?<build>[0-9]+)$', 'build')) AS Build
FROM dbo.Table_1;
RESULT
name_1.5.3 name 1 5 3
name_1.20.3 name 1 20 3
name_5.19.2 name 5 19 2
name_0.6.3 name 0 6 3
name_1.20.2 name 1 20 2
Tokenise the string using String.split('.') and then int.parse each "part string". This allows you to check major, minor, and build as required.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<NameVersion> lst = new List<NameVersion>();
lst.Add(new NameVersion("name_1.2.3"));
lst.Add(new NameVersion("name_1.20.3"));
lst.Sort(delegate(NameVersion x, NameVersion y)
{
int i = x.a.CompareTo(y.a);
if (i == 0)
i = x.b.CompareTo(y.b);
if (i == 0)
i = x.c.CompareTo(y.c);
return i;
});
}
}
public class NameVersion
{
public string Name = "";
public int a, b, c;
public NameVersion(string text)
{
Name = text;
string[] sa = text.Split('_');
string[] sb = sa[1].Split('.');
a = Convert.ToInt32(sb[0]);
b = Convert.ToInt32(sb[1]);
c = Convert.ToInt32(sb[2]);
}
}
Lets start off with a list of strings that will be used to filter the results:
List<String> RadioNames = new List<String>();
RadioNames.AddRange(new String[] { "abc", "123", "cba", "321" });
I want to be able to filter a LINQ to SQL database table based on RadioNames but the catch is that I want RadioNames to be a partial match (meaning it will catch Radio123 and not just 123).
The source that I need to filter is below:
var ChannelGrants = from cg in sddc.ChannelGrants
select new
{
cg.ID,
cg.Timestamp,
cg.RadioID,
cg.Radio
};
So I need to perform something similar to below (outside of the original ChannelGrants results as this is a conditional search)
if(RadioNamesToSearch != null)
{
List<String> RadioNames = new List<String>();
// Here I split all the radio names from RadioNamesToSearch based on a command separator and then populate RadioNames with the results
ChannelGrants = from cg in ChannelGrants
where ???
select cg;
}
I need help where ??? is in the code above (or if ChannelGrants = ... is invalid all together). Repeating above, I need to filter ChannelGrants to return any matches from RadioNames but it will do partial matches (meaning it will catch Radio123 and not just 123).
All the code is contained in a method as such...
public static DataTable getBrowseChannelGrants(int Count = 300, String StartDate = null, String StartTime = null, String EndDate = null, String EndTime = null, String RadioIDs = null, String RadioNamesToSearch = null, String TalkgroupIDs = null, String TalkgroupNames = null, bool SortAsc = false)
What field in ChannelGrants are you comparing RadioNames to?
To retrieve entries that are only in your RadioNames list, you'd use the contains method like this
ChannelGrants = from cg in ChannelGrants
where RadioNames.Contains(cg.Radio)
select cg;
(If you wanted to find all rows that had one of your RadioNames in the Radio property. Replace cg.Radio with the appropriate column you are matching)
This gives you a similar outcome if you had this where clause in SQL
where cg.Radio in ("abc", "123", "cba", "321")
from this link How to do SQL Like % in Linq?
it looks like you can combo it with like matching as well, but adding slashes, by it's not something I've done personally.
in place of the ???
RadioNames.Where(rn=>cg.Radio.ToLower().Contains(rn.ToLower())).Count() > 0
That should do it...
The ToLower() calls are optional, of course.
EDIT: I just wrote this and it worked fine for me in a Console Application. The result contained one item and the WriteLine spit out "cbaKentucky". Not sure what to tell ya.
class Program
{
static void Main(string[] args)
{
List<String> RadioNames = new List<String>();
RadioNames.AddRange(new String[] { "abc", "123", "cba", "321" });
List<ChannelGrants> grants = new List<ChannelGrants>();
grants.Add(new ChannelGrants() { ID = 1, Radio = "cbaKentucky", RadioID = 1, TimeStamp = DateTime.Now });
var result = from cg in grants
where RadioNames.Where(rn=>cg.Radio.ToLower().Contains(rn.ToLower())).Count() > 0
select cg;
foreach (ChannelGrants s in result)
{
Console.WriteLine(s.Radio);
}
}
}
class ChannelGrants
{
public int ID { get; set; }
public DateTime TimeStamp { get; set; }
public int RadioID { get; set; }
public string Radio { get; set; }
}
At the moment, there doesn't seem to be a best way so I'll answer this until a new answer that doesn't repeat the other answers that don't work on this thread.