VB.NET + LINQ: Save in a single class attribute the result of querying two columns from different tables - vb.net

I have two tables: Estructura (with two fields I want: descripcion_morologica and interpretacion) and estrato (with two fields I want: descripcion_larga and interpretacion_explic). On the other side I hace a class in VS with the attributes descripcion and interpretacion. Both tables have a common field called id_excavacion, which I pass to the method as a parameter.
What I'm trying to achieve is to make a query which saves in the "descripcion" attribute the results of t1.descripcion_morfologica and t2.descripcion_larga and also saves in "interpretacion" the results of t1.interpretacion and t2.interpretacion_explic.
So far, I've tried like this:
'GET: api/Excavacions/ListadoUE/5
<Route("api/Excavacions/ListadoUE/{idExcavacion}")>
Function GetListadoUEs(ByVal idExcavacion As Integer) As IQueryable(Of ListadoUEDto)
Dim listado =
From estru In db.Estructura
Join estra In db.Estrato On estra.id_excavacion Equals estru.id_excavacion
Where estru.id_excavacion = idExcavacion
Select New ListadoUEDto With {
.Descripcion = estru.descripcion_morfologica And estra.descripcion_larga,
.Interpretacion = estru.interpretacion And estra.interpretacion_explic
}
End Function
But I only get null, despite the id I pass actually exists.
Thanks a lot in advance!!

You should almost certainly not be using And there. That is a Boolean operator, for combining True and False values. As is always the case, if you want to concatenate Strings then you use &, which is the string concatenation operator.
You should have Option Strict On and then the compiler would have warned you that you were doing something that doesn't make sense. You should turn it On in the project properties and also in the VS options, so it is On by default for future projects. That will force you to put more thought into what data types you use and, therefore, make you write better code.

Related

Can you build an expression inside of a custom code function in Report Builder?

