Is it possible to create an extension method that can be used in an sql query? - sql

I have an application that draws elements from a database when a search button is clicked. I need to update it so that I can find elements that have a specific Substring exactly 6 places into the string. For example, I need to find 111-2233-44-555 by looking for 33 at the 6th and 7th places. My first instinct was to create an extension method for the string class so that I could say something like this:
Dim example As String = "111-2233-44-555"
If example.HasYear(33) Then
'Do Something'
End If
And this worked perfectly. Here's the method I made:
Public Module StringExtensionMethods
''' <summary>
''' Finds the year in a competition number of format XXX-XXXX-XX-XXX
''' </summary>
''' <param name="pstrCompNum">The competition number to find the year in</param>
<Extension()>
Public Function HasYear(ByVal pstrCompNum As String, ByVal pstrCompYear As String) As Boolean
Try
Dim testString As String = pstrCompNum
Debug.Print(testString)
Dim testSubstring As String = testString.Substring(6, 2)
If testSubstring.Equals(pstrCompYear) Then
Return True
End If
Return False
Catch ex As Exception
Throw ex
End Try
End Function
End Module
But a problem arises when I try to use this method in an SQL query. Rightfully so, since HasYear() isn't related to SQL in any way whatsoever. Here's the query I want to execute:
Dim o = From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions") _
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompYear Is Nothing OrElse c.comp_number.HasYear(strYear) = True) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId)) _
Order By c.comp_number _
Select c
I get the error LINQ to Entities does not recognize the method 'Boolean HasYear (System.String, System.String)' method, and this method cannot be translated into a store expression.
So what I'm looking for is essentially a way to make an extension method that can be used in an SQL query. Any ideas?

Don't include custom functions in your LINQ. It must be convertable to SQL so your extension method doesn't qualify. Just execute your query with supported where clauses, then tack on your custom queries after.
Dim o = (
From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions")
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId))
Order By c.comp_number Select c).ToList().
Where(Function(c) c.pstrCompYear Is Nothing OrElse c.comp_number.HasYear(strYear))

I like what #djv posted, but I've decided to try out Andrew Morton's suggestion, to just omit the HasYear() altogether. I ended up using this:
Dim k = From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions") _
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompYear Is Nothing OrElse c.comp_number.Substring(6, 2) = pstrCompYear) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId)) _
Order By c.comp_number _
Select c
Notice the line And (pstrCompYear Is Nothing OrElse c.comp_number.Substring(6, 2) = pstrCompYear)
This solved my problem very simply.

Related

Find and replace text string with only parts of the existing string

I have a .txt file that is formatted like this:
----------------------------------------------------------------------------------------------------------------------------------------------------------
|Order Number|PegReqOrNo |Loc |Product Number |OrdSrtTime|OrdEndTime|Prod. Time|Reqmt Time|OrdSrtDate|OrdEndDate|Comp. Date|Reqmt Date| Date Var.|
----------------------------------------------------------------------------------------------------------------------------------------------------------
|000105812778| |0002|10000347 |10:03:50 |19:37:43 |19:37:43 |00:00:00 |08/02/2016|02/16/2022|02/16/2022| | 0/00:00:00|
|000106805034|4200252838 |0002|H827080082GAAZ |13:43:25 |08:30:04 |08:30:04 |15:00:00 |02/18/2020|09/02/2020|09/02/2020|08/24/2020| 8/17:30:04-|
I am looking to change the strings with pattern ##:##:## in the date var. column, the last on the right. I want to retain the value in front of the / and the - at the end, if there is one.
The two examples from the data above are 0/00:00:00 and 8/17:30:04-, thus I want to retain 0 and -8 respectively.
For your strange request you can use as follows. I hope I well understood
Private Sub AdjustMyData()
Try
Dim allLines As IEnumerable(Of String) = IO.File.ReadAllLines("C:\Users\YourUser\Desktop\test\test.txt")
Dim data As List(Of String) = (From elemet In allLines).Select(Function(linea As String)
If linea.Contains("|") Then
Dim parts = linea.Split(CChar("|"))
Dim newLinea = (From part In parts
Where part IsNot Nothing
Where part Like "*#/##:##:##*").Select(
Function(s As String) As String
If s Is Nothing OrElse s.Length = 0 Then Return ""
linea = linea.Replace(s, "~")
Dim numerisS As String = CStr(IIf(s.Contains("-"), "-", "")) & Trim(s.Remove(s.IndexOf("/")))
Return Space(s.Length - numerisS.Length) & numerisS
End Function).ToList.FirstOrDefault
Return Strings.Replace(linea, "~", newLinea)
End If
Return linea
End Function).ToList
IO.File.WriteAllLines("C:\Users\YourUser\Desktop\test\testFinal.txt", data.ToArray)
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub

