Finding your largest file

I had a recent project where I had to loop though a directory and then any subdirectory and pull out any .jar files to stick onto a class path…. Man, java sucks.  In any event, I whipped this code, which I then modified to find the largest file on my file system.  Kinda handy – if my hard drive space starts creeping up:

static void Main(string[] args)
{
    Console.WriteLine("Start");
    string startingPath = @"C:\Users";
    List<FileInfo> fileInfos = GetFileInfos(new DirectoryInfo(startingPath));
    Console.WriteLine("{0} files found", fileInfos.Count);
    FileInfo largestFile = fileInfos.FirstOrDefault(f => f.Length == fileInfos.Max(t => t.Length));
    Console.WriteLine("{0} is the largest with {1} bytes", largestFile.Name, largestFile.Length);
    Console.WriteLine("End");
    Console.ReadKey();
}

static List<FileInfo> GetFileInfos(DirectoryInfo directoryInfo)
{
    List<FileInfo> fileInfos = null;
    try
    {
        fileInfos = directoryInfo.GetFiles().ToList();
        foreach (DirectoryInfo currentDirectoryinfo in directoryInfo.GetDirectories())
        {
            try
            {
                fileInfos.AddRange(GetFileInfos(currentDirectoryinfo));
            }
            catch (ArgumentNullException)
            {
            }
            catch (PathTooLongException)
            {
            }
        }
    }
    catch (UnauthorizedAccessException)
    {

    }


    return fileInfos;
}

CLR Profiler: Note to Jamie – Follow These Steps

1) Open CLR Profiler – make sure you are using the 32 bit version for 32-bit apps

2) Make sure Allocations and Calls are checked in the Profile section:

image

3) Start the application using the Start Application button

4) Run the app for a bit

5) Press the Kill Application button to get the following screen:

image

 

6) The math is: AllocatedBytes – RelocatedBytes = FinalHeapBytes.  ReloactedBytes are the bytes that moved into the garbage collection, FinalHeapBytes are the bytes that never got garbage collected.

7) Views –> Objects By Address.  Select most frequent object.  Right Click Export data to file.  Sort by size and then look at allocated by and callled from

8) Look at the heap graph – only the GC heap and then the tree view

StreamWriter – Don’t Forget To Flush

I started working through the examples in the CLR Profiler documentation over the weekend.  The 1st one looks like this:

StreamReader streamReader = new StreamReader(@"C:\Users\Jamie\Desktop\Demo1.dat");
string line;
int lineCount = 0;
int itemCount = 0;
while ((line = streamReader.ReadLine()) != null)
{
    lineCount++;
    string[] items = line.Split();
    for (int i = 0; i < items.Length; i++)
    {
        itemCount++;
    }
}
streamReader.Close();

Console.WriteLine("{0} lines, {1} items", lineCount, itemCount);

To make that Demo1.dat file, I needed to write a quick function that spits out the expected values.  I coded up this:

StreamWriter streamWriter = new StreamWriter(@"C:\Users\Jamie\Desktop\Demo1.dat");
string word = "0123456789 ";
for(int i=0; i<2000; i++)
{
    StringBuilder stringBuilder = new StringBuilder();
    for(int j = 0; j < 10; j++)
    {
        stringBuilder.Append(word); 
    }
    streamWriter.WriteLine(stringBuilder.ToString());
}

When I ran the function, the last chunks of data was not showing up:

image

I talked to my friend Rob Seder and he said “You dummy, you forgot to flush!*”  Sure enough, when I set this:

streamWriter.AutoFlush = true;

the results came out as expected.

 

*He really didn’t say “You Dummy”, that is what I told myself once he mentioned it…..

CLR Profiler Gotcha

I fired up the 64-bit version of CLR profiler on my 64-bit machine, attached to a demo Console application and got the following dialog box:

image

The problem is that the application was targeting 32-bit (x86) in Visual Studio.  Since it is a pain in the butt to target 64-bit Console apps from Visual Studio 2010 (see the trail of tears here – note that this is a feature, not a bug in VS2010 Eye rolling smile), I opened the 32-bit version of CLR profiler instead and did my analysis.

Note that you will also get the same dialog box if you run CLR Profiler as a standard user (I always wondered what  sub-standard user was…).  Make sure you run it as administrator.

Updating an ASP.NET website without the source code

I went back into the website of a girls lacrosse team to update it for the 2012 season.  The problem is that the source code is gone – it got deleted when I moved my TFS machine to Win7 and the backup failed (another good reason to use an on-line repro).

