Now the Visual Studio® 2008 Web Deployment Projects - RTW is released. This version of the Web Deployment Projects add-in is a 2008 version of the Web Deployment Projects for Visual Studio 2005.
With the deployment projects add-in you can control the deployment of your web Application easily. You configure it once and with the click of a button you can get a precompliled version of your application.
It really is a XML (project) file, containing the configuration for all your builds (debug, test, release or whatever). In combination with the Web Setup Project (you can make the Web Deployment Project the source for your Web Setup) it is really easy to create Setups for your Web Application. 

Download it here: http://www.microsoft.com/downloads/thankyou.aspx?familyId=0aa30ae8-c73b-4bdd-bb1b-fe697256c459&displayLang=en

Henry Cordes
My thoughts exactly...


Blogengine.net

Published 1/26/2008 by Henry in ASP.NET
Tags:

While I changed the bloggingengine for this blog from dasBlog to subText some time ago (because I wanted SQL server support instead of XML Files), a fellow developer pointed me to blogengine.net.

My main reason for wanting to change this fast again is that in my opinion blogengine.net is the best option at this moment in time. It provides XML and SQL Server support through the provider model, so a new datasource is easily integrated. I think because it has not been around that long it's foundation is pure ASP.NET 2.0. Blogengine.net has learned from the mistakes that dasBlog, subText etc. have made.
In software development projects sooner or later we always come for the choice: 'Are we going on on this path, or are we going to built from scratch?'. I think that blogengine.net is a good example that it sometimes can be rewarding to start all over.

Anyway I moved to blogengine.net so now I have tag support and a new layout once more. 
Blogengine.net is available on Codeplex.

Henry Cordes
My thoughts exactly...


LINQ to Active Directory

Published 1/20/2008 by Henry in C# | LINQ
Tags: ,

Because I needed a way to get users from Active Directory to sync them with another application (CRM). I started out writing Directory Services (LDAP) Queries. I started thinking: "Can't this be done with LINQ?", after all in C# and the .NET Framework 3.0, 3.5  we now have the Language Integrated Query possibilities in our programming toolkit. With LINQ you should be able to query all collections, as long as they impement the IQueryable interface. So why not Active Directory?
When you search: "LINQ to AD" with Google. You''ll find the following link: http://www.codeplex.com/LINQtoAD.

Bart de Smet is a belgian developer, who works for Microsoft. He is the one that made LINQ to AD. Here is a serie blogposts he wrote about this topic.
LINQ to AD is a query provider for LINQ that's capable of talking to Active Directory (and other LDAP data sources potentially) over LDAP.
Ofcourse LINQ To AD is a wrapper around System.DirectoryServices.

He wrote the BdsSoft.DirectoryServices.Linq, the LINQ to AD implementation, if you want to use LINQ To AD you need to add a reference to this assembly and you need to reference the Active DS Type Library (ActiveDs.dll). The 'activeds.dll' is a module that contains functions and object methods, or COM components, for the Active Directory Services Interfaces (ADSI) API.

One of the reasons System.DirectoryServices is so powerfull, is because you can still access native ADSI interfaces by using the NativeObject method. NativeObject will return the IADs interface of the specific type of object.
To use the NativeObject method, you'll need to add a reference to the ActiveDs.dll library. I need to get all users belonging to a particular AD Group and sync these to my application.
All I need to do is: Add a reference the BdsSoft.DirectoryServices.Linq assembly and the Active DS Type Library (ActiveDs.dll).
Next I created a method that must Retrieve all users member of a group in the Active Directory.

The method will have two parameters:

  1. domain, a string that will hold the Active Directory domain name
  2. groupName, a string that will contain the name of the group in AD the users belong to

The method will return a List of type User, User is a custom object that represents a user and is called: "RetrieveADUsersInGroup";

Here a listing of the signature: 

   1:  private List<User> RetrieveADUsersInGroup(string domain, string groupName)
   2:  {
   3:      // Put code here
   4:  }
Listing 1

In the method, an instance of the System.DirectoryServices.DirectoryEntry object needs to be instantiated.
I pass a string to the constructor ('LDAP://'), so the DirectoryEntry instance is bound to the node in Active Directory Domain Services that is located at this specific path.

   1:  DirectoryEntry rootOfDirectory = new DirectoryEntry(string.Format("LDAP://{0}", domain));
Listing 2

Now we take this DirectoryEntry object (rootOfDirectory) and pass it to the constructor of the object DirectorySource<T> (which implements the IQueryable<T> interface), together with the enum System.DirectoryServices.SearchScope (defines the scope for a Directory search).
We take SearchScope.Subtree, which means the whole SubTree, including Base object and all child objects.