For Loop: changing the loop condition while it is looping

What I want to do is replace all 'A' in a string with "Bb". but it will only loop with the original string not on the new string.
for example:
AAA
BbAA
BbBbA
and it stops there because the original string only has a length of 3. it reads only up to the 3rd index and not the rest.
Dim txt As String
txt = output_text.Text
Dim a As String = a_equi.Text
Dim index As Integer = txt.Length - 1
Dim output As String = ""
For i = 0 To index
If (txt(i) = TextBox1.Text) Then
output = txt.Remove(i, 1).Insert(i, a)
txt = output
TextBox2.Text += txt + Environment.NewLine
End If
Next
End Sub
I think this leaves us looking for a String.ReplaceFirst function. Since there isn't one, we can just write that function. Then the code that calls it becomes much more readable because it's quickly apparent what it's doing (from the name of the function.)
Public Function ReplaceFirst(searched As String, target As String, replacement As String) As String
'This input validation is just for completeness.
'It's not strictly necessary.
'If the searched string is "null", throw an exception.
If (searched Is Nothing) Then Throw New ArgumentNullException("searched")
'If the target string is "null", throw an exception.
If (target Is Nothing) Then Throw New ArgumentNullException("target")
'If the searched string doesn't contain the target string at all
'then just return it - were done.
Dim foundIndex As Integer = searched.IndexOf(target)
If (foundIndex = -1) Then Return searched
'Build a new string that replaces the target with the replacement.
Return String.Concat(searched.Substring(0, foundIndex), replacement, _
searched.Substring(foundIndex + target.Length, searched.Length - (foundIndex + target.Length)))
End Function
Notice how when you read the code below, you don't even have to spend a moment trying to figure out what it's doing. It's readable. While the input string contains "A", replace the first "A" with "Bb".
Dim input as string = "AAA"
While input.IndexOf("A") > -1
input = input.ReplaceFirst(input,"A","Bb")
'If you need to capture individual values of "input" as it changes
'add them to a list.
End While
You could optimize or completely replace the function. What matters is that your code is readable, someone can tell what it's doing, and the ReplaceFirst function is testable.
Then, let's say you wanted another function that gave you all of the "versions" of your input string as the target string is replaced:
Public Function GetIterativeReplacements(searched As String, target As String, replacement As String) As List(of string)
Dim output As New List(Of String)
While searched.IndexOf(target) > -1
searched = ReplaceFirst(searched, target, replacement)
output.Add(searched)
End While
Return output
End Function
If you call
dim output as List(of string) = GetIterativeReplacments("AAAA","A","Bb")
It's going to return a list of strings containing
BbAAA, BbBbAA, BbBbBbA, BbBbBbBb
It's almost always good to keep methods short. If they start to get too long, just break them into smaller methods with clear names. That way you're not trying to read and follow and test one big, long function. That's difficult whether or not you're a new programmer. The trick isn't being able to create long, complex functions that we understand because we wrote them - it's creating small, simpler functions that anyone can understand.
Check your comments for a better solution, but for future reference you should use a while loop instead of a for loop if your condition will be changing and you're wanting to take that change into account.
I've made a simple example below to help you understand. If you tried the same with a for loop, you'd only get "one" "two" and "three" printed because the for loop doesn't 'see' that vals was changed
Dim vals As New List(Of String)
vals.Add("one")
vals.Add("two")
vals.Add("three")
Dim i As Integer = 0
While i < vals.Count
Console.WriteLine(vals(i))
If vals(i) = "two" Then
vals.Add("four")
vals.Add("five")
End If
i += 1
End While
If you do want to replace one by one instead of using the Replace function, you could use a while loop to look for the index of your search character/string, and then replace/insert at that index.
Sub Main()
Dim a As String = String.Empty
Dim b As String = String.Empty
Dim c As String = String.Empty
Dim d As Int32 = -1
Console.Write("Whole string: ")
a = Console.ReadLine()
Console.Write("Replace: ")
b = Console.ReadLine()
Console.Write("Replace with: ")
c = Console.ReadLine()
d = a.IndexOf(b)
While d > -1
a = a.Remove(d, b.Length)
a = a.Insert(d, c)
d = a.LastIndexOf(b)
End While
Console.WriteLine("Finished string: " & a)
Console.ReadLine()
End Sub
Output would look like this:
Whole string: This is A string for replAcing chArActers.
Replace: A
Replace with: Bb
Finished string: This is Bb string for replBbcing chBbrBbcters.
I was going to write a while loop to answer your question, but realized (with assistance from others) that you could just .replace(x,y)
Output.Text = Input.Text.Replace("A", "Bb")
'Input = N A T O
'Output = N Bb T O
Edit: There is probably a better alternative, but i quickly jotted this loop down, hope it helps.
You've said your new and don't fully understand while loops. So if you don't understand functions either or how to pass arguments to them, I'd suggest looking that up too.
This is your Event, It can be a Button click or Textbox text change.
'Cut & Paste into an Event (Change textboxes to whatever you have input/output)
Dim Input As String = textbox1.Text
Do While Input.Contains("A")
Input = ChangeString(Input, "A", "Bb")
' Do whatever you like with each return of ChangeString() here
Loop
textbox2.Text = Input
This is your Function, with 3 Arguments and a Return Value that can be called in your code
' Cut & Paste into Code somewhere (not inside another sub/Function)
Private Function ChangeString(Input As String, LookFor As Char, ReplaceWith As String)
Dim Output As String = Nothing
Dim cFlag As Boolean = False
For i As Integer = 0 To Input.Length - 1
Dim c As Char = Input(i)
If (c = LookFor) AndAlso (cFlag = False) Then
Output += ReplaceWith
cFlag = True
Else
Output += c
End If
Next
Console.WriteLine("Output: " & Output)
Return Output
End Function

