Thursday, April 18, 2013

Programmatically Generating Properties for OptionSet Enums

Generating early bound entity classes for CRM Entities is fairly simple.  Generating the Enums for the OptionSets is fairly simple as well.  Generating the OptionSet Enum properties for entities that are typed to the correct Enum is not so simple, and not currently supported by the CrmSrvUtil.exe.  If you want to be able to use the enums for populating the option set values, you have to continually write code that looks like this:
contact.Address1_AddressTypeCode = new OptionSetValue((int)contact_address1_addresstypecode.Home);


Which I don’t like for three reasons:

No Compile Time Error Checking to Ensure the Correct Enum Type is Used
For my current customer, they have some OptionSets that were created in CRM 4.0, before they had global OptionSets.  Once they upgraded to CRM 2011, they added global versions of these OptionSets as well, so if you’re not paying attention, you could easily be populating a local OptionSetValue on an entity, with the int value of a global OptionSetEnum (which have different int values).

Extra “Boiler Plate” Code Requires More KeyStrokes, and is Less Readable
Any time there is extra code that really isn’t needed, it makes it harder on the human brain to determine exactly what the code is doing because it in essence, has to ignore part of the information.

Just as 
(((x)*2)+1) = 13 
is harder to read than 
x*2+1 = 13 
so is

contact.Address1_AddressTypeCode = new OptionSetValue((int)contact_address1_addresstypecode.Home);

harder to read than

contact.Address1_AddressTypeCodeEnum = contact_address1_addresstypecode.Home;

25% of the text has been eliminated, which results in code that requires less typing, and is more readable.

Inconsistent Mapping of Attribute/Property Name to Enum Type
Determining the correct Enum for a given entity’s OptionSet property is usually pretty straight forward, but sometimes it’s not. i.e. The Enum for FieldPermission.CanCreate is field_security_permission_type and the Enum for Metric.AmountDataType is metric_goaltype.  The only intellisense that’s provided is that it’s an OptionSet, and it is up to the developer to find and choose the correct one.


The Entity OptionSet Enum Mapper Utility to the Rescue!
I have created an EntityOptionSetEnumMapper utility to generate a property for each OptionSetValue with the correct Enum.  For example, it generates these additional properties for the Contact class (CrmEntities is the namespace):

public partial class Contact
{
  public CrmEntities.contact_accountrolecode? AccountRoleCodeEnum { ... }
  public CrmEntities.contact_address1_addresstypecode? Address1_AddressTypeCodeEnum { ... }
  public CrmEntities.contact_address1_freighttermscode? Address1_FreightTermsCodeEnum { ... }
  public CrmEntities.contact_address1_shippingmethodcode? Address1_ShippingMethodCodeEnum { ... }
  public CrmEntities.contact_address2_addresstypecode? Address2_AddressTypeCodeEnum { ... }
  public CrmEntities.contact_address2_freighttermscode? Address2_FreightTermsCodeEnum { ... }
  public CrmEntities.contact_address2_shippingmethodcode? Address2_ShippingMethodCodeEnum { ... }
  public CrmEntities.contact_customersizecode? CustomerSizeCodeEnum { ... }
  public CrmEntities.contact_customertypecode? CustomerTypeCodeEnum { ... }
  public CrmEntities.contact_educationcode? EducationCodeEnum { ... }
  public CrmEntities.contact_familystatuscode? FamilyStatusCodeEnum { ... }
  public CrmEntities.contact_gendercode? GenderCodeEnum { ... }
  public CrmEntities.contact_haschildrencode? HasChildrenCodeEnum { ... }
  public CrmEntities.contact_leadsourcecode? LeadSourceCodeEnum { ... }
  public CrmEntities.contact_paymenttermscode? PaymentTermsCodeEnum { ... }
  public CrmEntities.contact_preferredappointmentdaycode? PreferredAppointmentDayCodeEnum { ... }
  public CrmEntities.contact_preferredappointmenttimecode? PreferredAppointmentTimeCodeEnum { ... }
  public CrmEntities.contact_preferredcontactmethodcode? PreferredContactMethodCodeEnum { ... }
  public CrmEntities.contact_shippingmethodcode? ShippingMethodCodeEnum { ... }
  public CrmEntities.contact_statuscode? StatusCodeEnum { ... }
  public CrmEntities.contact_territorycode? TerritoryCodeEnum { ... }
}

This is a partial class, and so will be combined at compile time with the existing Contact Class.  All of the existing OptionSetValue properties will still exist, but now there will be new ones (appended with “Enum”) that are typed to the correct Enum type.

Each property still uses the OptionSetValue property to store and retrieve the data, so when Address1_AddressTypeCodeEnum is updated, it will also update Address1_AddressTypeCode and visa versa, since they use the Entity.Attributes collection and share the same key.  Here is the Address1_AddressTypeCodeEnum property that is generated:

public CrmEntities.contact_address1_addresstypecode? Address1_AddressTypeCodeEnum
{
     get
     {
         return (CrmEntities.contact_address1_addresstypecode?)EntityOptionSetEnum.GetEnum(this, "address1_addresstypecode");
     }
     set
     {
         Address1_AddressTypeCode = value.HasValue ? new Microsoft.Xrm.Sdk.OptionSetValue((int)value) : null;
     }
}

It makes use of an internal static class that is also in the output file, that retrieves the OptionSet from the Attributes collection as a nullable int:

internal static class EntityOptionSetEnum
{
    public static int? GetEnum(Microsoft.Xrm.Sdk.Entity entity, string attributeLogicalName)
    {
        if(entity.Attributes.ContainsKey(attributeLogicalName))
        {
            return entity.GetAttributeValue<Microsoft.Xrm.Sdk.OptionSetValue>(attributeLogicalName).Value;
        }
        else
        {
            return null;
        }
    }
}

The nullable int is then cast to the correct enum type which is then returned. 

Using the Entity OptionSet Enum Mapper Executable
The only thing that the Entity OptionSet Enum Mapper requires is the dll that contains the early bound Entities and Enums created via the CrmSrvcUtil.  It requires no interaction with the CRM server itself.  This creates a standard “which came first, the chicken or the egg” problem.  The Entity and Enum dll must be built first before the utility can be ran, but it must run before the dll is built in order for the generated code to be included in the dll.  This is fixed by adding a pre-build event to the Entities and Enums dll project to build a temporary version of the dll, and then invoking the utility to run and generated the code.      

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /target:library /reference:"C:\PathOfSdk\microsoft.xrm.sdk.dll" "$(ProjectDir)Entities.cs" "$(ProjectDir)OptionSets.cs"

This basically says, compile the Entities and OptionSet files, referencing the Microsoft.Xrm.Sdk.  By default this will get compiled to the bin directory of the project, with the name Entities.dll.  A second pre-build event will be needed to call the utility and generate the EntityOptionSetEnum.cs file (which will need to be manually added to the project file the first time).

"C:\PathOfExe\EntityOptionSetEnumMapper.exe" "$(TargetDir)Entities.dll" "$(ProjectDir)EntityOptionSetEnums.cs"

This will run the utility, passing in the path to the Entities.dll to use to generate the EntityOptionSetEnum.cs file, and the path and name of the file to generate.

Both these steps happen before the dll itself get’s built, generating the EntityOptionSetEnum.cs file, which will then be compiled with the actual dll.


Below are separate downloads for the Executable, and the Source Code.  Enjoy!



Entity OptionSet Enum Mapper Executable
Entity OptionSet Enum Mapper Source

Wednesday, March 20, 2013

Make Websites Startup Faster When Debugging

Currently I’m working on an ASP.net website that caches data during the Application_Start event of the Global.asax.cs.  Generally this is a great idea for a website, especially when deploying a website in a load balanced environment, because the penalty of loading cached data can be spent in one page load.  Take the server out of the load balancer, deploy your changes, hit the first page, verify that no errors occurred, and put the server back in the balancer.  This ensures that a user hitting the site, won’t have to pay the penalty of loading the cache data.

But, this is not so much fun when developing/debugging.  90% of the time a developer works on a single page at a time, and if the page uses only a small fraction of the cache, or none at all, they get to pay the cache load penalty each time they restart the website.  Having to wait even 20 seconds each time the website is restarted in Visual Studio is real productivity killer.

Some developers may opt to comment out the cache loading portion of the Application_Start.  But if they accidently commit the change into source control, production could be affected the next time it’s deployed.  And the next time they get latest from source control, they’ll loose their local commented out Application_Start.   So how does one ensure that production will always cache the data on Application_Start, without forcing developers to pay the cache load penalty?

System.Diagnostics.Debugger.IsAttached to the Rescue!

protected void Application_Start(object sender, EventArgs e)
{
     if (System.Diagnostics.Debugger.IsAttached)
     {
         // Speed up the first page load for debugging by not loading all of the Cache
         return;
     }
     PreloadCachedValues();
}
Debugger.IsAttached will check to see if a debugger is currently attached.  Since this will always be the case when developing the website locally, and never be the case in production (assuming there isn’t some catastrophic production event), developers can enjoy the productivity improvements when developing the website, without risking any performance issues in production.

Friday, March 8, 2013

Simplifying Retrieval of Aliased Values in CRM 2011

Aliased values have always annoyed me when performing a QueryExpression, or FetchXML Query.  It requires a lot of boiler plate code each time you attempt to retrieve an aliased value (checking for it to exist, including the entity logical name, casting the aliased value’s value to the correct type, etc).  Due to being a lazy developer who believes if I do something twice, that’s once too many, I created some extension methods to simplify the process.  I’m posting them here today for the world to enjoy.
First the code:  There are 5 methods. 
  1. GetAliasedValue<T> – This is the main method.  It’s an extension method on Entity, so it can be used on any entity, early bound or late.  It accepts the an attributeName which is the name of the aliased attribute.  As long as you don’t have duplicate aliased names (i.e. linking/joining on two different entities, and returning the same attribute name from both),  you don’t have to preappend the the logical name of the entity of the aliased value.  It is also generic, so that it will cast the value of the aliased value to the generic type.
  2. SplitAliasedAttributeEntityName – This helper method splits the attribute name passed in, by a period to determine if an entity logical name has been passed in, or only an attribute name.
  3. IsAttributeAliasedValue – This method determines if a particular aliased value is the aliased value requested by the user
  4. GetAliasedValueOrDefault<T> – By default GetAliasedValue<> will throw an exception if the aliased value wasn’t found, along with a list of the attributes contained in the collection.  This checks to see if the aliased value exists, and gets the default value for the generic type if isn’t doesn’t exist.
  5. HasAliasedAttribute – Used by GetAliasedValueOrDefault<T>.  Also helpful if you want to do some other logic besides getting the default value if it isn’t found
         /// <summary>
         /// Returns the Aliased Value for a column specified in a Linked entity
         /// </summary>
         /// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
         /// <param name="entity"></param>
         /// <param name="attributeName">The aliased attribute from the linked entity.  Can be preappeneded with the
         /// linked entities logical name and a period. ie "Contact.LastName"</param>
         /// <returns></returns>
         public static T GetAliasedValue<T>(this Entity entity, string attributeName)
         {
             string aliasedEntityName = SplitAliasedAttributeEntityName(ref attributeName);
             AliasedValue aliased;
             foreach (var attribute in entity.Attributes.Values)
             {
                 aliased = attribute as AliasedValue;
                 if(entity.IsAttributeAliasedValue(attributeName, aliasedEntityName, aliased))
                 {
                     try
                     {
                         return (T)aliased.Value;
                     }
                     catch (InvalidCastException)
                     {
                         throw new InvalidCastException(
                             String.Format("Unable to cast attribute {0}.{1} from type {2} to type {3}",
                                     aliased.EntityLogicalName, aliased.AttributeLogicalName,
                                     typeof(T).Name, aliased.Value.GetType().Name));
                     }
                 }
             }
             throw new Exception("Aliased value with attribute " + attributeName +
                 " was not found!  Only these attributes were found: " + String.Join(", ", entity.Attributes.Keys));
         }


         /// <summary>
         /// Handles spliting the attributeName if it is formated as "EntityAliasedName.AttributeName",
         /// updating the attribute name and returning the aliased EntityName
         /// </summary>
         /// <param name="attributeName"></param>
         /// <param name="aliasedEntityName"></param>
         private static string SplitAliasedAttributeEntityName(ref string attributeName)
         {
             string aliasedEntityName = null;
             if (attributeName.Contains('.'))
             {
                 var split = attributeName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                 if (split.Length != 2)
                 {
                     throw new Exception("Attribute Name was specified for an Alaised Value with " + split.Length +
                     " split parts, and two were expected.  Attribute Name = " + attributeName);
                 }
                 aliasedEntityName = split[0];
                 attributeName = split[1];
             }
             return aliasedEntityName;
         }


        private static bool IsAttributeAliasedValue(this Entity entity, string attributeName, string aliasedEntityName, AliasedValue aliased)
         {
             bool value =
            (aliased != null &&
                 (aliasedEntityName == null || aliasedEntityName == aliased.EntityLogicalName) &&
                 aliased.AttributeLogicalName == attributeName);

             /// I believe there is a bug in CRM 2011 when dealing with aggregate values of a linked entity in FetchXML.
             /// Even though it is marked with an alias, the AliasedValue in the Attribute collection will use the 
             /// actual CRM name, rather than the aliased one, even though the AttributeCollection's key will correctly
             /// use the aliased name.  So if the aliased Attribute Logical Name doesn't match the assumed attribute name
             /// value, check to see if the entity contains an AliasedValue with that key whose attribute logical name 
             /// doesn't match the key (the assumed bug), and mark it as being the aliased attribute
             if (!value && aliased != null && entity.Contains(attributeName))
             {
                 var aliasedByKey = entity[attributeName] as AliasedValue;
                 if (aliasedByKey != null && aliasedByKey.AttributeLogicalName != attributeName &&
                      Object.ReferenceEquals(aliased, aliasedByKey))
                 {
                     value = true;
                 }
             }
             return value;
         }


         /// <summary>
         /// Returns the Aliased Value for a column specified in a Linked entity, returning the default value for 
         /// the type if it wasn't found
         /// </summary>
         /// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
         /// <param name="entity"></param>
         /// <param name="attributeName">The aliased attribute from the linked entity.  Can be preappeneded with the
         /// linked entities logical name and a period. ie "Contact.LastName"</param>
         /// <returns></returns>
         public static T GetAliasedValueOrDefault<T>(this Entity entity, string attributeName)
         {
             T value;
             if (entity.HasAliasedAttribute(attributeName))
             {
                 value = entity.GetAliasedValue<T>(attributeName);
             }
             else
             {
                 value = default(T);
             }
             return value;
         }


         /// <summary>
         /// Returns the Aliased Value for a column specified in a Linked entity
         /// </summary>
         /// <typeparam name="T">The type of the aliased attribute form the linked entity</typeparam>
         /// <param name="entity"></param>
         /// <param name="attributeName">The aliased attribute from the linked entity.  Can be preappeneded with the
         /// linked entities logical name and a period. ie "Contact.LastName"</param>
         /// <returns></returns>
         public static bool HasAliasedAttribute(this Entity entity, string attributeName)
         {
             string aliasedEntityName = SplitAliasedAttributeEntityName(ref attributeName);
             return entity.Attributes.Values.Any(a =>
                 entity.IsAttributeAliasedValue(attributeName, aliasedEntityName, a as AliasedValue));
         }


Just add these methods to an Extension class and ease the burden aliased values incur:


        public Guid GetBusinessUnit(IOrganizationService service, Guid buildingId)
        {
             var qe = new QueryExpression("new_building");
             qe.ColumnSet.AddColumns("new_buildingid", "new_name");
             qe.Criteria.AddCondition("new_buildingid", ConditionOperator.Equal, buildingId);
             var location = qe.AddLink("new_location", "new_locationid", "new_locationid");
             location.Columns.AddColumn("new businessunitid");
             return service.RetrieveMultiple(qe).Entities.FirstOrDefault().GetAliasedValue<EntityReference>("new_businessunitid").Id;
        }

Thursday, February 24, 2011

IIS7 AppPool Controller

We have multiple Application Pools at my work that we sometimes have to go through and recycle all by hand. I'm a lazy programmer, so I made an app that does it for me. It also displays the PID and CPU utilization for each app pool, and allows you to completely start and stop all application pools, or one at a time. Enjoy!




It comes with the standard "Works On My Machine" disclaimer from Scott Hansleman:

Works On My Machine Disclaimer: This is released with exactly zero warranty or support. If it deletes files or kills your family pet, you have been warned. It might work great, and it might not.

Download Source Code Here

Download EXE Here

Tuesday, February 22, 2011

Getting % Processor Time for Process By PID

I had to search and search on how to do this, and now hopefully no one else will. It’s fairly easy to get the % CPU Usage for a process using the System.Diagnostics.PerformanceCounter (as long as you remember to sleep a second so you can get the a correct value and divide by the number of CPUs). That looks like this:



PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", processName, true);
pc.NextValue();
Thread.Sleep(1000);
int cpuPercent = (int)ppc.PerformanceCounter.NextValue() / Environment.ProcessorCount;


The PerformanceCounter constructor accepts the processName, but it doesn’t accept a process id, so if you had multiple processes with the same name (w3wp, svchost, etc.) you can’t get the value for a specific process easily. If there are multiple processes with the same name running, they’ll have names like “w3wp#1” and “w3wp#2”, where the 1 and the 2 are completely unrelated to ProcessId. I ended up creating this method to get the performance counter process name for a given process. You could be extra fancy and make it an extension method on the Process Class, but this works for now:



private string GetPerformanceCounterProcessName(int pid)
{
return GetPerformanceCounterProcessName(pid, System.Diagnostics.Process.GetProcessById(pid).ProcessName);
}

private string GetPerformanceCounterProcessName(int pid, string processName)
{
int nameIndex = 1;
string value = processName;
string counterName = processName + "#" + nameIndex;
PerformanceCounter pc = new PerformanceCounter("Process", "ID Process", counterName, true);

while (true)
{
try
{
if (pid == (int)pc.NextValue())
{
value = counterName;
break;
}
else
{
nameIndex++;
counterName = processName +
"#" + nameIndex;
pc =
new PerformanceCounter("Process", "ID Process", counterName, true);
}
}
catch (SystemException ex)
{
if (ex.Message == "Instance '" + counterName + "' does not exist in the specified Category.")
{
break;
}
else
{
throw;
}
}
}

return value;
}


Then calling it would look like this:


PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", GetPerformanceCounterProcessName(PID), true);
pc.NextValue();
Thread.Sleep(1000);
int cpuPercent = (int)ppc.PerformanceCounter.NextValue() / Environment.ProcessorCount;

Thursday, November 18, 2010

String Array UDT for Oracle, and Something Better

For work I was creating a simple website that allowed a user to batch multiple items together.  I was planning on taking the id’s of the items that they batched, and passing them into an Oracle SP, so they could be used in a “Where in” statement.  In MS-SQL you can hack together a solution doing some fancy string parsing, and the Execute query function, but you have lots of issues if your csv string is longer than 8000 characters.  I wanted to do it the “right” way in Oracle using a UDT (User Defined Type).



Basically to be able to pass an Oracle UDT as a parameter to a SP from C#, you have to create a C# class for your UDT which implements IOracleCustomType, INullable, and a factory to create the objects. First, here is the Oracle Code for the UDT and the SP:



create or replace

TYPE STRINGARRAY_UDT AS varray(3000) OF VARCHAR2(30);


create or replace

PROCEDURE TEST_ARRAY

(

  IN_MODELS IN STRINGARRAY_UDT,

  OUT_MODELS OUT SYS_REFCURSOR

) AS


BEGIN

  OPEN OUT_MODELS FOR

    SELECT * from Items WHERE id IN (SELECT * FROM table(IN_MODELS));

END TEST_ARRAY;



The C# code is rather simple as well, but you have to create a class for each UDT that you want to use.  This is a real pain (which leads me onto something better).  If you want to create a UDT with types that are not nullable, you have to maintain your own OracleStatus array to determine if the element is null or not, and pass it into the OractleUdt.SetValue and OracleUdt.GetValue.  The StringArrayUDTFactory get’s called by Oracle to create the object, it then uses reflection to call the FromCustomeObject or ToCustomObject methods, depending on whether you’re passing the UDT to oracle, or retrieving it from Oracle.



public class StringArrayUDT : IOracleCustomType, INullable
{
[
OracleArrayMapping()]
public string[] Array { get; set; }

public StringArrayUDT()
{
Array =
null;
}

public StringArrayUDT(string[] array)
{
Array = array;
}

#region INullable Members

public bool IsNull { get { return Array == null; } }

#endregion

#region IOracleCustomType Members

public void FromCustomObject(OracleConnection con, System.IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, 0, Array);
}

public void ToCustomObject(OracleConnection con, System.IntPtr pUdt)
{
Array = (
string[])OracleUdt.GetValue(con, pUdt, 0);
}

public override string ToString()
{
if (IsNull)
return "NULL";
else
{
return "StringArrayUDT('" + String.Join("', '", Array) + "')";
}
}

#endregion
}

/* StringArrayUDTFactory Class
** An instance of the StringArrayUDTFactory class is used to create
** StringArrayUDT objects
*/
OracleCustomTypeMapping("MySchema.STRINGARRAY_UDT")]
public class StringArrayUDTFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory
{
#region IOracleCustomTypeFactory Members

public IOracleCustomType CreateObject()
{
return new StringArrayUDT();
}

#endregion

#region IOracleArrayTypeFactory Members

public System.Array CreateArray(int numElems)
{
return new String[numElems];
}

public System.Array CreateStatusArray(int numElems)
{
return new OracleUdtStatus[numElems];
}

#endregion
}



Once all of this gets created, you can add your class as a parameter directly to an OracleCommand object for an SP.  But after doing all that work, with all of it’s custom code, my co-worker showed me a better way for this example, done entirely in Oracle.  The bulk of the work is done in this SplitByChar function:



FUNCTION SPLITBYCHAR

(

    p_list varchar2,

    p_del varchar2 := ','

) return split_tbl pipelined

is

    l_idx    pls_integer;

    l_list    varchar2(32767) := p_list;

    l_value    varchar2(32767);

begin

    loop

        l_idx := instr(l_list,p_del);

        if l_idx > 0 then

            pipe row(substr(l_list,1,l_idx-1));

            l_list := substr(l_list,l_idx+length(p_del));

        else

            pipe row(l_list);

            exit;

        end if;

    end loop;

    return;

END SPLITBYCHAR;



And then our Test Function becomes this:


create or replace


PROCEDURE TEST_ARRAY


(


  IN_MODELS IN varchar2,


  OUT_MODELS OUT SYS_REFCURSOR


) AS


BEGIN


  OPEN OUT_MODELS FOR


    SELECT * from Items WHERE id IN (SELECT * FROM table(SPLITBYCHAR(IN_MODELS)));


END TEST_ARRAY;

Where IN_MODELS is a csv string.  This puts a little more work on the Oracle database, but is much better on the application side of things.

Tuesday, October 19, 2010

Creating a ToDictionaryList Extension

ToDictionary

.Net 3.5 introduced the Enumerable.ToDictionary Method. It allows you to take a list of items, and convert them into a dictionary based on some key given as a lambda expression. For example, this code snippet for Snippet Compiler will put a weeks worth of dates into a Dictionary<int,DateTime>, so you could do a dictionary lookup by day:

public static void RunSnippet()
{
DateTime time = new DateTime(2228, 3, 22);
List<DateTime> items = new List<DateTime>();
items.Add(time);
items.Add(time.AddDays(1));
items.Add(time.AddDays(2));
items.Add(time.AddDays(3));
items.Add(time.AddDays(4));
items.Add(time.AddDays(5));
items.Add(time.AddDays(6));

var dayByDay = items.ToDictionary(d => d.Day);
foreach (var dayKeyValue in dayByDay)
{
WL(dayKeyValue.Key + " " + dayKeyValue.Value);
}
}

Which results in this output:

22 3/22/2228 12:00:00 AM
23 3/23/2228 12:00:00 AM
24 3/24/2228 12:00:00 AM
25 3/25/2228 12:00:00 AM
26 3/26/2228 12:00:00 AM
27 3/27/2228 12:00:00 AM
28 3/28/2228 12:00:00 AM
Press any key to continue...



ToDictionaryList


But what if the key you want to use, is not unique? You’ll end up getting this warning:

System.ArgumentException: An item with the same key has already been added.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)

I had this same issue and decided to create a ToDictionaryList() extension method that would return a Dictionary<key,List<item>>. Here is an example using the ToDictionaryList() grouping the dateTimes by day:



public static void RunSnippet()
{
DateTime time = new DateTime(2228, 3, 22);
List<DateTime> items = new List<DateTime>();
items.Add(time);
items.Add(time.AddHours(1));
items.Add(time.AddMinutes(1));
items.Add(time.AddDays(1));
items.Add(time.AddHours(26));

var results = items.ToDictionaryList(d => d.Day);
foreach (var result in results)
{
foreach (var item in result.Value)
{
WL(result.Key + " " + item);
}
}
}


Which results in this output:

22 3/22/2228 12:00:00 AM
22 3/22/2228 1:00:00 AM
22 3/22/2228 12:01:00 AM
23 3/23/2228 12:00:00 AM
23 3/23/2228 2:00:00 AM
Press any key to continue...


Basically it is two dictionary entries, one for the 22nd which is a List<DateTime> of 3 DateTimes, and one for the 23, which contains two items. You can even write a lambda expression for the type of item to be in the list. So in the last example, if you changed "var results = items.ToDictionaryList(d => d.Day);" to "var results = items.ToDictionaryList(d => d.Day, d => d.Hour); " item would in int and which would result in this output:

22 0
22 1
22 0
23 0
23 2
Press any key to continue...


The Code


Here is the code required to make the ToDictionaryList Possible:


public static class Extensions
{
private static Func<TElement, TElement> Instance<TElement>()
{
return delegate(TElement x) { return x; };
}

public static Dictionary<TKey, List<TSource>> ToDictionaryList<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return source.ToDictionaryList(keySelector, Instance<TSource>());
}

public static Dictionary<TKey, List<TElement>> ToDictionaryList<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
{
Dictionary<TKey, List<TElement>> dictionary = new Dictionary<TKey, List<TElement>>();
List<TElement> elements;
foreach (TSource local in source)
{
if (!dictionary.TryGetValue(keySelector(local), out elements))
{
elements = new List<TElement>();
dictionary.Add(keySelector(local), elements);
}
elements.Add(elementSelector(local));
}

return dictionary;
}
}



The Instance Method is used to return a delegate function that returns a single item of the given type (Basically a (c => c)). The ToDictionaryList() will loop through all items in the list, adding them to a new list if the key doesn’t already exist in the dictionary, or add the item to the already existing list in the dictionary. Pretty simple really.

Smile