Here the signature of the method that is called and lives inside the BdsSoft.DirectoryServices.Linq assembly. 

   1:  public class DirectorySource<T> : IQueryable<T>, IDirectorySource
   2:  {
   3:      // Code here
   4:  }
Listing 3

Now we use the keyword: 'var', var is a way to declare variables in C# 3.0  and up, that uses implicit typing. Keep in mind that var is not the same as 'object', or the JavaScript 'var' datatype as it’s actually strongly typed, but inferred from whatever the value is being assigned.
The proces of creating datatypes on the fly is called: 'projection'. Var is projecting the datatype from the Query.

   1:      var groups = new DirectorySource<Group>(rootOfDirectory, SearchScope.Subtree);
   2:      var listOfAllGroups = from          grp in groups
   3:                            where         grp.Name == groupName
   4:                            select new {  grp.Name, MemberCount = grp.Members.Length, grp.Members };
Listing 4

On line 1 'var groups' the complete root directory is inferred as BdsSoft.DirectoryServices.Linq.DirectorySource<T>, where 'T' is of type 'ActiveDirectory.Group', my own class that represents an ActiveDirectory group. 
From line 2 on the LINQ syntax is used to get all groups from the Collection where the name is equal to the value in methods string parameter 'groupName'. From these groups or group the Name, MemberCount and Members are taken and returned by the query and assigned to the listOfAllGroups variable.

I wanted to map the result from this LINQ Query directly to my User type, instead of needing to loop through every result and map each property one by one. What provides an opportunity to show that you can map your own types to the Query's result, after all you can predict the projection result. 
First the code that retrieves the users: 

   1:  var UserFromAD = new DirectorySource<Centric.PublieksDiensten.KC.ActiveDirectory.Entities.User>(rootOfMemberDirectory, SearchScope.Subtree);
   2:   
   3:  IEnumerable<User> usersFromQuery = from adUser in UserFromAD
   4:                                     select new User
   5:                                     {
   6:                                          Firstname = adUser.FirstName,
   7:                                          Lastname = adUser.LastName,
   8:                                          AccountName = adUser.AccountName,
   9:                                          Email = adUser.Email,
  10:                                          PhoneNumber = adUser.TelephoneNumber,
  11:                                          Fax = adUser.Fax,
  12:                                          AddressStreet = adUser.AddressStreet,
  13:                                          City = adUser.City,
  14:                                          State = adUser.State,
  15:                                          Country = adUser.Country
  16:                                      };
Listing 5

On line 3 the variable 'usersFromQuery' is of type 'IEnumerable of type User' (IEnumerable<User>). IEnumerable needs to be implemented if we do not want to use 'var'. On line 4 the part 'select new' is extended to 'select new User' in this way we project our own User type. In the body of the Query you see every field is mapped to a property of the User type through field name/value pairs. Under the covers var infers the field names, field values, and field data types and creates an anonymous type with the fields we project.

Summary:

  • IEnumerable of type ...
  • select new ...
  • map through field name/value pairs

Here a listing with the complete code from the Method:

   1:  private static List<User> RetrieveADUsersInGroup(string domain, string groupName)
   2:  {
   3:      DirectoryEntry rootOfDirectory = new DirectoryEntry(string.Format("LDAP://{0}", domain));
   4:   
   5:      var groups = new DirectorySource<Group>(rootOfDirectory, SearchScope.Subtree);
   6:      var listOfAllGroups = from          grp in groups
   7:                            where         grp.Name == groupName
   8:                            select new {  grp.Name, MemberCount = grp.Members.Length, grp.Members };
   9:   
  10:      List<User> userList = new List<User>();
  11:   
  12:      foreach (var currentGroup in listOfAllGroups)
  13:      {
  14:          foreach (var member in currentGroup.Members)
  15:          {
  16:              DirectoryEntry rootOfMemberDirectory = new DirectoryEntry(GetLDAPPath(domain) + "/" + member);
  17:              var UserFromAD = new DirectorySource<Centric.PublieksDiensten.KC.ActiveDirectory.Entities.User>(rootOfMemberDirectory, SearchScope.Subtree);
  18:   
  19:              IEnumerable<User> usersFromQuery = from adUser in UserFromAD
  20:                                          select new User
  21:                                          {
  22:                                              Firstname = adUser.FirstName,
  23:                                              Lastname = adUser.LastName,
  24:                                              AccountName = adUser.AccountName,
  25:                                              Email = adUser.Email,
  26:                                              PhoneNumber = adUser.TelephoneNumber,
  27:                                              Fax = adUser.Fax,
  28:                                              AddressStreet = adUser.AddressStreet,
  29:                                              City = adUser.City,
  30:                                              State = adUser.State,
  31:                                              Country = adUser.Country
  32:                                          };
  33:   
  34:              userList.AddRange(usersFromQuery);
  35:          }
  36:      }
  37:   
  38:      return userList;
  39:  }
