Mapping relationships across Tables (Entity Framework) From ICollections (vb.NET) - vb.net

I'm working on a huge project here and I'll try to simplify the question as much as I can.
It is Basically a DLL which saves the rights of each user to a Database and returns it to the application, this whole thing using Entity Framework.
Here the Classes, I'll not insert all of them, but just two that create a n:m Relashionship:
Public Class Group
Inherits Object
<Key>
Public Property GroupID As Long
<Required>
Public Property GroupName As String
Public Overridable Property Rights As ICollection(Of Rights) = New HashSet(Of Rights)
And here the other one:
Public Class Rights
<Key>
Public Property ID As Long
<Required>
Public Property Number As Integer
<Required>
Public Property Name As String
Public Property Description As String
Public Overridable Property Groups As ICollection(Of Groups) = New HashSet(Of Groups)
So far so good. This whole data comes from *.csv Files, which I read in as DataTable, return it to another DLL which takes the DataTable, makes objects from each Class and returns the Objects as an ICollection, this works fine as well.
Here the two functions from this classes, so the two functions that take the data From the DataTable, makes objects out of it and return it as ICollection:
Public Function ReturnAllGroups(ByVal dt As DataTable) As ICollection(Of Group) Implements IInfoServiceUtil.ReturnAllGroups
If dt Is Nothing Then
Throw New ArgumentNullException("DataTable can't be nothing", "dt")
End If
If Not dt.Columns.Contains("Profile") Then
Throw New ArgumentException("Couldn't find Column Profile")
End If
Dim profilNames = From row In dt.AsEnumerable()
Select row.Field(Of String)("Profile") Distinct
Dim coll As ICollection(Of Group) = New List(Of Group)
For Each name In profilNames
If name <> Nothing Then
Dim group As Group= New Group()
groupg.GroupName = name
coll.Add(group)
End If
Next
Return coll
End Function
And here the other onde:
Public Function ReturnAllRights(ByVal dt As DataTable) As ICollection(Of Rights) Implements IInfoServiceUtil.ReturnAllRights
If dt Is Nothing Then
Throw New ArgumentNullException("DataTable can't be nothing!", "dt")
End If
If Not dt.Columns.Contains("RightName") Then
Throw New ArgumentException("Couldn't find column RightName!")
End If
If Not dt.Columns.Contains("RightNumber") Then
Throw New ArgumentException("Couldn't find column RightNumber!")
End If
Dim query = From dr As DataRow In dt.DefaultView.ToTable(True, "RightName", "RightNumber")
Dim coll As ICollection(Of Rights) = New List(Of Rights)
For Each row In query
Dim right As Rights = New Rights()
If IsNumeric(row.Item("RightNumber")) Then
With right
.Name = row.Item("RightName")
.Number= row.Item("RightNumber")
coll.Add(right)
End With
End If
Next
Return coll
End Function
This works fine as well. And in this part I'm stucked, coz I'm not sure how I can make the association Table from the Group and Rights Tables.
It would be a Function like this:
Public Function CreateAssociation(group As ICollection(Of Groups), right As ICollection(Of Rights), dt As DataTable) As ICollection(Of Group)
'TODO
Return coll
End Function
Sorry if it's a long question, but any help would be much appreciated. I have more Tables as well, but as soon as I know how to do one, the others will work just fine.
Thanks a lot!

Related

How to create a Boolean Function to list objects in a Generic Collection?