I thought I would have to recreate the entire solution/project when I realized that the code I need to change is the markup for the .aspx pages themselves.  For example, here is 1 SqlDataSource (SDS) that I used:

    <asp:SqlDataSource ID="SqlDataSourcePlayers" runat="server" 
        ConnectionString="<%$ ConnectionStrings:DB_15641_mpmslaxConnectionString %>" 
        SelectCommand="Girls_SelectActivityPlayers" SelectCommandType="StoredProcedure">
        <SelectParameters>
            <asp:Parameter DefaultValue="8" Name="ActivityId" Type="Int32" />
        </SelectParameters>

Note the DefaultValue of the ActivityId Parameter – it was 6 for last spring season, I had to change it to 8 for this season.  So all I did was open up the .aspx file in notepad via my FTPProgram, change the Parameter and voila, the data is correct.

I then hit a roadblock when I went to another page.  In that page, I used an ObjectDataSource that referenced a factory class that I created to  do so data flattening – taking all of the girls in a single carpool and putting their names into a single string, for example.  Instead of re-creating that Factory, I swopped out the ODS for a SDS and mimicked the factory’s functionality a stored procedure.  Heck, I even mirrored the misspellings in the output parameters so I wouldn’t have to change the grid view.  I haven’t done much TSQL in the last couple of years, but it is amazing how fast it comes back:

Create procedure Girls_SelectCarpoolSummary
@carpoolId int
As

declare @riders varchar(1000)
set @riders = ''
select @riders = @riders + FirstName + ' ' + LastName + ', ' 
from [Girls.Player] as P
inner join [Girls.CarpoolLegPlayer] as CLP on P.PlayerId = CLP.PlayerId
inner join [Girls.CarpoolLeg] as CL on CLP.CarpoolLegId = CL.CarpoolLegId
where CL.CarpoolId = @carpoolId
and CL.CarpoolLegTypeId = 1

declare @leaveTime varchar(7)
set @leaveTime = ''
select @leaveTime = Right(CONVERT(varchar(100), CL.CarpoolLegStartTime, 100),7)
from [Girls.CarpoolLeg] as CL
where CL.CarpoolId = @carpoolId
and CL.CarpoolLegTypeId = 1

declare @returnTime varchar(7)
set @returnTime = ''
select @returnTime = Right(CONVERT(varchar(100), DateAdd(hh,3,CL.CarpoolLegStartTime), 100),7)
from [Girls.CarpoolLeg] as CL
where CL.CarpoolId = @carpoolId
and CL.CarpoolLegTypeId = 1

Select
C.CarpoolId,
TE.EventDescription + ' ' + CONVERT(varchar(100), C.CarpoolDate, 101) as CarpoolDescription,
T.FirstName + ' ' + LastName as Drivers,
@riders as Riders,
@leaveTime as LeaveTime,
@returnTime as ReturnTime
From [Girls.Carpool] as C
inner join [Girls.TeamEvent] as TE on C.TeamEventId = TE.TeamEventId
inner join [Girls.CarpoolLeg] as CL on C.CarpoolId = CL.CarpoolId
inner join [Girls.Parent] as T on CL.ParentId = T.ParentId
Where C.CarpoolId = @carpoolId
and CL.CarpoolLegTypeId = 1
GO

Notice the flattening for the @riders variable. 

I then had to update the summary page to show ALL of the upcoming carpools in a single grid – so I needed a stored procedure to call this one many times (I thought about changing this sp to a function but I didn’t).  Temp tables to the rescue:

Create procedure Girls_SelectUpcommingCarpoolSummaries
As

Select *
into #UpcommingCarpools
From [Girls.Carpool] as C
Where DateDiff(d,getdate(),CarpoolDate) >= 0

CREATE TABLE #UpcommingCarpoolSummaries
( 
   CarpoolId int, 
   CarpoolDescrpition varchar(8000),
   Drivers varchar(1000),
   Riders varchar(8000),
   LeaveTime varchar(10),
   ReturnTime varchar(10)    
) 

Declare @carpoolId int
While (Select Count(*) From #UpcommingCarpools) > 0 
Begin 
    Select Top 1 @carpoolId = carpoolId From #UpcommingCarpools 
    INSERT INTO #UpcommingCarpoolSummaries  
    Exec Girls_SelectCarpoolSummary @carpoolId
    Delete #UpcommingCarpools Where carpoolId = @carpoolId 
End 

Select * from #UpcommingCarpoolSummaries

GO

The thing is, it worked.  I have no covering unit tests.  I used about 85 lines of TSQL when 15 lines of C# would have done the trick.  But it worked.  I feel so dirty…..