Listing 6

LINQ is a layer of abstraction over the way we query data. It is in the case of LINQ to AD a nice beginning to get rid of the LDAP Querying. LINQ overall provides a way to query Databases, Collections, Active Directory, XML and every Datasource for that matter in the same way from now on. In the LINQ to AD case the fact that had to query all groups and then loop through these groups to get the members of the groups is a big shortcoming. I had hoped to have to write one query that did all the work and returned a list of users. Still because the datasource is DirectoryServices and Active DS I can understand that their limits are in the way of making this possible. Still I hope in the future this will be possible.
I do like to work with LINQ and will try to learn more about it's inner workings. I definitely see a shift in paradigm, no more static language only in C# and I think change like this is exiting!

Henry Cordes
My thoughts exactly...


Adding new custom entity to CRM 3.0 fails

Published 1/16/2008 by Henry in CRM
Tags:

While we went in production a few requirements were added to our CRM 3.0 project. The last thing we did before going into production was adding and removing roles and teams.

All system roles accept System Administrator where removed, only custom roles where needed, als teams where added. Because of the new features a new (custom) entity needed to be created. When I saved it the following message appeared:
"An error has occurred. For more information, contact your administrator" (talking about a meaningfull error message!).
While I tried to solve this problem I found out Microsoft has a hotfix available for this problem (http://support.microsoft.com/kb/936204/EN-US/). This hotfix can only be obtained through a support ticket from MS Support. In this blogpost is stated that the deletion of the 'System Customizer Role' does the damage.

It has a reference to this KB Article (Microsoft KB article 934690):
When a custom entity is created, Microsoft Dynamics CRM automatically grants the System Customizer role access to the new entity. If you delete or edit the System Customizer role, you receive the error message that is mentioned in the "Symptoms" section.
The blogpost states it appears that when creating a custom entity CRM checks if the 'System Customizer' role is present (at least the if a role with a RoleID equal to the Guid this role had when CRM was installed). Also just manually recreating a "System Customizer" role does not fix the problem.

The solutions present at that moment in time:

  • MS Hotfix
  • Fresh install of CRM with data migration (back up CRM, uninstall CRM, install CRM fresh and then migrate all of your data from the back up into the new system)

Now I really wanted to know what's going on, so I hooked up a trace with SQL Server's profiler. A lot of stored proc calls are made, but just before the first ROLLBACK this dynamic SQL was executed:

   1:  exec sp_executesql N ' select role.RoleId as ''roleid'',role.BusinessUnitId as ''businessunitid''
   2:  from Role as role inner join BusinessUnit as rolebusinessunitid
   3:  on (role.BusinessUnitId = rolebusinessunitid.BusinessUnitId) and rolebusinessunitid.DeletionStateCode in (0)
   4:  and (rolebusinessunitid.ParentBusinessUnitId is null )
   5:  inner join SystemUser as businessunitorganizationid
   6:  on (rolebusinessunitid.OrganizationId = businessunitorganizationid.OrganizationId)
   7:  and businessunitorganizationid.DeletionStateCode in (0)
   8:  and (businessunitorganizationid.SystemUserId = @P1)
   9:  where role.DeletionStateCode in (0)
  10:  and (role.RoleTemplateId = @P2)',N'@P1 uniqueidentifier,
  11:  @P2 uniqueidentifier','X1X1X1X1-Q1Q1-Q1Q1-Q1Q1-X1X1X1X1X1X1','Z2Z2Z2Z2-Y2Y2-Y2Y2-Y2Y2-Z2Z2Z2Z2Z2Z2'
Listing 1

When I ran it in the  SQL Server Manager's Query Window an empty row was returned. 
The following part of the SQL is interesting in this case:

(role.RoleTemplateId = @P2) 
Listing 2

Maybe this could be the Guid that is absent. When we look inside the RoleTemplateBase table we see that all system roles exist in this table, even though they do not exist as roles in this CRM implementation anymore.
When a new entity is created CRM checks to see if the executing user is a member of any role that has a relation to the template with the RoleTemplateId of the 'System Customizer' role template. This to me is strange behavior, specially because the RoleTemplateBase table has no field or relation to the settings of a Role, the only fields it has are:

  • RoleTemplateID;
  • Name;
  • Version;
  • Upgrade.

'RoleTemplateBase' table of the '<organization>_MSCRM' database  
'RoleTemplateBase' table of the '<organization>_MSCRM' database

I used the MS CRM Role Utility to export the System Customizer role to a xml file from a shadow VPC that still contained the role. Als with the MS CRM Role Utility I imported the role into CRM. Here is a link: Systeemaanpasser.xml (31,23 kb) to the xml containing the role (NOTE this is a Dutch CRM installation!, I will try to post an English version of the 'System Customizer' role also).
So the role is in  the system again, now I ran a somewhat customized version of the Dynamic SQL, so it is not dynamic anymore. Figuring when it returns a row this error will be gone.

Query:

   1:  select role.RoleId as 'roleid',role.BusinessUnitId as 'businessunitid' 
   2:  from Role as role 
   3:  inner join BusinessUnit as rolebusinessunitid 
   4:  on (role.BusinessUnitId = rolebusinessunitid.BusinessUnitId) 
   5:  and rolebusinessunitid.DeletionStateCode in (0) 
   6:  and (rolebusinessunitid.ParentBusinessUnitId is null ) 
   7:  inner join SystemUser as businessunitorganizationid 
   8:  on (rolebusinessunitid.OrganizationId = businessunitorganizationid.OrganizationId) 
   9:  and businessunitorganizationid.DeletionStateCode in (0)  
  10:  and (businessunitorganizationid.SystemUserId = 'X1X1X1X1-Q1Q1-Q1Q1-Q1Q1-X1X1X1X1X1X1')  
  11:  where role.DeletionStateCode in (0)  
  12:  and (role.RoleTemplateId = 'Z2Z2Z2Z2-Y2Y2-Y2Y2-Y2Y2-Z2Z2Z2Z2Z2Z2') 
Listing 3

I ran the query and it returned nothing:

Empty result of query
Empty result

I opened the 'RoleTemplateBase' table, selected the row containing the 'System Customizer' role and copied the Guid in the 'RoleTemplateID' field to the clipboard.
Than I opened the Role view (remember 'Select ... from Role as role') and searched for the row containing the newly added 'System Customizer' role.

Records in 'Role' view 
Records in 'Role' view

In this row I pasted the Guid (I earlier had copied to the clipboard) into the field 'TemplateRoleID' and saved it.
Than I ran the query again, but now it returned a row:

Result: "one row"
Result: "one row"

I tried to add a new custom entity into CRM and guess what it worked again!
So the advice I read in a few CRM related blogposts to never remove system roles (specially the 'System Customizer' role) is repeated here in this post.

Henry Cordes
My thoughts exactly...


My fellow-avanade dude Dennis pointed me out the following site:
Lost in Tangent

On this site there are lots links to of tutorials, walkthroughs of the ADO.NET Data Services (formerly "Astoria"). Astoria was a project from Microsoft's 'Data team'. A team that works on all data related stuff in and for the .NET Framework, like the Entity Framework. The ADO.NET Data Services is the name that is now used for project 'Astoria', after a few CTP's now the ASP.NET 3.5 Extensions preview release contains the first production release. With these Extensions Microsofts paves the way for more 'in-between' releases. We do not always have to wait for a major .NEt Framework release that contains new functionality. The ASP.NET AJAX Framework had the premiere with this in-between release methodology. It was a success and with the .NET Framework 3.5 just released, ADO.NET Data services is here with some more goodies.
These services are a combination of patterns and libraries that enables any data store to be exposed as a data service, naturally integrating with the Web. Also these services can be consumed by Web clients. It is built making heavy use of the Entity Framework. 

I quote: "ADO.NET Data Services uses URIs to point to pieces of data and simple, well-known formats to repirst production release.resent that data, such as JSON and ATOM/APP. This results in data being exposed to Web clients as a REST-style resource collection, addressable with URIs that agents can interact with using standard HTTP verbs such as GET, POST, or DELETE."

I played with it and it was remarkebly easy to get data on the client using Javascript and because JSON is a supported format, it is easy to get these objects directly from the server tier. I think this could be a really helpfull if you want to create Client-centric AJAX Webapps. The use of formats like RSS, JSON and Atom is brilliant in simplicity. The ADO.NET Data Services are released in the ASP.NET 3.5 Extensions preview release.

If you are interested in what it is all about, take a look here

Henry Cordes
My thoughts exactly