Linqkit Generic Predicates with VB.NET - vb.net

I recently came across the wonderful Linqkit library and I want to make use of Generic Predicates to create a function for mapping users to the data they have access too accross any table that contains our data mapping fields. (H1L1, H1L2, etc)
Based on the tutorial (C# only) I see that this is indeed possible but I'm stuck.
So far I've created an interface:
Public Interface IDataMap
ReadOnly Property H1L1() As String
ReadOnly Property H1L2() As String
ReadOnly Property H1L3() As String
ReadOnly Property H2L1() As String
ReadOnly Property H2L2() As String
ReadOnly Property H2L3() As String
ReadOnly Property H3L1() As String
ReadOnly Property H3L2() As String
ReadOnly Property H3L3() As String
End Interface
Adjusted the Linq class for a table I'd like to operate on by adding
Implements IDataMap
and mapped each of the respective classes properties to the interface. I probably should have extended the linq class but for now i've just hardcoded the changes into the class generated by VS.
<Global.System.Data.Linq.Mapping.ColumnAttribute(Storage:="_H1L1", DbType:="VarChar(30)")> _
Public ReadOnly Property H1L1() As String Implements IDataMap.H1L1
Get
Return Me._H1L1
End Get
End Property
But I'm not sure where to go from here... or where to put this function so it's accessible from anywhere in my project. My test function is basic:
Public Shared Function mapUserToData(Of TEntity As IDataMap)(H1L1 As String) As Expression(Of Func(Of TEntity, Boolean))
Return Function(x) (H1L1 = x.H1L1))
End Function
Evenually I want to be able to say something similar to this:
DB.someTables.Where(someTable.mapUserToData("345BDS"))
The only way intellisense allows me to see that "mapUserToData" is available is if I put the function inside of my Linq Class... but then it's not generic. If I put the function inline in my code behind intellisense doesn't see my "mapUserToData" function as a method on my table. Maybe this is because of language/namespace differences between C# and VB.NET?
I'm a newbie to both .Net and Linq so please forgive me in advance for that.
I can use the linqkit predicate function successfully on an adhoc basis using
Dim predicate = PredicateBuilder.False(Of someTable)()
predicate = predicate.Or(Function(p) p.H1L1 IsNot Nothing)
Dim PgmED = (From x In DB.someTables.Where(predicate) Select x).AsEnumerable()
But can't afford to replicate the data mapping logic each time I need it. If anyone knows how to help I will be forever in their debt!

Try putting the mapUserToData function in a module as an Extension Method. Make it an extension of the IDataMap Interface.
<Extension()> _
Public Function mapUserToData(Of TEntity As IDataMap)(ByVal objTarget As IDataMap, H1L1 As String) As Expression(Of Func(Of TEntity, Boolean))
Return Function(x) (H1L1 = x.H1L1)
End Function

Related

From A Method With In The Class Return An Instance Of The Class As An Interface Type That The Class Implements