After creating a List(Of T), I want to create aBoolean function. First, we will ask data to add an object in the list. However, in case this new object has the same "DNI" (String attribute from the class Aspirante), then we cannot include this new Object in the list. Therefore, it should be True when we have an Object with the same attribute and False when we don´t, so we can add the new object.
Below is the code I did:
Public Class Oposicion
Private datos As New List(Of Aspirante)()
Public Function Alta(ByRef objAspirante As Aspirante) As Boolean
If datos.Contains(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante)
Return False
End If
End Function
End Class
However it doesn´t work. I have no clue on how to do it. Sorry if I was not clear enough.
This doesn't answer your question directly but it involves a significant amount of code, so it won't work in a comment.
You probably shouldn't be using a List(Of T) in the first place. The HashSet(Of T) already includes functionality to prevent adding duplicate items, so that may be a better option. If you want to compare objects on a specific property value then you need to first create a comparer based on that:
Public Class Thing
Public Property Stuff As String
End Class
Public Class ThingComparer
Implements IEqualityComparer(Of Thing)
Public Overloads Function Equals(x As Thing, y As Thing) As Boolean Implements IEqualityComparer(Of Thing).Equals
Return x.Stuff.Equals(y.Stuff)
End Function
Public Overloads Function GetHashCode(obj As Thing) As Integer Implements IEqualityComparer(Of Thing).GetHashCode
Return obj.GetHashCode()
End Function
End Class
You then create a HashSet(Of T) that uses that comparer to determine equality:
Dim things As New HashSet(Of Thing)(New ThingComparer)
You can then just add items as you please by calling Add. That will either add the new item and return True or it will not not add the duplicate item and return False:
Dim variousStuff = {"One", "Two", "One"}
For Each stuff In variousStuff
Dim something As New Thing With {.Stuff = stuff}
If things.Add(something) Then
Console.WriteLine($"'{stuff}' was added successfully.")
Else
Console.WriteLine($"'{stuff}' is a duplicate and was not added.")
End If
Next
The potential drawback is that HasSet(Of T) does not implement IList(Of T), so you cannot access items by index. It does implement ICollection(OF T) though, so it does have a Count property and you can enumerate it with a For Each loop.
You can create a List(Of String) containing the just the DNI property of each object in datos. Then see if the DNI of objAspirante is contained in lst.
Public Class Oposicion
Private datos As New List(Of Aspirante)()
Public Function Alta(objAspirante As Aspirante) As Boolean
Dim lst As New List(Of String)
For Each a As Aspirante In datos
lst.Add(a.DNI)
Next
If lst.Contains(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante)
Return False
End If
End Function
End Class
If you can change the type of datos, this might be easier.
Public Class Oposicion
Private datos As New Dictionary(Of String, Aspirante)()
Public Function Alta(objAspirante As Aspirante) As Boolean
If datos.ContainsKey(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante.DNI, objAspirante)
Return False
End If
End Function
End Class
If you want to stick with your existing List(Of Aspirante), then simply use .Any() and pass it a Lambda to determine if one already exists. It'd look something like this:
Public Function Alta(ByVal objAspirante As Aspirante) As Boolean
If Not datos.Any(Function(x) x.DNI = objAspirante.DNI) Then
datos.Add(objAspirante)
Return True ' your description and code did not match for these!
End If
Return False ' your description and code did not match for these!
End Function
Note my comment on the Return lines...your code and description did not match here.

VB.NET Serealize structure that contains lists