I need to provide my RDL files to teammates so that they can make minor customizations for each client. One of the ways I thought I might improve the efficiency is if I could build more complex expressions inside of custom code functions so that they can input some simple arguments and have the function handle the "heavy lifting" of adjusting the expression accordingly.
This is a very simple example, and not one I would take this step for, but I thought it the easiest place to start figuring out if I can make this work. For instance, in a tablix we want a count returned based on a value where the value is customized per client (and isn't a parameter).
=Count(iif(trim(Fields!Category.Value)="OPTIONA",1,nothing))
Is there a way I could build a function so that my teammates would just need to enter the following?
=Code.CustomFunction("OPTIONA")
My understanding is that the custom code in Report Builder can't query datasets, or at least not in the way that an expression within a tablix would be able to. I've built custom functions that work with the results of an expression added as the argument, but I can't seem to wrap my head around if there's a way to construct an expression within a custom function and pass it back to the expression.
For instance:
Public Function CustomFunction(field As String) As String
Dim customExpression As String = "Count(iif(trim(Fields!Category.Value)=" & field & ",1,nothing))"
Return customExpression
End Function
As expected, this just returns a string with the text of the expression, but not an executed expression. Is what I'm trying to achieve possible with Report Builder?
Or, as an alternative approach, can I somehow place variables at the beginning of an expression that are used later so that anyone else working on the expression just needs to worry about the beginning? Essentially create multiple custom functions and call them later on?
=Code.CustomFunction("OPTIONA",Count(iif(trim(Fields!Category.Value)=Code.CustFunc01(),1,nothing)))
Honestly not sure how I would go about building the functions themselves from here.
You can use a function instead of the related field. The function takes the field string as an argument and the filter string for which will increase the counter. Finally it returns the original field value
Private Dim Counter As Integer
Public Function SetCounter( Expr As String, Filter As String) As String
If Expr = Filter Then Counter = Counter + 1
Return Expr
End Function
Public Function GetCounter( ) As Integer
Return Counter
End Function
For the field value you can use the following expression (yellow color)
=Code.SetCounter( Fields!MyString.Value,"OPTION A")
To get the counter value you can either use the following expression calling a function (orange color)
= Code.GetCounter()
Or make the variable public and use Code.Counter as the expression

Naming Boolean Variables for Mutually Exclusive options rather than True/False relationship

I'm looking for a way to name a boolean variable that represents two mutually exclusive options such that it's clear what the selected option is when the variable is True or when it's False, without looking at the implementation.
As an example, imagine your program chooses between Chicken xor Fish. Currently, in the database, the variable would be named "lChickenOrFish". Looking at just that, it's unclear if lChickenOrFish = True would represent Chicken, or if it would represent Fish. To find out, you would have to open the code and search for where it's used, and interpret from there.
I know you could instead have two variables, lChicken and lFish, but that opens up the possibility of having lChicken = True and lFish = True in the same row, which would be an error, and therefore require code to check for that.
You could also just use lChicken for the name, and have lChicken = 0 implicitly mean Fish. But then, if a programmer needs to know what the other option is, they have to open the code to look it up, which is the same problem as before. And in some cases, it may not even be clear if there is another option to look up, depending on what the boolean is doing.
So is there a way to make that clear for a programmer looking purely at a SQL Server database without looking up the actual implementation in code?
Rather than use a boolean, I'd recommend an enum or a string value. With no language specified, I can offer some pseudo code for an enum:
enum FoodValue {
Chicken = 1,
Fish = 2
}
var dbValue = FoodValue.Chicken;
That way if you want to add values later, it is easy to update the enum or store the string value directly.

How to write Subset ⊆ in an operation

I want to perform logic operation to check if a string or a set of numbers is contained in a variable. In the same manner simply write:
a + b
a * b
a = b
Is there a way to write something like:
a ⊆ b
I expect te retrieve a boolean result out of it, stating true or false to determine if it is contained in the other variable. I am writing a comparison tool and would like to simplify it to use a math or logic operator instead of a method like InStr().
You can use LINQ for this:
Dim bContainsAllA As Boolean = Not a.Except(b).Any()
You can't create new operators... Your options is to use an existing operator, create a method or do an extension method.
I wouldn't recommend using InStr since this is old VB. There are good methods in the String class. Or use LINQ.

Using a Dictionary TryGetValue With Multiple Conditions

I am fairly new to VB net and have been playing around with dictionaries for the past week. I have a problem however when trying to do something rather complex with my dictionary look-up.
First, I should point out that I am filling my dictionary with a class object in order to store multiple values:
Class NodeLoad
Public Property NodeName As String
Public Property NodeCase As String
Public Property NodeAxis As String
Public Property NodeDir As String
Public Property NodeValue As Double
End Class
And my problem lies in doing a dictionary look-up where my only option is to do a try catch for when the value I am looking for doesn't exist:
Try
tempnodeitem = (From load In load_dict.Values Where load.NodeName = nodenum And load.NodeCase = pattern And load.NodeDir = dirarray(d)).First
loadforce(d) = tempnodeitem.NodeValue
Catch ex As Exception
loadforce(d) = "0"
End Try
The above code runs, but it takes much longer than I would expect, and after a little research found that try/catch takes much longer than TryGetValue. The thing I would like to do (since it is a much for efficient function) is to use TryGetValue. However, as far as I know, it only works for one key and one value (TKey, TValue).
Can anyone give me an example of how to use TryGetValue with multiple conditions?
Or perhaps how to catch false dict look-ups without being resource intensive?
I am thinking a good way to approach this problem is using nested TryGetValue statements... or possibly multiple dicts or lists which can handle this problem differently.
I appreciate any input!
Thanks!
As you're using a function anyway, I would tend to use function syntax in this case rather than query syntax. Is it possible that there could be more than one match to your conditions? There are four similar methods, i.e. First, Single, FirstOrDefault and SingleOrDefault, and there is never a case where more than one is appropriate. The choice of which to use comes down to two simple questions:
Will there always be at least one match? If not then use one that ends with "OrDefault".
Will there ever be more than one match? If not then use one that starts with "Single".
The answers to those two questions will always tell you which of the four methods to call.
Now, you're using a Dictionary in this case, right? What are the keys? I would have thought NodeName would be but I guess not. Anyway, assuming that there will be zero or one matches to your conditions, you would use SingleOrDefault. The code for FirstOrDefault would look exactly the same anyway:
Dim item = myDictionary.Values.SingleOrDefault(Function(nl) nl.NodeName = nodenum AndAlso
nl.NodeCase = pattern AndAlso
nl.NodeDir = dirarray(d))
loadforce(d) = If(item Is Nothing, 0.0, item.NodeValue)
Notice two other corrections to your code: the proper use of AndAlso instead of And as well as the assignment of a Double value to loadforce(d) rather than a String if there is no match. The NodeValue property is type Double so how can you want a Double if there is a match and a String if there isn't?

How to access the object itself in With ... End With

Some code to illustrate my question:
With Test.AnObject
.Something = 1337
.AnotherThing = "Hello"
''// why can't I do this to pass the object itself:
Test2.Subroutine(.)
''// ... and is there an equivalent, other than repeating the object in With?
End With
There is no way to refer to the object referenced in the With statement, other than repeating the name of the object itself.
EDIT
If you really want to, you could modify your an object to return a reference to itself
Public Function Self() as TypeOfAnObject
Return Me
End Get
Then you could use the following code
With Test.AnObject
Test2.Subroutine(.Self())
End With
Finally, if you cannot modify the code for an object, you could (but not necessarily should) accomplish the same thing via an extension method. One generic solution is:
' Define in a Module
<Extension()>
Public Function Self(Of T)(target As T) As T
Return target
End Function
called like so:
Test2.Subroutine(.Self())
or
With 1
a = .Self() + 2 ' a now equals 3
End With
I suspect you'll have to repeat yourself. If the expression (to get the object) is expensive, then perhaps drop it into a variable first, and either use that variable in the With, or drop the With completely:
tmp = Test.AnObject;
tmp.Something = 1337;
...
Test2.Subroutine(tmp);
As others have said, you're going to have to write
Test2.Subroutine(Test.AnObject)
This is a good example of why it's worth being a little careful with the With construct in VB.Net. My view is that to make it worth using at all, you really need to be setting more than one or two properties, and/or calling more than one or two methods on the object in the With statement.
When there are lots, and you're not interspersing the .SomeProperty = , or .DoSomething, with other things, it's a terrific aid to readability.
Conversely, a few dots sprinkled amongst a load of other stuff is actually a lot harder to read than not using With at all.
In this case, . characters on their own could easily get lost visually, although of course, it would be syntactically consistent.
I guess they just chose not to implement it. VB isn't really the sort of language where they want to encourage single character language elements, and as a heavy user of VB.Net, I broadly agree with that.
Bottom line: if you're using a With clause with many contained elements, having to refer to the object itself isn't that big a deal. If you're using it with just one or two, maybe better not to use a With clause in the first place.
I'm not sure this is an "answer", per se, but it does illustrate another reason to want a short-hand reference to the parent in a With.
Here's a code sample using a "bare With" (that's what I call it, anyway):
With New frmMySubForm
.lblLinkLabel.Links.Add(New LinkLabel.Link With {.Name = "link", .LinkData = "someUrl", .Start = .lblLinkLabel.Text.IndexOf("link"), .Length = "link".Length})
...
End With
But you actually can't code that because in the term .Start = .lblLinkLabel.Text.IndexOf("link") the compiler expects anything starting with . to be a member of LinkLabel.Link, which .lblLinkLabel isn't.
What would be good, I think, is to be able to write something like:
With New frmMySubForm
.lblLinkLabel.Links.Add(New LinkLabel.Link With {.Name = "link", .LinkData = "someUrl", .Start = Me.lblLinkLabel.Text.IndexOf("link"), .Length = "link".Length})
...
End With
where Me in this scope is taken to be New frmMySubForm.
Yes, I realize that I'm being picky and I could easily assign a variable, etc. But the example form is something I use a lot simply out of preference.