what I'm trying to archive with the code below is to have the GetInstance generic function take in an interface type that SystemVars implements (say IAuthentication) then create an instance of SystemVars and return it as interface type T.
The problem I an having is that no matter what casting method I try I can't find a way to return the new instance of SystemVars as T. The line in the GetInstance method Return <CastingFunction>(New SystemVars,T) always fails to compile with the error message saying Value of type SystemVars cannot be converted to 'T'.
How do I return the instance of the class as the interface type that was passed into T?
Imports System.Drawing
Public Class SystemVars
Implements IAuthentication,
IAuthorization,
IApplicationStarting
Private Sub New()
End Sub
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(New SystemVars, T)
End Function
Public ReadOnly Property Username As String _
Implements IAuthentication.Username,
IAuthorization.Username
Get
Return _userName
End Get
End Property
Public ReadOnly Property Rolls As List(Of String) _
Implements IAuthorization.Rolls
Get
Return _rolls
End Get
End Property
Public ReadOnly Property InstallationId As Guid _
Implements IAuthentication.InstallationId,
IApplicationStarting.InstallationId
Get
Return _installationId
End Get
End Property
Public ReadOnly Property MainWindowStartUpPlacement As Rectangle _
Implements IApplicationStarting.MainWindowStartUpPlacement
Get
Return _mainWindowStartUpPlacement
End Get
End Property
'........
Private Shared _userName As String
Private Shared _rolls As List(Of String)
Private Shared _installationId As Guid
Private Shared _mainWindowStartUpPlacement As Rectangle
End Class
You can make an otherwise illegal cast work by passing through Object.
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(CObj(New SystemVars), T)
End Function
You will get a runtime error if the cast isn't possible; as noted in the comments, this strategy is chucking type safety out the window and basically telling the compiler, "Don't bother me, I know what I'm doing." The runtime will throw an InvalidCastException on failure if you don't test and throw yourself. You can test using Type.IsAssignableFrom if you want to create a more developer-friendly error message; there isn't much context available in the debugger at the point of failure, though it may be pretty obvious if you look up the call stack.
For just three interfaces, it might be better to do three separate specific functions rather than a generic version, especially considering that the functions are necessarily Shared (and thus can't themselves be part of an interface).
You might also consider a design that includes a Dependency Injection container. In this kind of design, there would be a configuration step that would associate the interfaces with SystemVars as the implementation, then the client would ask the container for an instance of the interface and receive a SystemVars object.
The rough way that the three options (the third being to cast the SystemVars object to the requested interface) would look in code is:
'Casting a received object to a requested interface
Dim asInterface = DirectCast(SystemVars.GetInstance(), IAuthorization)
'Using a custom casting function on SystemVars
Dim asInterface = SystemVars.GetInstance(Of IAuthorization)
'Using a DI container
'Behavior if the interface isn't supported depends on the container
Dim asInterface = container.GetInstance(Of IAuthorization)
Note that TryCast could be used instead of DirectCast, in which case the result would be Nothing if the interface isn't supported.

How can I create a generic class that only works for classes that support certain interface?

Class cacheable(Of T As haveTimeStampandMainStringKey)
Public ReadOnly Property Cache As T
Public ReadOnly Property timestamp As Date
Public Shared Function create(cache1 As T) As cacheable(Of T)
Dim a = New cacheable(Of T)
a._Cache = cache1
a._timestamp = Now
Dim key = T.mainkey 'this things fail to compile
Return a
End Function
End Class
Interface haveTimeStampandMainStringKey
ReadOnly Property TimeStamp As DateTime
ReadOnly Property mainKey As String
End Interface
Basically I want class cacheable to work only with classes that support haveTimeStampandMainStringKey
Yet
Dim key = T.mainkey produces an error
Clearly T supports haveTimeStampandMainStringKey interface. So I should be able to access T.mainkey. I can't. Why? What's wrong with the code?
Why?
It doesn't work because T is a type, not an instance. You need to have an instance to refer to mainKey. You probably want either a.Cache.mainKey or cache1.mainKey.
(If you really want something Shared rather than something attached to an instance, unfortunately, there isn't a good way to do it as it's not supported by .NET except through various reflection-based approaches, see various lamentations about the absence of "static interfaces" over the years.)

Implement IEquatable Get distinct objects

This is not working for me. I couldn't find the answer on MSDN or elsewhere after having spent too much time on it. What am I missing?
Public Class PrinterInfo
Implements IEquatable(Of PrinterInfo)
Public PrinterName As String
Public PrinterDesc As String
'default equality comparer for class vb.net
Public Overloads Function Equals(ByVal other As PrinterInfo) As Boolean _
Implements IEquatable(Of PrinterInfo).Equals
Return other.PrinterName = Me.PrinterName
End Function
End Class
Public ReadOnly Property PrinterInfoList(ByVal Normal As NormalCopier) As List(Of PrinterInfo)
Get
Dim pList1 As List(Of PrinterInfo) = GetList
pList1.Sort()
Return pList1.Distinct.ToList
End Get
End Property
I get the list just fine but I want only distinct items. I tried to implement an equality comparer but it's not working. I'm getting multiple duplicates. What do I need to do to get only distinct values?
MSDN: Enumerable.Distinct(Of TSource)
MSDN: IEqualityComparer(Of T) Interface
This seems similar but I don't understand it
I'd like to avoid Linq GroupBy if I can. That just seems clumsy to me.
The documentation for Enumerable.Distinct(Of Source) says:
The default equality comparer, Default, is used to compare values of the types that implement the IEquatable<T> generic interface. To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.
That's the part you're missing. You are expected to provide a GetHashCode() implementation in your class. If you look at the code examples given, you'll see it there too. And when you think about it, it makes sense. The implementation of Distinct uses a hash set internally, so it naturally requires a proper GetHashCode implementation to function properly.
In your case, try adding this to your PrinterInfo class:
Public Overrides Function GetHashCode() As Integer
Return Me.PrinterName.GetHashCode()
End Function

Are generic operator overloads allowed in .NET 4?

I am assuming "No", but I cannot find conclusive proof on Google to back this assumption. Using keywords of 'vb.net "generic operator overload"' yields exactly 1 result, and removing 'overload' gives more, but no direct statement to the issue.
My thinking is given an abstract class, it'd be great to be able to implement a generic operator overload that a derived class can use in such a case when said operator overload has to return a New copy of the derived class, yet the code for each overload is the same. If that makes any sense.
This touches back to my previous questions on my custom Enum class and overloading the bitwise operators (And, Or, Not, & Xor), but, this particular thought was prompted by a mere curiosity of "Can it be done?".
Here's what one of my custom enums basically look like:
The parent, EBase is nothing special, just hosting common Name and Value properties, plus two shared operators, op_Equality and op_Inequality.
Friend NotInheritable Class EExample
Inherits EBase
Private Sub New()
End Sub
Friend Shared Function GetValue(ByVal Name As String) As Enums
Dim tmpOffset As Int32 = Array.IndexOf(_Names, Name)
Return If(HasContent(Name), If(tmpOffset <> -1, Values(tmpOffset), Nothing), Nothing)
End Function
' Num of Enums defined.
Friend Shared ReadOnly MaxEnums As Int32 = 5
' String literals.
Private Shared ReadOnly _Names As String() = New String() _
{"one_adam", "two_boy", "three_charles", "four_david", "five_edward"}
' Enums.
Friend Shared ReadOnly OneA As New Enums(_Names(0), 1)
Friend Shared ReadOnly TwoB As New Enums(_Names(1), 2)
Friend Shared ReadOnly ThreeC As New Enums(_Names(2), 4)
Friend Shared ReadOnly FourD As New Enums(_Names(3), 8)
Friend Shared ReadOnly FiveE As New Enums(_Names(4), 16)
' Enum Values Array.
Friend Shared ReadOnly Values As Enums() = New Enums() _
{OneA, TwoB, ThreeC, FourD, FiveE}
Friend NotInheritable Class Enums
Inherits EBase
Private Sub New()
End Sub
Friend Sub New(ByVal Name As String, ByVal Value As Int32)
MyBase.Name = Name
MyBase.Value = Value
End Sub
End Class
End Class
Here's how the things are used:
Dim Foo As EExample.Enums
Foo = EExample.TwoB
Debug.Print(Foo.Name)
will print two_boy
Now, given that, if I want to do the following:
Dim Foo as EExample.Enums
Foo = EExample.OneA Or EExample.FiveE
I have to define an operator overload for Or inside the EExample.Enums definition. How would this operator overload look?
Public Shared Operator Or(ByVal lhOp As Enums, ByVal rhOp As Enums) As Enums
Return New Enums(String.Concat(lhOp.Name, "|"c, rhOp.Name),
lhOp.Value Or rhOp.Value, True)
End Operator
I have to return a new EEXample.Enums object containing the Bitwise-Or'ed Value property of the parent EExample enums. For the name, I just concatenate the Name properties together with a pipe character until I think of something better.
Assume I have 20 enum classes similar to EExample. I have to duplicate all that operator overload code for each definition even though in the IDE, it looks the exact same. In IL, however, each overload is specific to the containing parent enum class:
.method public specialname static class MyAssembly.EExample/Enums
op_BitwiseOr(class MyAssembly.EExample/Enums lhOp,
class MyAssembly.EExample/Enums rhOp) cil managed
{ ... }
But! A generic operator overload would solve this problem if defined in EBase!
Friend Interface IEnums
Property Name As String
Property Value As Int32
End Interface
Public Shared Operator Or(Of T As IEnums)(ByVal lhOp As T, ByVal rhOp As T) As T
Return New T(String.Concat(lhOp.Name, "|"c, rhOp.Name),
lhOp.Value Or rhOp.Value, True)
End Operator
Then (in theory anyways), calling EExample.OneA Or EExample.FiveE would work because the compiler would know to call the generic operator overload from EBase, know that EExample.Enums matches the IEnums interface constraint, and automatically supply T.
That or I'm just swimming up a certain creek here without a paddle and over-analyzing things. But it's an interesting thought, no? What is StackOverflow's consensus? Do I need to lay off the Spice a little bit?
PS: I know that, in the last example, Return New T( ... ) is invalid, but I can't think of a proper syntax that would articulate the basic idea.
According to what I can see in the language specification, generic operators are not allowed. Section 9.8 says
The type of at least one of the operands or the return value must be the type that contains the operator.
and later when it describes the declaration syntax makes no accounting for a generic specifier as methods do in section 9.2.1.
Found a "workable" solution myself.
For the top-level EBase, I exposed the interface (IEnumBase) as a Friend, then created generic methods in EBase to handle the overload operators:
Protected Shared Function _
op_BitwiseOr(Of T As {IEnumBase, Class})(ByVal lhOp As T, ByVal rhOp As T, ByVal RetEnum As T) As T
RetEnum.Name = String.Concat(lhOp.Name, "|"c, rhOp.Name)
RetEnum.Value = (lhOp.Value Or rhOp.Value)
Return RetEnum
End Function
The trick here, is the generic method simply returns RetEnum back to the caller. In the derived Enums (i.e., EExample), I have:
Public Shared Shadows Operator Or(ByVal lhOp As Enums, ByVal rhOp As Enums) As Enums
Return EBase.op_BitwiseOr(lhOp, rhOp, New Enums)
End Operator
This allows me to keep the bulkier code defined once in EBase, and not replicated each time in my many derived enum classes. Those enum classes simply call on the parent's implementation and use generics to pass-in their sub-defined Enums implementation!
Yeah, not groundbreaking. I could do better, but this works well enough for my needs and doesn't over-inflate the codebase too much. It also reduces code duplication and technically makes maintenance easier, IMHO.
Still leaving Gideon Engelberth's answer as the accepted answer, however. My question initially asked if overloaded operators could be genericized, and he found the snippet on MSDN that says they can't.

Weird VB.NET array-property situation

I have this weird situation.
I have these two classes:
Public Class Entry
End Class
Public Class Core
End Class
One of the properties of the Core class will be an array of Entry objects. How can I declare it?
Now, the only way to change (add/remove) this array from outside should be using two functions - AddEntry(Ent As Entry) and RemoveEntry(ID As String). Note that here, whoever is calling the AddEntry function should only be bothered with creating an Entry object and passing it. It will be added to the existing array.
But, the Entry array should be accessible like this from outside, for looping through and printing or whatever like this:
' core1 is a valid Core object
For Each Ent As Entry In core1.Entries
MsgBox(Ent.SomeProperty)
Next Ent
Is it possible to expose the Array as a property but restrict modification through functions alone? I know that the logic inside the Add and Remove functions can be inside the setter or getter, but the person wanting to add should pass only a single Entry object.
It is like saying You have readonly access to the array, but for modifying it, just create an object and send it or the ID to remove it. Don't bother about the entire array.
I hope I am making sense.
Why do you want to expose it as an array ?
What I would do, is use a List internally to store the entries. (That List would be private)
Create the necessary public methods (AddEntry / RemoveEntry / ... ), which manipulate the private list.
Then, create a public property which exposes the List, but in a ReadOnly fashion. That is, that property should return an ReadOnlyCollection instance.
Like this:
(I know it is in C#, but that 's my 'main language' - a bit too lazy to convert it to VB.NET)
public class Core
{
private List<Entry> _entries = new List<Entry>();
public void AddEntry( Entry entry )
{
_entries.Add (entry);
}
public ReadOnlyCollection<Entry> Entries
{
get { return _entries.AsReadOnly(); }
}
}
EDIT: VB.Net version provided by MarkJ
Imports System.Collections.ObjectModel
Public Class Core
Private _entries As New List(Of Entry)
Public Sub AddEntry( new As Entry )
_entries.Add (new)
End Sub
Public ReadOnly Property Entries() As ReadOnlyCollection(Of Entry)
Get
Return _entries.AsReadOnly
End Get
End Property
End Class
Create a private field for the array and then create your accessing methods to work with the array internally. In order to expose this array to callers so that they can enumerate it you should expose a property of type IEnumerable(Of T).
This approach is not foolproof, however as a clever caller could simply cast the IEnumerable(Of T) back to an array and modify it so it may be necessary to create a copy of the array and return that as the IEnumerable(Of T). All this has obvious performance penalties as I am sure you already see. This is just one of many issues that can arise when arrays are used as underlying data structures.
You can keep the List private and instead return an IEnumerable. Code generated via Reflector - I hope it's readable:
Public Class Core
Public Sub AddEntry(ByVal Ent As Entry)
Me.entries.Add(Ent)
End Sub
Public Sub RemoveEntry(ByVal ID As String)
Dim pred As Predicate(Of Entry) = Function (ByVal entry As Entry)
Return (entry.Id = ID)
End Function
Me.entries.RemoveAll(pred)
End Sub
Public ReadOnly Property Entries As IEnumerable(Of Entry)
Get
Return Me.entries
End Get
End Property
Private entries As List(Of Entry) = New List(Of Entry)
End Class
Note: I'd recommend using a List<Entry> instead of an array if you'll be adding and removing objects - or perhaps even a Dictionary<string, Entry> given the way you are using it.