I'm currently tasked with upgrading our existing TFS 2008 system to TFS 2010. This in itself isn't a problem, but it does throw up 1 or 2 slight 'Technical Issues.'
We're using a dual server setup, whereby the application tier and the build tier will sit on one server and the database elsewhere. We also have a requirement to integrate with SharePoint 2010 and SQL Reporting Services, so those all need to be installed too.
There is a specific order that things should be installed in, it goes as follows:
1) IIS 7
2) SQL Server 2008 (You need to make sure Full Text Search is installed, as well as Reporting Services and Analysis Services if you want to use Reporting)
3) Any SharePoint Products
4) TFS
Now in our situation, we already have a database we were planning on using, which has everything it needs installed on it. However, we have discovered that TFS needs its own SSRS instance and can't piggy-back on to an existing instance, so we need to install a new instance of SQL Server.
Another thing we have found is that if you want to run the Reporting Services on the Application Tier server, then you need a full SQL License as it seems the SSRS that comes with SQL Express can't connect to a SQL Enterprise database.
Once you've got round all that, the install itself is fairly painless, the wizard that is used in TFS 2010 is quite friendly and makes things nice and easy.
Once I've completed the installation, I'll be moving on to the migration of the data from 2008 to 2010. That is a whole can of worms and I'll blog about that when I've done it!
OK, So in a previous post, I explained that I was developing an application that relied on AD for usernames. Well, today I found that the code I posted earlier only returns 1000 users, so it was excluding some of the users at our company.
What I found was that 1000 is the default number of items returned from AD and that to get every AD object you need to override that value.
To do that is quite simple, check out the revised code below:
C#
public SearchResultCollection GetUsers()
{
DirectoryEntry entry = GetDirectoryObject();
entry.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher deSearch = new DirectorySearcher(entry);
deSearch.Filter = "(&(objectCategory=person)(objectClass=user))";
deSearch.SizeLimit = System.Int32.MaxValue;
deSearch.PageSize = System.Int32.MaxValue;
SearchResultCollection results = deSearch.FindAll();
return results;
}
VB.Net
Public Shared Function GetUsers() As SearchResultCollection
Dim entry As New DirectoryEntry
entry = GetDirectoryObject()
entry.AuthenticationType = AuthenticationTypes.Secure
Dim deSearch As New DirectorySearcher(entry)
deSearch.Filter = "(&(objectCategory=person)(objectClass=user))"
deSearch.SizeLimit = System.Int32.MaxValue
deSearch.PageSize = System.Int32.MaxValue
Dim results As SearchResultCollection
results = deSearch.FindAll()
Return results
End Function
You'll see the extra 2 lines above that make all the difference!
I have, over the past 2 weeks, been developing on a system which should use Active Directory as it's user source. That is to say, it get's the usernames from AD and stores them in it's own database with a custom password for security.
Now, when I first started work on this system, there were already a couple of hundred users set up, mostly using the correct username structure, but some which weren't. The requirement was to ensure that all users had a standard username which matched their AD username, however site security was to be handled by the application rather than Active Directory.
To throw another spanner in the works, the application had been developed back in 2007, by a contractor who, obviously, wasn't with the company any more. It hadn't been touched since 2007 and it was developed in VB.net... 2.0.
Having never developed against AD before, I did some research and found that there weren't too many good examples about, so I had to dig out my VB.Net bible.
I needed to get a list of all users in the directory, so I could populate a DropDownList to ensure the username was in the correct format whenever a new user was created. The code to do this is as follows:
C#
public DirectoryEntry GetDirectoryObject()
{
DirectoryEntry oDE = new DirectoryEntry(LDAP://YOUR DOMAIN);
return oDE;
}
public SearchResultCollection GetUsers()
{
DirectoryEntry entry = GetDirectoryObject();
entry.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher deSearch = new DirectorySearcher(entry);
deSearch.Filter = "(&(objectCategory=person)(objectClass=user))";
SearchResultCollection results = deSearch.FindAll();
return results;
}
private void LoadDropdowns()
{
foreach (SearchResult searchResult in DataObjects.Users.GetUsers()) {
DirectoryEntry directoryEntry = default(DirectoryEntry);
directoryEntry = searchResult.GetDirectoryEntry;
if (directoryEntry.SchemaClassName.ToString().ToLower() == "user") {
IEnumerable query = null;
bool userExists = false;
query = users.AsEnumerable().Where(user => user("username").Equals(directoryEntry.Properties("sAMAccountName").Value.ToString));
if (directoryEntry.Properties("sAMAccountName").Value.ToString() != directoryEntry.Properties("CN").Value.ToString()) {
listItem = new ListItem();
listItem.Value = directoryEntry.Properties("sAMAccountName").Value.ToString();
listItem.Text = directoryEntry.Properties("sAMAccountName").Value.ToString() + " (" + directoryEntry.Properties("CN").Value.ToString() + ")";
ddUserName.Items.Add(listItem);
}
}
}
}
VB.Net:
Public Shared Function GetDirectoryObject() As DirectoryEntry
Dim oDE As DirectoryEntry
oDE = New DirectoryEntry("LDAP://YOUR DOMAIN")
Return oDE
End Function
Public Shared Function GetUsers() As SearchResultCollection
Dim entry As New DirectoryEntry
entry = GetDirectoryObject()
entry.AuthenticationType = AuthenticationTypes.Secure
Dim deSearch As New DirectorySearcher(entry)
deSearch.Filter = "(&(objectCategory=person)(objectClass=user))"
Dim results As SearchResultCollection
results = deSearch.FindAll()
Return results
End Function
Private Sub LoadDropdowns()
For Each searchResult As SearchResult In DataObjects.Users.GetUsers()
Dim directoryEntry As DirectoryEntry
directoryEntry = searchResult.GetDirectoryEntry
If directoryEntry.SchemaClassName.ToString().ToLower() = "user" Then
Dim query As IEnumerable
Dim userExists = False
query = users.AsEnumerable().Where(Function(user) user("username").Equals(directoryEntry.Properties("sAMAccountName").Value.ToString))
If directoryEntry.Properties("sAMAccountName").Value.ToString() <> directoryEntry.Properties("CN").Value.ToString() Then
listItem = New ListItem
listItem.Value = directoryEntry.Properties("sAMAccountName").Value.ToString()
listItem.Text = directoryEntry.Properties("sAMAccountName").Value.ToString() + " (" + directoryEntry.Properties("CN").Value.ToString() + ")"
ddUserName.Items.Add(listItem)
End If
End If
Next
End Sub
I'll go into AD Development further over the next few days/weeks as I think it's pretty funky!
TDD, it has caused more office arguments than just about anything recently.
In business, it's all about the money, so by telling a management team that you need to take a little longer to develop an application because you want to write a full test suite won't go down well. The management team will tell you that they can't afford the time it's going to take, so skip the tests and just develop the App.
The problem is, that is a very short term view. I've been in that same situation, I've had that same argument on more than one occasion. I've now come up with a solution that seems to work. I was asked to build a Web Application that would be easy for any other developer to get to grips with in the future. I was given a full specification and was asked for a time-scale, so I planned the full development process, twice. The first plan included time to build a test suite, it also showed a 'future plan' for development of phase 2 and phase 3 functionality. The second plan didn't include any test suite and the results were fairly conclusive.
In the first plan, phase 1 development was increased by about 2 weeks, but phase 2 and 3 development were both decreased by 2 weeks each. Despite the obvious time savings long term, the directors of the company were still sceptical, they needed more convincing. So I sat them down and gave them a detailed run through of what would be included in the test suite. We were using Visual Studio 2010, so I built a small application, with a full test suite, I included unit tests, UI tests, performance tests and load tests. Once I'd built the app, I sat the directors down at my computer and showed them the benefits of the tests. I added something new to the application that I knew would still allow the code to build, but would break something obscure in run time. I asked the Directors to try and find the error just by testing the app in a browser. It took them about 20 minutes to find it and this was on an application consisting of 3 pages. I then showed them what happened when I ran the tests. It took just 15 seconds to build the code and run the tests.
The look on the faces of the directors was priceless. 15 Seconds to find an error it took 2 people 20 minutes to find. From there on in, we were only ever going to go down the TDD route.
I'll be honest though. TDD only works if it is done well. Write your tests before you build any other logic. Your tests should test your design, not the code you have written. Once you have your tests properly written, you should be able to build your application safe in the knowledge that your design works.