I'm searching a way to serialize a structure that contains different kind of object such as Strings, Integer and Collections.
I have this data structure:
Dim database as New mainStruct
<Serializable()> Structure mainStruct
Public name As String
Public address As String
Public Shared bookings As IList(Of booking) = New List(Of booking)
End Structure
<Serializable()> Structure booking
Public id As Integer
Public category As String
Public description As String
End Structure
running the serializer:
Dim fs As FileStream = New FileStream("x.bin", FileMode.OpenOrCreate)
Dim serial As New XmlSerializer(GetType(mainStruct))
serial.Serialize(fs, database)
fs.Close()
the output only contains all non-collection variables such as:
<?xml version="1.0"?>
<mainStruct xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<name>foo</name>
<address>bar</address>
</mainStruct>
How i can serialize bookings Collection, without creating separate files using GetType(List(Of booking))?
Thanks!
Shared members are not serialized because serialization is all about saving a class/structure instance, and shared members are not part of an instance.
Add an instance property to your structure and you should be good to go:
Public Property bookingList As IList(Of booking)
Get
Return mainStruct.bookings
End Get
Set(value As IList(Of booking)
mainStruct.bookings = value
End Set
End Property
If you want the tag to be called <bookings> you can just apply an XmlArray attribute to the property:
<XmlArray("bookings")> _
Public Property bookingList As IList(Of booking)
...same code as above...
End Property

For a combobox with item = a datarow, how to point ValueMember to one of it's columns

I add items to a combobox like this:
For each R as DataRow in MyDataTable.Rows
If R("ID") > 10 then MyCombo.Items.Add(R)
Next
And now I need to set the DisplayMember and ValueMember to a column of the datarow:
MyCombo.ValueMember = R("ID")
MyCombo.DisplayMember = R("Name")
I know it doesn't make sence to to use "R" as it doesn't reference to anything at this point but it's just to make an indication of what I mean ;-)
The documentation for ValueMember says:
"A String representing a single property name of the DataSource property value, or a hierarchy of period-delimited property names that resolves to a property name of the final data-bound object"
I know I can add the rows to a new datatable and set it to the DataSource, but as you can add any object to the combobox items, it would be nice to use the rows directly, just can't figures out how to make a reference the particular column as a string.?
Maybe you cannot use a row object directly. I guess to use Valuemember you need your item objects to be wrapped in a collection which implement an ilist interface.
In the old MS-Access days combobox items had natively Display- and ValueMember properties, I've always missed that in the .Net combobox control.
My work-around is to use this class, which then can be used for all your ComboBoxes:
Class oComboItems
Public items As New List(Of oDVpairs)
Class oDVpairs
Implements IComparable(Of oDVpairs)
Private myDM As String
Private myVM As Object
Sub New(DM As String, VM As Object)
myDM = DM
myVM = VM
End Sub
Public ReadOnly Property DM() As String
Get
Return myDM
End Get
End Property
Public ReadOnly Property VM() As Object
Get
Return myVM
End Get
End Property
Public Function CompareTo(other As oDVpairs) As Integer Implements IComparable(Of oDVpairs).CompareTo
Return Me.myDM.CompareTo(other.myDM)
End Function
End Class
Public Sub AddItems(DisplayMember As String, ValueMemeber As Object)
items.Add(New oDVpairs(DisplayMember, ValueMemeber))
End Sub
Public ReadOnly Property DisplayMember() As String
Get
Return "DM"
End Get
End Property
Public ReadOnly Property ValueMember() As Object
Get
Return "VM"
End Get
End Property
End Class
And now add my datarows(or any other objects) to the ComboBox:
Dim CI As New oComboItems
For Each R As DataRow In DT_U.Rows
If R("medlnr") > 10 Then
CI.AddItems(R("name"), R("ID"))
end if
Next
CI.items.Sort()
MyCombo.DataSource = CI.Items
MyCombo.DisplayMember = CI.DisplayMember
MyCombo.ValueMember = CI.ValueMember

List (Of T) as DataGridView.DataSource makes sorting fail

I have read some threads about this "error" but I can't figure out how to solve my problem.
I have a class that looks something like this:
Public Class Person
Public Property Name As String
Public Property PhoneNumber As string
Public Property Age As Integer
Public sub New(ByVal Values As String())
Me.Name = Values(0)
Me.PhoneNumber = Values(1)
Me.Age = Convert.ToInt32(Values(2))
End Sub
End Class
I get my data from a semicolon separated file, and i create a list of Person objects by looping this file and split on semicolon. Like this
Dim PersonsList As New List(Of Person)
For Each line in textfile..........
PersonsList.Add(New Person(line.Split(";")))
Next
When the list is complete, I tell my DataGridView that DataSource is PersonsList.
This works like a charm, but I'm not able to sort the columns.
I found this post amongst many (where the class values are not properties, which mine are) and tried that converting function which did'nt really work in my case. The right amount of rows were created, but all of the columns were blank.
What am I missing?
If you use a datatable as the data source, column sorting is automatically enabled and you can sort the data by any column:
Dim dt As New DataTable
dt.Columns.AddRange(
{
New DataColumn("Name"),
New DataColumn("Phone"),
New DataColumn("Age")
})
For Each s As String In IO.File.ReadAllLines("textfile1.txt")
Dim temprow As DataRow = dt.NewRow
temprow.ItemArray = s.Split(";"c)
dt.Rows.Add(temprow)
Next
DataGridView1.DataSource = dt

NullReferenceException adding to list in parent class

I am working on an application to add users to groups in Active Directory, with two classes: ADUser and Student, which inherits from ADUser.
public class ADUser
property UserID as string
property MemberOf as new List(Of String)
(etc)
end class
public class Student inherits ADUser
property subjects as new list(Of Subject) 'Subject is a class of multiple strings
end class
I am attempting to copy the subject name from the subjects list to the MemberOf list, which results in a NullReferenceException (Object reference not set to an instance of an object). The following code generates an error on the me.MemberOf.Add line:
Function ConvertSubjectsToGroupMembership() As Boolean
Try
For Each s As Subject In Subjects
Me.MemberOf.Add(String.Format("t.{0}", s.ShortName).ToString)
Next
Return True
Catch ex As Exception
Return False
End Try
End Function
My understanding of inheritance is that the MemberOf list should be created when the Student object is created, however this does not seem to be the case.
Any assistance is appreciated.
Edit:
I managed to work around the issue by modifying the function to create a list(of String), which is then copied to the MemberOf property:
Function ConvertSubjectsToGroupMembership() As Boolean
Dim SubjectShortNameList As New List(Of String)
For Each s As Subject In Subjects
SubjectShortNameList.Add(String.Format("t.{0}", s.ShortName).ToString)
Next
Try
MemberOf = SubjectShortNameList
Return True
Catch ex As Exception
Return False
End Try
End Function
It works, but I'm sure there's a better way to do it. I look forward to reading the answers/opinions of others!
It looks like you are nulling out MemberOf somewhere else in code. Assuming ConvertSubjectsToGroupMembership is a function on Student I ran the code and it worked fine. You could try expanding the property so you can set a breakpoint. This will allow you to find out where the Null is being introduced.
Public Class ADUser
Property UserID As String
Private _MemberOf As New List(Of String)
Public Property MemberOF As List(Of String)
Get
Return _MemberOf
End Get
Set(ByVal value As List(Of String))
_MemberOf = value ***'breakpoint here***
End Set
End Property
End Class