Weird VB.NET array-property situation - vb.net

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.

Related

How should I serialize a vb.NET object which has class attributes with JsonConvert?

I am trying to use Json.NET to serialize an object in vb.NET.
The object fails to serialize correctly. This seems to be because of some attributes on the class.
Below is the class definition – a very simple definition.
Here is the code to serialize:
Dim MyObject As New TestClass() With {.Property1 = "Hello", .Property2 = 3}
Dim Serialized As String = JsonConvert.SerializeObject(MyObject)
After the 2 lines above execute, the variable Serialized has the following value (which is not what I would expect or want):
"Namespace1.TestClass"
When I remove the class attributes completely (just the class attributes, not the property attributes) and then execute the same 2 lines of code, the variable Serialized has the following value (which is what I would expect):
{"Property1":"Hello","Property2":"3"}
This is only an example: we have many such classes with these kinds of attributes. We need to serialize them with Json.NET.
Removing the attributes is not possible, the classes I am dealing with are part of a system of existing applications and WCF based web services (i.e. part of our system needs our current serialization system for WCF kept in place, and another part of our system needs to serialize the same class with Json .. I won't go into the "why" details, just that we are serializing thousands of such objects for database writes and have speed and space isues).
I realize also that I can use JsonTextWriter to serialize, but then we have maintenance issues – every time we add/remove a property we have to remember to maintain the serialization code appropriately.
So how must I serialize this class correctly without removing the attributes?
I have not seen anything on the NewtonSoft site, nor anywhere else, which addresses this specific problem.
Here again is the class definition, along with the attributes.
<System.CodeDom.Compiler.GeneratedCodeAttribute ("System.Xml", "2.0.50727.3053"), _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute ([Namespace]:="http://Namespace.com/SomePath/SomeXsd.xsd", TypeName:="TestClass"), _
System.ComponentModel.TypeConverterAttribute(GetType (System.ComponentModel.ExpandableObjectConverter))> _
Partial Public Class TestClass
Private _Property1 As String
Private _Property2 As Integer
<System.ComponentModel.Browsable(False), System.Xml.Serialization.XmlIgnoreAttribute()> _
Public Property Property1() As String
Get
Return Me._Property1
End Get
Set(ByVal value As String)
If (Me._Property1 <> value) Then
Me._Property1 = value
End If
End Set
End Property
<System.ComponentModel.Browsable(False), System.Xml.Serialization.XmlIgnoreAttribute()> _
Public Property Property2() As String
Get
Return Me._Property2
End Get
Set(ByVal value As String)
If (Me._Property2 <> value) Then
Me._Property2 = value
End If
End Set
End Property
End Class
The problem is the TypeConverterAttribute on your class. When Json.Net sees that, it will use the associated TypeConverter to convert the object to a string. In this case, it results in the class's type name being output.
You can override the unwanted behavior by adding a JsonObjectAttribute to the classes that have a TypeConverterAttribute applied. But since it appears that your classes are generated code, it might not be feasible to do that on an class-by-class basis unless you can modify the code generator. In that case, another alternative is to use a custom IContractResolver to force Json.Net to ignore the TypeConverter on classes that have it.
Here is the code you would need for the resolver:
Class TypeConverterIgnoringResolver
Inherits DefaultContractResolver
Protected Overrides Function CreateContract(objectType As Type) As JsonContract
If objectType.GetCustomAttributes(True) _
.OfType(Of System.ComponentModel.TypeConverterAttribute)() _
.Any() _
Then
Return MyBase.CreateObjectContract(objectType)
End If
Return MyBase.CreateContract(objectType)
End Function
End Class
You can use the resolver like this:
Dim MyObject As New TestClass() With {.Property1 = "Hello", .Property2 = 3}
Dim Settings As New JsonSerializerSettings
Settings.ContractResolver = New TypeConverterIgnoringResolver()
Dim Serialized As String = JsonConvert.SerializeObject(MyObject, Settings)
Console.WriteLine(Serialized)
Fiddle: https://dotnetfiddle.net/s6Ebmc
I guess you don't want to change your TestClass as it must have been generated by some tool, I would suggest to derive a new class from it:
Imports Newtonsoft.Json
<JsonObject()>
Public Class OtherClass
Inherits TestClass
End Class
and use the attribute <JsonObject()>. This should do the trick:
Dim MyObject As New OtherClass() With {.Property1 = "Hello", .Property2 = 3}
Dim Serialized As String = JsonConvert.SerializeObject(MyObject)
UPDATE:
Since you're already working with a Partial you can extend it creating a new one (in another folder):
Imports Newtonsoft.Json
<JsonObject()>
Partial Public Class TestClass
End Class

Pass an argument to a generic type New constructor in VB.Net Generics

I'm trying to be able to pass a Type parameter to a function called ConvertList, and have that function create some instances of the specified type. So, if I passed in type Foo, the function would create new objects of type Foo and put the created objects into a custom List object (SLMR_OBjList).
The function is in a generic class that is defined:
Public Class BOIS_Collection_Base(Of T)
The function would accept types other than what is passed in the class definition. So, if we create an instance of BOIS_Collection_Base(Of MyTypeA) we may call the function ConvertList(Of MyTypeB).
I want the private variable _convertedList to be of a different type than the class. Is this possible? I can only seem to define it with (Of T).
Here is what I have so far:
Public Class BOIS_Collection_Base(Of T)
Private _convertedList As SLMR_ObjList(Of T) ' I can only seem to define this as (Of T), but want to make sure I can pass in a Type other than the Base(Of T)
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of T) ' Should this be (Of T) or (Of myT) since I want it to use whatever Type is passed in
For Each tempVar In Me.ObjList
Dim newitem As myT = Activator.CreateInstance(GetType(myT), tempVar)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
Here is what I would like to be able to do:
Dim mainCollInstance As New BOIS_Collection_Base(Of MyTypeA)
....
'Code that populates the BOIS_Collection_Base.ObjList property with an SLMR_ObjList(Of MyTypeA)
....
' Now I want to take that ObjList, and cast all the items in it to MyTypeB
Dim newListObj As SLMR_ObjList(Of MyTypeB) = mainCollInstance.ConvertList(Of MyTypeB)
Is this possible? Am I going about it wrong?
In response to Plutonix:
If I define _convertedList inside the method, like this:
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of myT)
Dim _convertedList = New SLMR_ObjList(Of myT)
my errors go away, and the method does what I want, but _convertedList is no longer persistant in the object.
If you want to persist the list, then you can't really allow the consuming code to pass a different type for the list each time. That doesn't really make much sense, unless each time it's called, you only want the function to return the portion of the persisted list which contains objects of the given type. If that's the case, then you just need to declare _convertedList As SLMR_ObjList(Of Object) and then filter it and convert it to the correct type as necessary.
If, however, as I suspect is the case, the consumer will always be requesting that it be converted to the same type each time the function is called, then that output type is not really a property of the function call. Rather, it's a property of the whole class. In that case, you should make your class take two generic type arguments, like this:
Public Class BOIS_Collection_Base(Of T, TOut)
Private _convertedList As SLMR_ObjList(Of TOut)
Public Function ConvertedObjList() As SLMR_ObjList(Of TOut)
For Each tempVar As T In Me.ObjList
Dim newitem As TOut = DirectCast(Activator.CreateInstance(GetType(TOut), tempVar), TOut)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
End Class
Based on the previous related question and an assumption that MyTypeA and MyTypeB inherit from the same class (never got an answer), you may not need Generics for this. At any rate, this should help with the ctor part of the question. I do not as yet see where Generics fit in since inheritance may do what you want already:
Class MustInherit BiosItem
Public Property Name As String
Public Property TypeCode As String
...
MustOverride Function Foo(args...) As Type
Overridable Property FooBar As String
' etc - the more stuff in the base class the better
End Class
Class TypeA
Inherits ABClass
Public Sub New
MyBase.New ' stuff common to all child types
TypeCode = "A" ' EZ type ID rather than GetType
...
End Sub
End Class
Class TypeB would be the same, but initialize TypeCode to "B". The same for C-Z. These allow you to poll the object rather than needing GetType: If thisObj.TypeCode = "A" Then.... Now, the collection class:
Public Class BIOSItems
Inherits Collection(Of BiosItem)
' inheriting from Collection<T> provides Add, Count, IndexOf for us
' most important the Items collection
'
End Class
Typing the collection as BiosItem will allow TypeA or TypeJ or TypeQ in it. As is, your collection will hold one Type only as it should be. This works because an item which is GetType(TypeA) is also GetType(BiosItem). See also note at the end.
Converting one item to another would seem to be something that would largely be handled by the NEW item being created or converted to. Since they are likely to be very similar then it can be handled by a constructor overload (if they are not similar, well we are well down the wrong road):
' a ctor overload to create the new thing based on the old things props
Public Sub New(oldThing As BiosItem)
MyClass.New ' start with basics like TypeCode, MyBase.New
With BiosItem ' coversion
myFoo = .Foo
myBar = .Bar ' copy common prop vals to self
...
Select Case .TypeCode
Case "B"
myProp1 = .Prop33 ' conversions
myProp3 = .Prop16 + 3.14
...
End Select
' then initialize stuff unique to this type maybe
' based on other props
If .PropX = "FooBar" Then myPropZ = "Ziggy"
End With
End Sub
Code to create, convert, store:
Dim varOldBItem As TypeB = myBiosCol(ndx) ' get old item
Dim varAItem As New TypeA(varOldBItem) ' call the ctor above
myBiosCol.Add(varAItem) ' add new item
myBiosCol.Remove(varoldBItem) ' delete the old if need be
If BOIS_Collection_Base is always supposed to contain MyTypeA, then type it that way (inheriting from Collection<T> still seems in order). If also MyTypeB objects are never added to the collection directly, but converted to MyTypeA first (Edit makes that less clear), then most of the above still applies, except for the inheritance. A ctor overload on MyTypeA could still take an old B object and create itself based on it. I'd be less inclined to do it via the ctor if they do not inherit from the same base class, but it could be done.

