Ivan Mitev In The Software Trenches

Technology weblog on .NET development and other things that make the world go round

March 24, 2007

Controlling the number of active users of an ASP.NET application

Last week out team had to implement a requirement that limits the number of concurrent active users of an ASP.NET application. In order to deal with concurrent users, there should be a continuous interaction between the user and the application, which does not match very well the essence of web applications: request-response. Since our app is meant to be an intranet application (used mostly like a desktop app), such a licensing scheme makes some sense. And the concept of an ASP.NET session denotes a continuous communication, so it provided a good way of determining who is an active user and who is not.

I was surprised that I could not find a way to directly control the active ASP.NET sessions - e.g. kill a session (by its SessionID), get the session data, etc. As a workaround, we used a two-step process: keep the IDs of the active sessions in a "session manager" and when some event occurs (e.g. the max number of concurrent users is reached) mark a session "to be killed". That's how the next request from a "to be killed" session, forces the session to be abandoned.

This schema worked nicely, until we noticed that closing the browser and reopening it again caused a new session to be registered in our "session manager". The previous one remained alive since the ASP.NET application did not know that the user had closed his browser (ASP.NET session ID cookie is stored only in browser memory, so it gets lost). So we came up with another workaround and used a persistent cookie to identify that the user has finished a session and started a new one.

The thing that I don't quite like, is that we started with a simple problem and developed a pretty complex solution. I wonder if there was a more elegant way to deal with the concurrent users issue.

Labels:

March 12, 2007

VS.NET 2005 Macro For Removing Unnecessary Projects From A Solution

I usually work with a pretty large VS.NET solution. Currently It contains 60 projects and I don't need them all of the time. To ease the solution loading and help VS.NET add-ins like ReSharper load and function faster, it is a good idea to split this solution into several smaller ones. Since the projects interdependencies are not that simple, I decided to automate the process of creating the smaller solutions.

The macro below finds all projects referenced of the selected ones and removes the other projects from the solution. Then you one can do "Save as" and use the shrinked solution. Use ShowAllReferencedProjects() for displaying the projects that will remain after the operation and RemoveAllProjectsUnusedByActiveProjects() for the actual removal (you will be asked if you are sure you want to do that).

Note: Excuse my VB, I have not exercised my VB skills, since doing some VB6 coding more than two years ago. The part that was most fun and challenging was not VB, but exploring the VS.NET object model - surprisingly the resources on this topic were a little scarce.