weird IF statement error " Conversion from string "FAIL" to type 'Long' is not valid."

I'm basically running through rows in a table. I'm checking each row to see if at least 1 of the columns im interested is = "pass" and that the rest = either "pass" or "N/A".
But im getting an error:
"An exception of type 'System.InvalidCastException' occurred in Microsoft.VisualBasic.dll but was not handled in user code
Additional information: Conversion from string "FAIL" to type 'Long' is not valid."
The statuses can either be "PASS", "FAIL", OR "N/A" in the DB is you're wondering where the FAIL came from.
Here's the code:
Private Function HasAuditPassed(activeLotId As String) As Boolean
Dim sql = String.Format("SELECT STEPID, CAVITYPAIRASTATUS,CAVITYPAIRBSTATUS,CAVITYPAIRCSTATUS,CAVITYPAIRDSTATUS FROM " & _
"TB_BL_AMMSSTEPSTATUS WHERE AUDITLOT = '{0}'", activeLotId)
Dim dynaset = DB.CreateDynaset(sql, DBWrapper.DynasetOptions.ORADYN_READONLY)
Do Until dynaset.EOF
Dim cavityPairAStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRASTATUS")
Dim cavityPairBStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRBSTATUS")
Dim cavityPairCStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRCSTATUS")
Dim cavityPairDStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRDSTATUS")
If (cavityPairAStatus Or cavityPairBStatus Or cavityPairCStatus Or cavityPairDStatus) = "PASS" And
(cavityPairAStatus And cavityPairBStatus And cavityPairCStatus And cavityPairDStatus) = ("PASS" Or "N/A") Then
Return True
End If
Loop
Return False
End Function
Sadly, you can't do if statements that way. You have to write the entire statement out because VB .Net does not handle string comparison that way. So a statement that would be the equivalent and work would be much longer and look like this:
If (cavityPairAStatus = "PASS" OrElse cavityPairBStatus = "PASS" OrElse
cavityPairCStatus = "PASS" OrElse cavityPairDStatus = "PASS")
And (cavityPairAStatus = "PASS" OrElse cavityPairAStatus = "N/A" AndAlso
cavityPairBStatus = "PASS" OrElse cavityPairBStatus = "N/A" AndAlso
cavityPairCStatus = "PASS" OrElse cavityPairCStatus = "N/A" AndAlso
cavityPairDStatus = "PASS" OrElse cavityPairDStatus = "N/A") Then
Return True
End If
A few more elegant solutions with creating lists are below, and can be used to help out readability some if you have to write statements like this repeatedly.
There are a couple of issues here. You can't write a If stytement in this style and you can't use binary operators (And and Or) with String.
Your If statement should be something like this:
If (cavityPairAStatus = "PASS" OrElse cavityPairBStatus = "PASS" OrElse …
If you want something a little more elegant you may consider something like this:
Dim values = {cavityPairAStatus, cavityPairBStatus, cavityPairCStatus, cavityPairDStatus}
If values.Any(Function(v) v.Equals("PASS")) AndAlso values.All(Function(v) v.Equals("PASS") OrElse v.Equals("N/A")) Then
…
End If
The way IF statement you have used is incorrect.
To compare so many conditions you need to write them separately.
Instead of using IF condition here I would suggest you to use a list.
Here's the sample code
Dim cavityPairStatus As New List(Of String)
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRASTATUS"))
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRBSTATUS"))
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRCSTATUS"))
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRDSTATUS"))
If cavityPairStatus.Contains("PASS") OrElse cavityPairStatus.Contains("N/A") Then
Return True
End If

Setting value to a not-existing array item?

Please take a look at the following code:
Try
Dim Reader As New System.IO.StreamReader(PositionsFileName)
Do While Reader.Peek() <> -1
Dim Positions() As String = Reader.ReadLine().Split("|")
If (Positions(0) Is Nothing) Or (Positions(1) Is Nothing) Or (Positions(2) Is Nothing) Then
' something
End If
Loop
Catch ex As Exception
ex.Source = Nothing
End Try
I am reading a file and expecting format something|something1|something2. I am trying to make it set "Nothing" to the array index which does not exists (the file format is broken), so that the If statement goes smoothly, but it seem I am doing it wrong. Can you give me some hints?
If you do Split("|") and there are only 2 items (for example, something|something1), Positions(2) will not be Nothing, it will just not be there. So your code will raise an exception, something about index out of bounds of the array.
If you need Positions(2) contain Nothing in this case, you code can look like this:
Dim Positions(2) As String
Dim tmpArray() As String = Reader.ReadLine().Split("|")
For i = 0 To UBound(Positions)
If i <= UBound(tmpArray) Then
Positions(i) = tmpArray(i)
Else
Positions(i) = Nothing
End If
Next
I assume that you only have three "Somethings" per valid line. If so, try writing your Positions() assignment like this:
Dim Positions() As String = Reader _
.ReadLine() _
.Split("|") _
.Concat(Enumerable.Repeat("Nothing", 3)) _
.Take(3) _
.ToArray()
This will ensure that you have three items every time. No need to check for nothings.
Just check positions.length after the split. Also, if you want to check for cases like "||Something2|Something3", the first position will be "" not Nothing. The orelse is a shortcircuit that will keep the latter condtitions from being evaulated if an earlier condition is met.
If Positions.length < 3 OrElse Positions(0) = "" OrElse Positions(1) = "" OrElse Positions(2) = "" Then
' something
End If

vb.net linq update Data

Hi Guys thanks for all the help on things. Im using linq and im able get get data ouf of it realy easy. but i seem to not be able to update the data. the program does not error on it and it looks likes it has updated but it does not save te changes.
Public Function UpdateAlarmsbyKey(ByVal objKey As Integer, ByVal IdNumber As String) As Boolean
Dim lqAlarms As New linqAlarmDumpDataContext
Dim GetAlrms = From r In lqAlarms.AlarmDrops _
Where r.Key = objKey _
Select r
For Each Calls In GetAlrms
If Calls.AlarmsHandled = "" Then
Calls.AlarmsHandled = IdNumber
Return True
Else
Calls.AlarmsHandled = Calls.AlarmsHandled & ":" & IdNumber
Return True
End If
Next
Return False
End Function
Try calling -
lqAlarms.SubmitChanges()
after you've updated the Calls.AlarmsHandled property. Also, your code is looping round a result set but will only change the first record it finds, is that the behaviour you wanted?