Make static member persistent

I have one class with a private static (shared, since I'm in VB.BET) field and its associated public static property, since it stores one variable that should be the same to all the instances of this class.
My Class looks like this:
Public MustInherit Class NitrogenController
Private _active As Boolean
Private Shared _controlInterval As TimeSpan
Private _lastControlTime As Date
Public Property Active() As Boolean
Public Shared Property ControlInterval() As System.TimeSpan
'other properies that must be persisted
Public Function Control() As Boolean
If Not Now > _lastControlTime.Add(_controlInterval) Or Not _active Then
Return False
Else
DoControl()
_lastControlTime = Now
Return True
End If
End Function
End Class
The problem arrives when trying to binary serialize these kind of objects, since this shared field is nos being properly stored and returns to its default value when deserializing.
I suppose this is the expected behaviour, so my question is... how can I make a shared field persistent? I have read some comments to similar questions that say that this is a bad design, but it really makes sense (AFAIK) in my case, since this variable should be the same to all the object, but can be changed by the user and therefore should be stored.
Can you suggest another way of doing it?
Thanks!
What you have read, in my opinion, is correct. This is, likely, a bad design. However, if you must, there are two ways to do this with the XmlSerializer. The easy way would be to simply add a public instance (non-shared) property which has a getter and setter which simply wrap the shared property, for instance:
Public MustInherit Class NitrogenController
Public Shared Property ControlInterval As TimeSpan
Public Property CurrentControlInterval() As TimeSpan
Get
Return ControlInterval
End Get
Set(value As TimeSpan)
ControlInterval = value
End Set
End Property
End Class
If you aren't satisfied with that method, the second, more involved, option would be to override the default serialization logic by implementing the ISerializable interface.

Linqkit Generic Predicates with 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

Why is this Entity Framework association not loading lazily?

I'm using a Code First Entity Framework approach, and in my OnModelCreating function I have the following code:
With modelBuilder.Entity(Of FS_Item)()
.HasKey(Function(e) e.ItemKey)
.Property(Function(e) e.ItemRowVersion).IsConcurrencyToken()
.HasMany(Function(e) e.ItemInventories) _
.WithRequired(Function(e) e.Item).HasForeignKey(Function(e) e.ItemKey)
End With
Elsewhere I have a Web API Get implementation with some diagnostic code I'm looking at in a debugger:
Public Function GetValue(ByVal id As String) As FS_Item
GetValue = If(data.FS_Item.Where(Function(i) i.ItemNumber = id).SingleOrDefault(), New FS_Item())
Dim c = GetValue.ItemInventories.Count
End Function
I expect that c should get a non-zero value by looking up rows in the FS_Inventory view where ItemKey matches the retrieved FS_Item row's ItemKey. But I'm getting 0 even though there are matching rows. Am I calling .HasMany, .WithRequired and .HasForeignKey properly?
Note that .WithRequired is operating on the return value from the previous line whereas the other lines are operating on the With block expression.
Edit This model for FS_Item has been requested. Here it is:
Partial Public Class FS_Item
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUM As String
Public Property ItemRevision As String
Public Property MakeBuyCode As String
' Many many more properties
Public Property ItemRowVersion As Byte()
Public Property ItemKey As Integer
Private _ItemInventories As ICollection(Of FS_ItemInventory) = New HashSet(Of FS_ItemInventory)
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
Get
Return _ItemInventories
End Get
Friend Set(value As ICollection(Of FS_ItemInventory))
_ItemInventories = value
End Set
End Property
End Class
Edit Learned something interesting. If I change Dim c = GetValue.ItemInventories.Count to this:
Dim c = data.FS_ItemInventory.ToList()
Dim correctCount = GetValue.ItemInventories.Count
Then correctCount gets the value of 3. It's like it understands the association between the objects, but not how to automatically query them as I'm used to coming from LINQ-to-SQL. Is EF different somehow in this regard?
Edit I have determined that I can make the associated objects load using this explicit loading code:
data.Entry(GetValue).Collection(Function(e) e.ItemInventories).Load()
What I want to understand now is what exactly determines whether an entity will load lazily or not? From all indications I can find, it should have loaded lazily. I even tried changing the declaration of ItemInventories to this, but then I got a NullReferenceException when trying to access it:
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
It turns out that code which I thought was unrelated had disabled lazy loading. I have this in the constructor of FSDB:
DirectCast(Me, IObjectContextAdapter).ObjectContext.ContextOptions.ProxyCreationEnabled = False
Thanks to EF 4 - Lazy Loading Without Proxies I see that this will also disable lazy loading. The reason that code had been added was due to another error:
Type
'System.Data.Entity.DynamicProxies.FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418'
with data contract name
'FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
And according to Serialization of Entity Framework objects with One to Many Relationship, the easy solution for that was to disable proxies.