How to approach refactoring a class? (VB.Net) - vb.net

I recently started a project and needed to do some integration with LDAP via DirectoryServices. I've done this in other apps so I went into one of them to see how I did it -- why reinvent the wheel right? Well, while this wheel works, it was developed years ago and kinda smells (it's wooden, firmly attached to the previous vehicle, hard to repair and produces a potentially bumpy ride).
So I thought to myself, this is the perfect time to refactor this puppy and make it more portable, reusable, reliable, easier to configure etc. Now that's fine and dandy, but then I started feeling a tad overwhelmed regarding to where to start. Should it be a separate library? How should it be configured? Should it use IoC? DI?
So my [admittedly subjective] question is this -- given a relatively small, but quite useful class like the one below, what is a good approach to refactoring it? What questions do you ask and how do you decide what to implement or not implement? Where do you draw the line regarding configuration flexibility?
[Note: Please don't bash this code too much okay? It was written a long time ago and has functioned perfectly well baked into an in house application.]
Public Class AccessControl
Public Shared Function AuthenticateUser(ByVal id As String, ByVal password As String) As Boolean
Dim path As String = GetUserPath(id)
If path IsNot Nothing Then
Dim username As String = path.Split("/")(3)
Dim userRoot As DirectoryEntry = New DirectoryEntry(path, username, password, AuthenticationTypes.None)
Try
userRoot.RefreshCache()
Return True
Catch ex As Exception
Return False
End Try
Else
Return False
End If
End Function
Private Shared Function GetUserPath(ByVal id As String) As String
Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher
Dim results As SearchResultCollection
Dim result As SearchResult
Try
With searcher
.SearchRoot = root
.PropertiesToLoad.Add("cn")
.Filter = String.Format("cn={0}", id)
results = .FindAll()
End With
If results.Count > 0 Then
result = results(0)
Return result.Path.ToString()
Else
Return Nothing
End If
Catch ex As Exception
Return Nothing
End Try
End Function
Public Shared Function GetUserInfo(ByVal id As String) As UserInfo
Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher
Dim results As SearchResultCollection
Dim props() As String = {"id", "sn", "mail", "givenname", "container", "cn"}
Try
With searcher
.SearchRoot = root
.PropertiesToLoad.AddRange(props)
.Filter = String.Format("cn={0}", id)
results = .FindAll()
End With
If results.Count > 0 Then
Dim properties As PropertyCollection = results(0).GetDirectoryEntry().Properties
Dim user As New UserInfo(properties("id").Value)
user.EmailAddress = properties("mail").Item(0).ToString
user.FirstName = properties("givenname").Item(0).ToString
user.LastName = properties("sn").Item(0).ToString
user.OfficeLocation = properties("container").Item(0).ToString
Return user
Else
Return New UserInfo
End If
Catch ex As Exception
Return Nothing
End Try
End Function
Public Shared Function IsMemberOfGroup(ByVal id As String, ByVal group As String) As Boolean
Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher
Dim results As SearchResultCollection
Dim result As SearchResult
Dim props() As String = {"cn", "member"}
Try
With searcher
.SearchRoot = root
.PropertiesToLoad.AddRange(props)
.Filter = String.Format("cn={0}", group)
results = .FindAll()
End With
If results.Count > 0 Then
For Each result In results
Dim members As PropertyValueCollection = result.GetDirectoryEntry().Properties("member")
Dim member As String
For i As Integer = 0 To members.Count - 1
member = members.Item(i).ToString
member = member.Substring(3, member.IndexOf(",") - 3).ToLowerInvariant
If member.Contains(id.ToLowerInvariant) Then Return True
Next
Next
End If
Return False
Catch ex As Exception
Return False
End Try
End Function
Public Shared Function GetMembersOfGroup(ByVal group As String) As List(Of String)
Dim groupMembers As New List(Of String)
Dim root As DirectoryEntry = New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher
Dim results As SearchResultCollection
Dim result As SearchResult
Dim props() As String = {"cn", "member"}
Try
With searcher
.SearchRoot = root
.PropertiesToLoad.AddRange(props)
.Filter = String.Format("cn={0}", group)
results = .FindAll()
End With
If results.Count > 0 Then
For Each result In results
Dim members As PropertyValueCollection = result.GetDirectoryEntry().Properties("member")
Dim member As String
For i As Integer = 0 To members.Count - 1
member = members.Item(i).ToString
member = member.Substring(3, member.IndexOf(",") - 3).ToLowerInvariant
groupMembers.Add(member)
Next
Next
End If
Catch ex As Exception
Return Nothing
End Try
Return groupMembers
End Function
End Class
Clarifications:
- there is a separate class for user (simple poco)
- there isn't a group class since all that's used right now is the list of ids, could be useful to add though

Here is an example of a refactored code sample:
Public Class AccessControl
Public Shared Function AuthenticateUser(ByVal id As String, ByVal password As String) As Boolean
Dim path As String
Dim username As String
Dim userRoot As DirectoryEntry
path = GetUserPath(id)
If path.Length = 0 Then
Return False
End If
username = path.Split("/")(3)
userRoot = New DirectoryEntry(path, username, password, AuthenticationTypes.None)
Try
userRoot.RefreshCache()
Return True
Catch ex As Exception
' Catching Exception might be accepted way of determining user is not authenticated for this case
' TODO: Would be better to test for specific exception type to ensure this is the reason
Return False
End Try
End Function
Private Shared Function GetUserPath(ByVal id As String) As String
Dim results As SearchResultCollection
Dim propertiesToLoad As String()
propertiesToLoad = New String() {"cn"}
results = GetSearchResultsForCommonName(id, propertiesToLoad)
If results.Count = 0 Then
Return String.Empty
Else
Debug.Assert(results.Count = 1)
Return results(0).Path
End If
End Function
Public Shared Function GetUserInfo(ByVal id As String) As UserInfo
Dim results As SearchResultCollection
Dim propertiesToLoad As String()
propertiesToLoad = New String() {"id", "sn", "mail", "givenname", "container", "cn"}
results = GetSearchResultsForCommonName(id, propertiesToLoad)
If results.Count = 0 Then
Return New UserInfo
End If
Debug.Assert(results.Count = 1)
Return CreateUser(results(0).GetDirectoryEntry().Properties)
End Function
Public Shared Function IsMemberOfGroup(ByVal id As String, ByVal group As String) As Boolean
Dim allMembersOfGroup As List(Of String)
allMembersOfGroup = GetMembersOfGroup(group)
Return allMembersOfGroup.Contains(id.ToLowerInvariant)
End Function
Public Shared Function GetMembersOfGroup(ByVal group As String) As List(Of String)
Dim results As SearchResultCollection
Dim propertiesToLoad As String()
propertiesToLoad = New String() {"cn", "member"}
results = GetSearchResultsForCommonName(group, propertiesToLoad)
Return ConvertMemberPropertiesToList(results)
End Function
Private Shared Function GetStringValueForPropertyName(ByVal properties As PropertyCollection, ByVal propertyName As String) As String
Return properties(propertyName).Item(0).ToString
End Function
Private Shared Function CreateUser(ByVal userProperties As PropertyCollection) As UserInfo
Dim user As New UserInfo(userProperties("id").Value)
With user
.EmailAddress = GetStringValueForPropertyName(userProperties, "mail")
.FirstName = GetStringValueForPropertyName(userProperties, "givenname")
.LastName = GetStringValueForPropertyName(userProperties, "sn")
.OfficeLocation = GetStringValueForPropertyName(userProperties, "container")
End With
Return user
End Function
Private Shared Function GetValueFromMemberProperty(ByVal member As String) As String
Return member.Substring(3, member.IndexOf(",") - 3).ToLowerInvariant
End Function
Private Shared Function ConvertMemberPropertiesToList(ByVal results As SearchResultCollection) As List(Of String)
Dim result As SearchResult
Dim memberProperties As PropertyValueCollection
Dim groupMembers As List(Of String)
groupMembers = New List(Of String)
For Each result In results
memberProperties = result.GetDirectoryEntry().Properties("member")
For i As Integer = 0 To memberProperties.Count - 1
groupMembers.Add(GetValueFromMemberProperty(memberProperties.Item(i).ToString))
Next
Next
Return groupMembers
End Function
Private Shared Function GetSearchResultsForCommonName(ByVal commonName As String, ByVal propertiesToLoad() As String) As SearchResultCollection
Dim results As SearchResultCollection
Dim searcher As New DirectorySearcher
With searcher
.SearchRoot = GetDefaultSearchRoot()
.PropertiesToLoad.AddRange(propertiesToLoad)
.Filter = String.Format("cn={0}", commonName)
results = .FindAll()
End With
Return results
End Function
Private Shared Function GetDefaultSearchRoot() As DirectoryEntry
Return New DirectoryEntry("LDAP://XXXXX/O=YYYY", String.Empty, String.Empty, AuthenticationTypes.None)
End Function
End Class
I think you can ever take this further by extracting constants, etc, but this removes most of the duplication, etc. Let me know what you think.
Way, too late, I realize... but also wanted to address some of the questions you asked as well. See http://chrismelinn.wordpress.com/2011/06/18/questions-before-refactoring-2/

I believe it's important to modify the exception handling as well. The pattern I see in the methods above is:
Try
...
Catch ex As Exception
Return False
End Try
The code above is essentially hiding (swallowing) its exceptions. This may have been implemented initially because certain types of exceptions were thrown as a result of the user not being found, and returning False or Nothing was probably appropriate. However, you may been getting other types of exceptions in your application which you may never know about (e.g. OutOfMemoryException, etc).
I would suggest catching only specific types of exceptions that you may want to legitimately return false/Nothing. For others, let the exception bubble up, or log it at a minimum.
For other tips on exception handling, read this helpful post.

The first thing I would do is remove any duplication. Strip out common functionality in to separate methods.
Also, think along the lines of every class should have a single role/responsibility - you might want to create separate User and Group classes.
There is a catalog of common refactorings here:
http://www.refactoring.com/catalog/index.html
You should only really consider Inversion of Control if you wish to swap in different classes for testing reasons etc...

It might not be the most significant refactoring, but I am a big fan of the early return. So, for instance, where you have:
If results.Count > 0 Then
Dim properties As PropertyCollection = results(0).GetDirectoryEntry().Properties
Dim user As New UserInfo(properties("id").Value)
user.EmailAddress = properties("mail").Item(0).ToString
user.FirstName = properties("givenname").Item(0).ToString
user.LastName = properties("sn").Item(0).ToString
user.OfficeLocation = properties("container").Item(0).ToString
Return user
Else
Return New UserInfo
End If
I would use, instead:
If results.Count == 0 Then Return New UserInfo
Dim properties As PropertyCollection = results(0).GetDirectoryEntry().Properties
Dim user As New UserInfo(properties("id").Value)
user.EmailAddress = properties("mail").Item(0).ToString
user.FirstName = properties("givenname").Item(0).ToString
user.LastName = properties("sn").Item(0).ToString
user.OfficeLocation = properties("container").Item(0).ToString
Return user
Indentation implies complexity, and there isn't enough complexity in the special handling of the empty result case to warrant 8 lines of indentation. There is a point at which removing indentation can actually hide real complexity, so don't push this rule too hard, but for the code presented, I'd definitely use the early return.

The first thing that immediately jumps out at me is that there's a lot of functions involving users that don't seem to be associated with the user class you have created.
I would try some of the following approaches:
Add the following to the user class:
Path
Authenticate(string password) - this could be a static method, not sure of the use cases here.
Groups - I would also recommend creating an actual domain object for groups. It could have a collection of users as a property to start with.

Related

Identify the properties of an object and put values into it

I want to identify the properties of specific object that it receives when the method is called and put values in it from the db result that I got. I've searched about it but I'm currently stucked in how I should proceed from here. Here is my code..
Public Class DBModel
Public Sub getFromDB(ByRef lists As List(Of Object), ByVal classType As Type, ByVal tblName as String)
Dim strSql As String = "SELECT * FROM " & tblName
Dim props = classType.GetProperties()
Try
Using cnn As New SqlConnection("Data Source = .\; Initial Catalog = DBName;" & "Integrated Security = True;")
Using cmd As New SqlCommand(strSql, cnn)
cnn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read
For Each prop In props
For i As Integer = 0 To dr.VisibleFieldCount - 1
prop = dr.GetValue(i)
Next
Next
lists.Add(props)
End While
End Using
End Using
End Using
Catch e As Exception
MessageBox.Show(e.ToString())
End Try
End Sub
End Class
I'm calling here the getFromDB method to populate the list of customers in this class, but I'll also call the getFromDB method from other classes with another different set of properties..
Public Class CustomerCtrler
private _CustomerList As New List(Of Customer)
Public Sub New()
Dim dbModel As New DBModel
Dim cust As New Customer
dbModel.getFromDB(_CustomerList, cust.GetType, "CustTbl")
End sub
End Class
Public Class Customer
Public Property custID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property DateRegistered As DateTime
End Class
But I got a InvalidCastException, so I've searched about converting the data types but I got: "Value type of Integer cannot be converted into PropertyInfo" at the 'prop = dr.GetValue(i)' line..
I'm quite new to object oriented programming so I'm sorry if there's a lot of mistakes there but your help will be really appreciated..
I would tend to go with something like this:
Public Function GetListFromDatabase(Of T As New)(tableName As String) As List(Of T)
Dim itemType = GetType(T)
Dim allProperties = itemType.GetProperties()
Dim items As New List(Of T)
Using connection As New SqlConnection("connection string here"),
command As New SqlCommand($"SELECT * FROM [{tableName}]", connection)
connection.Open()
Using reader = command.ExecuteReader()
Dim columnNames = reader.GetColumnSchema().
Select(Function(column) column.ColumnName).
ToArray()
'Ignore properties that don't have a corresponding column.
Dim properties = allProperties.Where(Function(prop) columnNames.Contains(prop.Name)).
ToArray()
Do While reader.Read()
'We can do this because we have specified that T must have a
'parameterless constructor by using "As New" in the method declaration.
Dim item As New T
For Each prop In properties
prop.SetValue(item, reader(prop.Name))
Next
items.Add(item)
Loop
End Using
End Using
Return items
End Function
You can then do this:
_CustomerList = dbModel.GetListFromDatabase(Of Customer)("CustTbl")
You can obviously create a variation on that if you really want to pass in an existing list but I don't see the point in that unless the list might already contain items.
EDIT: Here is an alternative method to get the data reader column names. I haven't tested it so it may be that "COLUMN_NAME" isn't quite right but it will be something very close to this:
Dim schemaTable = reader.GetSchemaTable()
Dim columnNames = schemaTable.Rows.
Cast(Of DataRow).
Select(Function(row) CStr(row("COLUMN_NAME"))).
ToArray()

Variable 'reader' is used before it has been assigned a value. A null reference exception could result at run time

Trying to fix a warning and not sure how to restructure code as reader.IsClosed is throwing a warning that states "Variable 'reader' is used before it has been assigned a value. A null reference exception could result at run time." Logistically, since reader As SqlDataReader && reader is not initialized with a value then I could ignore as should be fine at runtime, but my inexperience would make me believe there is a better way?
Public Function GetTotalItems(ByVal userId As Long) As Int16
Dim lstParam As List(Of SqlParameter) = New List(Of SqlParameter)()
Dim tablMd = Me.GetMetaData()
Dim retList As ArrayList = New ArrayList()
lstParam.Add(New SqlClient.SqlParameter("#" + tablMd.PrimaryKey.ColumnName, 0))
lstParam.Add(New SqlClient.SqlParameter("#UserID", userId))
lstParam.Add(New SqlClient.SqlParameter("#ActionFlag", "SELECT_ITEMS_COUNT"))
Dim spName As String = Me.GetStoreProcname()
Dim reader As SqlDataReader
Try
reader = SqlHelper.ExecuteReader(
Utility.GetConnectionStringSetting(),
CommandType.StoredProcedure,
Me.GetStoreProcname(),
lstParam.ToArray()
)
If (reader.HasRows = True) Then
If (reader.Read()) Then
Dim value As Object = reader(0)
Return CInt(value)
End If
End If
Catch ex As Exception
Throw
Finally
If Not reader.IsClosed Then
reader.Close()
End If
End Try
Return 0
End Function
We can narrow the problem down to this excerpt:
Dim reader As SqlDataReader
Try
reader = SqlHelper.ExecuteReader( ... )
Finally
If Not reader.IsClosed Then reader.Close()
End Try
The problem comes if an exception is thrown by the ExecuteReader() function. In that event, the reader variable is never assigned a value. It's still Nothing when you try to evaluate reader.IsClosed, and that will cause an exception.
Given you don't actually do anything with the exception and the SqlHelper takes care of the connection and command objects, you can narrow the entire function down to just this:
Public Function GetTotalItems(ByVal userId As Long) As Int16
Dim lstParam = {
New SqlClient.SqlParameter("#" + Me.GetMetaData().PrimaryKey.ColumnName, 0),
New SqlClient.SqlParameter("#UserID", userId),
New SqlClient.SqlParameter("#ActionFlag", "SELECT_ITEMS_COUNT")
}
Using reader As SqlDataReader = SqlHelper.ExecuteReader(
Utility.GetConnectionStringSetting(),
CommandType.StoredProcedure,
Me.GetStoreProcname(),
lstParam)
If reader.Read() Then Return CInt(reader(0))
End Using
Return 0
End Function
#jmcilhinney, #o_O, #Chris Dunaway ... Thank you for the help + appreciation + admiration for your knowledge + reverence == deverence(); ... This removed the error:
Public Function GetTotalAmount(ByVal userId As Long) As Decimal
Dim lstParam As List(Of SqlParameter) = New List(Of SqlParameter)()
Dim tablMd = Me.GetMetaData()
Dim retList As ArrayList = New ArrayList()
lstParam.Add(New SqlClient.SqlParameter("#" + tablMd.PrimaryKey.ColumnName, 0))
lstParam.Add(New SqlClient.SqlParameter("#UserID", userId))
lstParam.Add(New SqlClient.SqlParameter("#ActionFlag", "SELECT_TOTAL_AMOUNT"))
Dim spName As String = Me.GetStoreProcname()
Using reader As SqlDataReader = SqlHelper.ExecuteReader(
Utility.GetConnectionStringSetting(),
CommandType.StoredProcedure,
Me.GetStoreProcname(),
lstParam.ToArray()
)
If (reader.HasRows = True) Then
If (reader.Read()) Then
Dim value As Object = reader(0)
Return CDec(value)
End If
End If
End Using
Return 0
End Function

checking login credentials to see if they are valid in Active Directory AND check to see if they are apart of a specific group in AD

below is a method used to check to see if the Creds entered are good. i also would like to add on to this to see if they are part of group "XXX".
Private Function ValidateActiveDirectoryLogin(ByVal Domain As String, ByVal Username As String, ByVal Password As String) As Boolean
Dim Success As Boolean = False
Dim Entry As New System.DirectoryServices.DirectoryEntry("LDAP://" + Domain, Username, Password)
Dim Searcher As New System.DirectoryServices.DirectorySearcher(Entry)
Searcher.SearchScope = DirectoryServices.SearchScope.OneLevel
Try
Dim Results As System.DirectoryServices.SearchResult = Searcher.FindOne
Success = Not (Results Is Nothing)
Catch ex As Exception
Success = False
End Try
Return Success
End Function
and below i tried to play around with stuff i found on stack but im not having much luck. how can i use existing method and add to it in order to get my results?
Public Function IsInGroup(ByVal UserName As String) As Boolean
'Dim MyIdentity As System.Security.Principal.WindowsIdentity = New WindowsPrincipal(New WindowsIdentity(UserName)) ' System.Security.Principal.WindowsIdentity.GetCurrent()
'Dim userPrincipal = New WindowsPrincipal(New WindowsIdentity(Username))
Dim MyPrincipal As System.Security.Principal.WindowsPrincipal = New WindowsPrincipal(New WindowsIdentity(UserName)) 'New System.Security.Principal.WindowsPrincipal(userPrincipal)
Return MyPrincipal.IsInRole("XXX_YYY")
End Function
Also Tried to do something like this but getting the error i screenshotted.
Public Function IsInGroup(ByVal UserName As String) As Boolean
Dim Result As Boolean
Dim de As New DirectoryEntry("LDAP://AD")
Dim MemberSearcher As New DirectorySearcher
With MemberSearcher
.SearchRoot = de
.Filter = "(&(ObjectClass=Group)(CN=VAL_ITS))"
.PropertiesToLoad.Add("Member")
End With
Dim mySearchResults As SearchResult = MemberSearcher.FindOne()
For Each User In mySearchResults.Properties("Member")
If User = UserName Then
Result = True
Else
Result = False
End If
Next
Return Result
End Function
'Project > Add Reference > System.DirectoryServices.AccountManagement & System.DirectoryServices
Validate using the System.DirectoryServices.AccountManagement namespace
Imports System.DirectoryServices.AccountManagement
Public function validate(username as string, password as string, domain as string)
Dim valid As Boolean = False
Using context As New PrincipalContext(ContextType.Domain, domain)
valid = context.ValidateCredentials(username, password)
End Using
return valid
End Function
Public function checkgroup(domain as string, username as string, groupname as string)
Dim isMember as boolean = false
Dim ctx As New PrincipalContext(ContextType.Domain, domain)
Dim user As UserPrincipal = UserPrincipal.FindByIdentity(ctx, username)
Dim group As GroupPrincipal = GroupPrincipal.FindByIdentity(ctx, groupname)
If user IsNot Nothing Then
If user.IsMemberOf(group) Then
isMember = True
End If
End If
return isMember
End Function

Office Add In Memory Limit

I am developing a set of Office add ins that reads and writes templates and other files from a locally stored serialized object. Until now it has been going well, however, since I have been stress testing it by writing big amounts of data to this local object, and loading it into memory when reading from it, I have been getting SystemOutOfMemory exceptions. I do clean memory out each time I am done with this object, but simply loading it into memory can cause the exception be thrown.
The object is at most 100mb in size at any given time, so there is no way that it is using all the desktop resources to load this object into memory, so I have been wondering whether it may be that Microsoft limits the amount of memory that an add in may use.
What I would like to know is of anyone knows if there is a size limit and what it may be, as well as how to increase it if there is a limit present?
Thank you in advance for any advice given.
UPDATE:
Here is the code of the class that I am serializing:
Imports System.Environment
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices
<Serializable()> _
Public Class LocalDBObject
Private aOfficesDS As DataSet = New DataSet()
Private aFieldDS As DataSet = New DataSet()
Private aSectorsDS As DataSet = New DataSet()
Private aDocumentsDS As DataSet = New DataSet()
Private aLibraryDS As DataSet = New DataSet()
Private aRolesDS As DataSet = New DataSet()
Private aSettings As mySettings = New mySettings()
Private aUpdateDate As DateTime = DateTime.Now
Private aUser As UserInfo = New UserInfo()
Private aDistributionFilesDS As DataSet = New DataSet()
Public Property UpdateDate() As DateTime
Get
UpdateDate = aUpdateDate
End Get
Set(ByVal Value As DateTime)
aUpdateDate = Value
End Set
End Property
Public Property Settings() As mySettings
Get
Settings = aSettings
End Get
Set(ByVal Value As mySettings)
aSettings = Value
End Set
End Property
Public Property RolesDS() As DataSet
Get
RolesDS = aRolesDS
End Get
Set(ByVal Value As DataSet)
aRolesDS = Value
End Set
End Property
Public Property UserInfo() As UserInfo
Get
UserInfo = aUser
End Get
Set(ByVal Value As UserInfo)
aUser = Value
End Set
End Property
Public Shared Userfolder As String = GetFolderPath(SpecialFolder.ApplicationData) + "\CIM\" + Environment.UserName
Public Shared UserDistributionfolder As String = GetFolderPath(SpecialFolder.ApplicationData) + "\CIM\CIM Distribution Files"
Public Shared Function CheckFile() As Boolean
Return System.IO.File.Exists(Userfolder + "\LocalDB.dat")
End Function
Public Shared Sub Write(ByVal obj As LocalDBObject)
Serializer.Serialize2(obj, Userfolder + "\LocalDB.dat")
End Sub
Public Shared Function Read() As LocalDBObject
If Not System.IO.Directory.Exists(Userfolder) Then
System.IO.Directory.CreateDirectory(Userfolder)
End If
Dim obj As LocalDBObject = Serializer.deSerialize2(Userfolder + "\LocalDB.dat")
Return obj
End Function
Public Property DocumentsDS() As DataSet
Get
DocumentsDS = aDocumentsDS
End Get
Set(ByVal Value As DataSet)
aDocumentsDS = Value
End Set
End Property
Public Property LibraryDS() As DataSet
Get
LibraryDS = aLibraryDS
End Get
Set(ByVal Value As DataSet)
aLibraryDS = Value
End Set
End Property
Public Property OfficesDS() As DataSet
Get
OfficesDS = aOfficesDS
End Get
Set(ByVal Value As DataSet)
aOfficesDS = Value
End Set
End Property
Public Property FieldDS() As DataSet
Get
FieldDS = aFieldDS
End Get
Set(ByVal Value As DataSet)
aFieldDS = Value
End Set
End Property
Public Property SectorsDS() As DataSet
Get
SectorsDS = aSectorsDS
End Get
Set(ByVal Value As DataSet)
aSectorsDS = Value
End Set
End Property
Public Property DistributionDS() As DataSet
Get
DistributionDS = aDistributionFilesDS
End Get
Set(ByVal Value As DataSet)
aDistributionFilesDS = Value
End Set
End Property
Public Shared Function GetNameForLibaryGroup(ByVal FieldID As Integer, ByVal obj As LocalDBObject) As String
For Each dr As DataRow In obj.aFieldDS.Tables(0).Rows
If dr("FieldID") = FieldID Then
Return dr("FieldName")
End If
Next
Return ""
End Function
Public Shared Function GetFileForTemplate(ByVal DocID As Integer) As Byte()
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.DocumentsDS.Tables(0).Rows
If dr("docid") = DocID Then
Try
Dim data As Byte() = CType(dr("docdata"), Byte())
Return data
Catch ex As Exception
Return Nothing
End Try
End If
Next
Return Nothing
End Function
Public Shared Function GetFilenameForTemplate(ByVal DocID As Integer) As String
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.DocumentsDS.Tables(0).Rows()
If dr("docid") = DocID Then
Return Convert.ToString(LocalDBObject.Userfolder + "\" + dr("docName")) + "_" + Convert.ToString(dr("DocID")) + "." + Convert.ToString(dr("docext")).Substring(1)
End If
Next
Return Guid.NewGuid().ToString().Replace("-", "") + ".txt"
End Function
Public Shared Function GetFilenameOnlyForTemplate(ByVal DocID As Integer) As String
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.DocumentsDS.Tables(0).Rows()
If dr("docid") = DocID Then
Return Convert.ToString(dr("docName")) + "." + Convert.ToString(dr("docext")).Substring(1)
End If
Next
Return Guid.NewGuid().ToString().Replace("-", "") + ".txt"
End Function
Public Shared Function GetFilenameForPicture(ByVal DocID As Integer) As String
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.DocumentsDS.Tables(0).Rows()
If dr("docid") = DocID Then
Return Convert.ToString(LocalDBObject.Userfolder + "\" + dr("docName")) + "." + Convert.ToString(dr("docext")).Substring(1)
End If
Next
Return Guid.NewGuid().ToString().Replace("-", "") + ".txt"
End Function
Public Shared Function GetFileForLibrary(ByVal DocID As Integer) As Byte()
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.LibraryDS.Tables(0).Rows
If dr("docid") = DocID Then
Try
Dim data As Byte() = CType(dr("docdata"), Byte())
Return data
Catch ex As Exception
Return Nothing
End Try
End If
Next
Return Nothing
End Function
Public Shared Function GetFileLibaryDate(ByVal DocID As Integer) As DateTime
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.LibraryDS.Tables(0).Rows
If dr("docid") = DocID Then
Try
Dim adate As DateTime = Convert.ToDateTime(dr("docdateupdated"))
Return adate
Catch ex As Exception
Return DateTime.Now.AddYears(-100)
End Try
End If
Next
Return DateTime.Now.AddYears(-100)
End Function
Public Shared Function GetFilenameForLibary(ByVal DocID As Integer) As String
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each dr As DataRow In obj.LibraryDS.Tables(0).Rows
If dr("docid") = DocID Then
Return Convert.ToString(LocalDBObject.Userfolder + "\" + dr("docName")) + "_" + Convert.ToString(dr("DocID")) + "." + Convert.ToString(dr("docext")).Substring(1)
End If
Next
Return Guid.NewGuid().ToString().Replace("-", "") + ".txt"
End Function
'EUGENE: Added this to get the distribution folders.
Public Shared Function GetDistributionFileDS() As List(Of Integer)
Dim LocalDocIDList As New List(Of Integer)
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
If obj.aDistributionFilesDS.Tables.Count > 0 Then
For Each Row As DataRow In obj.aDistributionFilesDS.Tables(0).Rows
LocalDocIDList.Add(Row("docid"))
Next
End If
Return LocalDocIDList
End Function
Public Shared Function GetDistributionFilePath(DocumentID As Integer) As String
Dim LocalDocumentPath As String = ""
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each Row As DataRow In obj.aDistributionFilesDS.Tables(0).Rows
If Row("docid") = DocumentID Then
LocalDocumentPath = LocalDBObject.UserDistributionfolder + "\" + CStr(Row("docid")) + "-" + CStr(Row("docName")) + CStr(Row("docext"))
End If
Next
Return LocalDocumentPath
End Function
Public Shared Function GetDistributionFileData(DocumentID As Integer) As Byte()
Dim LocalDocumentData As Byte() = Nothing
Dim obj As CIMShared.LocalDBObject = CIMShared.LocalDBObject.Read()
For Each Row As DataRow In obj.aDistributionFilesDS.Tables(0).Rows
If Row("docid") = DocumentID Then
LocalDocumentData = CType(Row("docdata"), Byte())
End If
Next
Return LocalDocumentData
End Function
End Class
Here are the serializer functions that serializes the class, and that deserilizes the class.
Public Shared Sub Serialize(ByVal obj As LocalDBObject, ByVal FileName As String)
Dim Stream As New FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)
Dim objStreamWriter As New StreamWriter(Stream)
Dim x As New XmlSerializer(obj.GetType)
x.Serialize(objStreamWriter, obj)
objStreamWriter.Close()
End Sub
Public Shared Function deSerialize(ByVal FileName As String) As LocalDBObject
Dim obj As New LocalDBObject()
Dim Stream As FileStream
Dim objStreamReader As StreamReader
Try
Stream = New FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
objStreamReader = New StreamReader(Stream)
Dim x As New XmlSerializer(obj.GetType)
obj = x.Deserialize(objStreamReader)
objStreamReader.Close()
Catch ex As Exception
obj = New LocalDBObject()
Try
Stream.Close()
Catch
End Try
Try
objStreamReader.Close()
Catch
End Try
End Try
Return obj
End Function
NOTE: I realize this is not the cleanest or neatest code, this is a former colleagues code that I am using and trying to resolve bugs on.
What is currently happening is that every time I de-serialize the class, which is about 300MB in size, I get a Out Of Memory Exception. In no way is this using all the computer memory, hence why I would like to find out if there is a memory usage limit on Microsoft Office add-ins?
What object are you talking about? Could you be more specific?
Anyway, I always suggest releasing underlying COM objects insteatly. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Office object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. Read more about that in the Systematically Releasing Objects article.

Problem Understanding Access Modifiers in VB.Net with List( Of Object)

I've recently been updating a lot of my code to comply with proper n-tier architecture and OO programming, following examples from a book.
I'm starting to get problems now because I don't fully understand the access modifiers.
If I run the following code I get an error at the line
Dim clientFamilyDataAccessLayer As New ClientFamilyDAO
in the BLL at the point it creates an instance of the DAL. The full error message is: "The type initializer for 'ClientFamilyDAO' threw an exception. ---> System.NullReferenceException: Object reference not set to an instance of an object."
How do I use these function to create a list of ClientFamily objects that I can then work with?
On my UI layer I'm creating a list of objects; ClientFamilies
Dim listOfClientFamilies As List(Of ClientFamily) = ClientFamily.GetClientFamiliesByKRM(selectedEmployee.StaffNumber)
This is the function in the BLL
Public Shared Function GetClientFamiliesByKRM(ByVal krmStaffNumber As Integer) As List(Of ClientFamily)
Dim clientFamilyDataAccessLayer As New ClientFamilyDAO
Return clientFamilyDataAccessLayer.GetClientFamiliesByKRM(krmStaffNumber)
End Function
and this is function in the DAL
Public Function GetClientFamiliesByKRM(ByVal staffNumber As Integer) As List(Of ClientFamily)
Dim currentConnection As SqlConnection = New SqlConnection(_connectionString)
Dim currentCommand As New SqlCommand
currentCommand.CommandText = mainSelectStatement & " WHERE Key_Relationship_Manager = #StaffNumber ORDER BY Client_Family_Name"
currentCommand.Parameters.AddWithValue("#StaffNumber", staffNumber)
currentCommand.Connection = currentConnection
Dim listOfClientFamilies As New List(Of ClientFamily)
Using currentConnection
currentConnection.Open()
Dim currentDataReader As SqlDataReader = currentCommand.ExecuteReader()
Do While currentDataReader.Read
Dim newClientFamily As AECOM.ClientFamily = PopulateClientFamily(currentDataReader)
listOfClientFamilies.Add(newClientFamily)
Loop
End Using
Return listOfClientFamilies
End Function
Here's the full ClientFamilyDAO Class:
Public Class ClientFamilyDAO
Private Const mainSelectStatement As String = "SELECT Client_Family_ID, Client_Family_Name, Key_Relationship_Organisation, Key_Relationship_Manager, Obsolete, Market_Sector_ID FROM Client_Families"
Private Shared ReadOnly _connectionString As String = String.Empty
Shared Sub New()
_connectionString = WebConfigurationManager.ConnectionStrings("ClientFamilyManagementConnectionString").ConnectionString
End Sub
Public Function GetClientFamiliesByKRM(ByVal staffNumber As Integer) As List(Of ClientFamily)
Dim currentConnection As SqlConnection = New SqlConnection(_connectionString)
Dim currentCommand As New SqlCommand
currentCommand.CommandText = mainSelectStatement & " WHERE Key_Relationship_Manager = #StaffNumber ORDER BY Client_Family_Name"
currentCommand.Parameters.AddWithValue("#StaffNumber", staffNumber)
currentCommand.Connection = currentConnection
Dim listOfClientFamilies As New List(Of ClientFamily)
Using currentConnection
currentConnection.Open()
Dim currentDataReader As SqlDataReader = currentCommand.ExecuteReader()
Do While currentDataReader.Read
Dim newClientFamily As AECOM.ClientFamily = PopulateClientFamily(currentDataReader)
listOfClientFamilies.Add(newClientFamily)
Loop
End Using
Return listOfClientFamilies
End Function
Private Function PopulateClientFamily(ByVal currentDataReader As SqlDataReader) As AECOM.ClientFamily
Dim newClientFamily As New AECOM.ClientFamily
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Client_Family_ID"))) Then
newClientFamily.ClientFamilyID = currentDataReader("Client_Family_ID")
End If
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Client_Family_Name"))) Then
newClientFamily.ClientFamilyName = currentDataReader("Client_Family_Name")
End If
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Key_Relationship_Organisation"))) Then
Select Case currentDataReader("Key_Relationship_Organisation")
Case False
newClientFamily.IsKeyRelationshipOrganisation = False
Case True
newClientFamily.IsKeyRelationshipOrganisation = True
End Select
End If
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Key_Relationship_Manager"))) Then
newClientFamily.KeyRelationshipManagerStaffNumber = currentDataReader("Key_Relationship_Manager")
End If
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Obsolete"))) Then
Select Case currentDataReader("Obsolete")
Case False
newClientFamily.Obsolete = False
Case True
newClientFamily.Obsolete = True
End Select
End If
If Not (currentDataReader.IsDBNull(currentDataReader.GetOrdinal("Market_Sector_ID"))) Then
newClientFamily.MarketSectorID = currentDataReader("Market_Sector_ID")
End If
Return newClientFamily
End Function
End Class
The issue doesn't relate to access modifiers, rather it is more to do with the exception message you get. The following line within the constructor of ClientFamilyDAO would seem to be causing the issue:
_connectionString = WebConfigurationManager.ConnectionStrings("ClientFamilyManagementConnectionString").ConnectionString
Are you sure ClientFamilyManagementConnectionString exists in the configuration?