Disclaimer: Use the code at your own risk! (well, it can't really do much harm, since it does not save the solution automatically).

UPDATE: Well, actually RemoveAllProjectsUnusedByActiveProjects() can affect some of the project files, since the order of removal turned out to be important (removing a referenced projects, shortens the reference lists of other project). This issue is fixable, but currently I don't have the time to fix it :) Moreover the operation is very slow so it might be easier to add projects to a blank solution instead of removing them from existing solution. Actually, the best way to handle this task is on a file level, where you need a parser for solution and project files. Parsing those files will not be that trivial and I had not been able to find such parsers online. Looks like a good idea for a pet project :)

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Windows.Forms

Public Module VsProjectsManagement
Public Sub RemoveAllProjectsUnusedByActiveProjects()

Dim unusedProjects As List(Of VSLangProj.VSProject) = FindAllProjectsUnusedByActiveProjects()
If unusedProjects.Count = 0 Then
MessageBox.Show("There are no unused projects for the current project selection")
Exit Sub
End If

Dim s As String
s = "Do you want to remove the following " & unusedProjects.Count & " projects from the solution: " _
& GetProjectNames(unusedProjects) & "?"

If Not DialogResult.Yes = MessageBox.Show(s, "Unused projects removal", MessageBoxButtons.YesNo, MessageBoxIcon.Question) Then
Exit Sub
End If

RemoveProjectsFromSolution(unusedProjects)

End Sub

Public Sub ShowAllReferencedProjects()
Dim usedProjects As List(Of VSLangProj.VSProject)
usedProjects = FindAllProjectsReferencedByActiveProjects()

Dim s As String
s = "The selected project(s) reference total of " & CStr(usedProjects.Count) _
& " projects in the solution: " & GetProjectNames(usedProjects)

MessageBox.Show(s, "Referenced projects")
End Sub

Sub RemoveProjectsFromSolution(ByVal projects As List(Of VSLangProj.VSProject))
For Each vsProject As VSLangProj.VSProject In projects
DTE.Solution.Remove(vsProject.Project)
Next
End Sub

Function GetProjectNames(ByVal projects As List(Of VSLangProj.VSProject)) As String
Dim projectNames As List(Of String) = New List(Of String)(projects.Count)

For Each vsProject As VSLangProj.VSProject In projects
projectNames.Add("'" & vsProject.Project.Name & "'")
Next

projectNames.Sort()

Return String.Join(", ", projectNames.ToArray())

End Function

Function FindAllProjectsUnusedByActiveProjects() As List(Of VSLangProj.VSProject)
Dim allVsProjects As List(Of VSLangProj.VSProject)
Dim vsProject As VSLangProj.VSProject

allVsProjects = FindAllVsProjectsInSolution()
If (allVsProjects.Count = 0) Then
Debug.WriteLine("There are no Projects In Solution")
Exit Function
End If

Dim allActiveAndReferencedProject As List(Of VSLangProj.VSProject)
allActiveAndReferencedProject = FindAllProjectsReferencedByActiveProjects()

Return DifferenceProjects(allVsProjects, allActiveAndReferencedProject)
End Function

Function DifferenceProjects(ByVal mainSet As List(Of VSLangProj.VSProject), ByVal setToRemove As List(Of VSLangProj.VSProject)) As List(Of VSLangProj.VSProject)

Dim resultSet As New List(Of VSLangProj.VSProject)

For Each vsProject As VSLangProj.VSProject In mainSet
If Not setToRemove.Contains(vsProject) Then
resultSet.Add(vsProject)
End If
Next

Return resultSet
End Function

Function FindAllProjectsReferencedByActiveProjects() As List(Of VSLangProj.VSProject)

Dim activeVSProjects As List(Of VSLangProj.VSProject)
Dim allActiveAndReferencedProject As New List(Of VSLangProj.VSProject)
Dim vsProject As VSLangProj.VSProject

activeVSProjects = FindAllActiveVsProjects()
If (activeVSProjects.Count = 0) Then
Debug.WriteLine("Please select one or more projects in the solution")
Exit Function
End If

' fill first the initial selected projects
For Each vsProject In activeVSProjects
allActiveAndReferencedProject.Add(vsProject)
Next

' find all referenced projects recursively
For Each vsProject In activeVSProjects
FindAndFillReferencedProjects(vsProject, allActiveAndReferencedProject)
Next

Return allActiveAndReferencedProject
End Function

Function FindAllVsProjectsInSolution() As List(Of VSLangProj.VSProject)

Dim usedVSProjects As New List(Of VSLangProj.VSProject)

For Each proj As Project In DTE.Solution.Projects
If TypeOf proj.Object Is VSLangProj.VSProject Then
usedVSProjects.Add(proj.Object)
End If
Next

Return usedVSProjects
End Function

Function FindAllActiveVsProjects() As List(Of VSLangProj.VSProject)

Dim usedVSProjects As New List(Of VSLangProj.VSProject)

For Each proj As Project In DTE.ActiveSolutionProjects()
If TypeOf proj.Object Is VSLangProj.VSProject Then
usedVSProjects.Add(proj.Object)
End If
Next

Return usedVSProjects
End Function

Sub FindAndFillReferencedProjects(ByVal objVSProject As VSLangProj.VSProject, ByVal usedVSProjects As List(Of VSLangProj.VSProject))

Dim referencedVSProject As VSLangProj.VSProject
Dim proj As Project

For Each objReference As VSLangProj.Reference In objVSProject.References
If objReference.Type = VSLangProj.prjReferenceType.prjReferenceTypeAssembly Then
proj = objReference.SourceProject
If Not proj Is Nothing Then
If TypeOf proj.Object Is VSLangProj.VSProject Then
referencedVSProject = proj.Object
If Not referencedVSProject Is Nothing Then
If Not usedVSProjects.Contains(referencedVSProject) Then
' Add current and recursively add the others
usedVSProjects.Add(referencedVSProject)
FindAndFillReferencedProjects(referencedVSProject, usedVSProjects)
End If
End If
End If
End If
End If
Next

End Sub

End
Module


Labels: