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

Wednesday, May 5, 2010

Correctly Creating Classes Using xsd.exe

Not a lot of people are familiar with the xsd.exe that ships with Visual Studio. It allows you to create XML/classes from XML/classes. The following are the 4.0 xsd.exe capabilities:
XDR to XSD
Generates an XML schema from an XML-Data-Reduced schema file. XDR is an early XML-based schema format.
XML to XSD
Generates an XML schema from an XML file.
XSD to DataSet
Generates common language runtime DataSet classes from an XSD schema file. The generated classes provide a rich object model for regular XML data.
XSD to Classes
Generates runtime classes from an XSD schema file. The generated classes can be used in conjunction with System.Xml.Serialization.XmlSerializer to read and write XML code that follows the schema.
Classes to XSD
Generates an XML schema from a type or types in a runtime assembly file. The generated schema defines the XML format used by System.Xml.Serialization.XmlSerializer.
It I’m a big fan of using xml schemas (XSDs) to generate classes that can be used to serialize and deserialize objects to and from XML (the XSD to Classes functionality listed above). If your schema changes, just rerunning xsd.exe for the correct schema updates the classes. No manually changes have to be made, including serialization code. It’s a beautiful thing. The problem has been, how do you set up your classes to automatically recompile with changes to the schema, and how do you deal with schemas that import other schemas?

XSDs Importing other XSDs

A common issue that developers of XSDs run into is violating the DRY principle repeatedly with XSD types. For example, let’s say you create a calendar meeting request service that has two XSDs, one for the request XML and one for the response XML. You’ve defined a xs:complexType “Meeting” that includes the date and location:
  <xs:complexType name="Meeting">
    <xs:sequence>
      <xs:element name="Location" type="xs:string"/>
      <xs:element name="Date" type="xs:date"/>
    </xs:sequence>
  </xs:complexType>
But you want to use it in both the request and the response XML. You could just copy and past it into both XSD files, and it will validate just fine, but if you use xsd.exe to generate your classes, it’s going to create two classes of type Meeting, which will cause a compiler error. You could have a separate namespace for each class, but then you’re definitely violating DRY. The answer is to place the Meeting type in a separate XSD and then reference it from both your request and your response XSD. This results in the XSDs below

C:\Solution\Project\Types.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Types"
targetNamespace="http://www.Example.com/Types"
elementFormDefault="qualified"
xmlns="http://www.Example.com/Types"
xmlns:mstns="http://www.Example.com/Types"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:complexType name="Meeting">
    <xs:sequence>
      <xs:element name="Location" type="xs:string"/>
      <xs:element name="Date" type="xs:date"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>


C:\Solution\Project\Request.xsd
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:myTypes="http://www.Example.com/Types">
  <xs:import namespace="http://www.Example.com/Types" schemaLocation="Types.xsd" />
  <xs:element name="Request">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="RqstMeeting" type="myTypes:Meeting"/>
        <xs:element name="RqstName" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

C:\Solution\Project\Response.xsd
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:myTypes="http://www.Example.com/Types">
  <xs:import namespace="http://www.Example.com/Types" schemaLocation="Types.xsd" />
  <xs:element name="Response">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Accepted" type="xs:boolean"/>
        <xs:element name="AlternateMeeting" type="myTypes:Meeting" minOccurs="0"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>


Now we’ve defined our Meeting type in one file, and reused it in both our Request.xsd and Response.xsd.


Getting xsd.exe To Import XSDs

Now that the type has been defined in another file, the xsd.exe will generate this error if you attempt to create the create the Request XML:
C:\Solution\Project>xsd.exe Request.xsd /c
Schema validation warning: Type 'http://www.Example.com/Types:Meeting' is not declared.
Warning: Schema could not be validated. Class generation may fail or may produce incorrect results.
Error: Error generating classes for schema 'C:\Solution\Projects\Request'.

- The datatype 'http://www.Example.com/Types:Meeting' is missing.
If you would like more help, please type "xsd /?".
This is due to the fact that the xsd.exe does not use the schemaLocation hint to find the imported schema. You’ve got to include it as a parameter. in your xsd.exe call:
C:\Solution\Project>xsd.exe Types.xsd Request.xsd /c
This will generate one file call Request.cs that has a Request class, and a Meeting class. Now we just need to create the Response class and we’re good to go. But wait… running “C:\Solution\Project>xsd.exe Types.xsd Response.xsd /c” will create a different file, Response.cs, that contains a Response class and a duplicate Meeting class. Now we’re stuck with another compiler error and no longer DRY.


Getting xsd.exe To Not Create Duplicate Classes

This is a simple fix, but it took me a long time to figure out. You have to use xsd.exe to compile all of your classes at once, so rather than running two separate commands, you just need to run one:
C:\Solution\Project>xsd.exe Types.xsd Request.xsd Response.xsd /c
Now you have one file, Response.xsd, with all three classes in it.

Getting Visual Studio 2010 To Auto Recompile XSD Generated Classes

Using the Project Build Events, you can set the project to always recompile the XSD classes each time you build the project. It is also helpful to rename the file so it isn’t always the name of the last XSD file passed to xsd.exe. Here are the Pre-build event command line values required to auto build the XSD classes and rename the file to XsdGeneratedClasses.cs:

EDIT suggested by Jamie instead of the Registry hack (it worked back in the day for me, don't know if it still does), he suggests using "$(TargetFrameworkSDKToolsDirectory)xsd.exe" to find the path of the xsd.exe. Thanks Jamie!

"$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A\@InstallationFolder)\bin\NETFX 4.0 Tools\xsd.exe" "$(ProjectDir)Request.xsd" "$(ProjectDir)Response.xsd" "$(ProjectDir)Types.xsd" /c /o:"$(ProjectDir)"
move "$(ProjectDir)Types.cs" "$(ProjectDir)XsdGeneratedClasses.cs"

Now whenever the project get’s built, the XSD generated classes will always be rewritten by xsd.exe



Extending XSD Generated Classes

Don’t forget that classes created by xsd.exe are all partial classes. It’s a good idea to add default constructors and logic in a separate partial class in a different file. It’s especially helpful for initializing arrays since xsd.exe generated classes use arrays and not ArrayLists or Generic Lists. This allows you to add logic, that won’t be changed when the class is regenerated.


Serializing/Deserializing XSD Generated Classes

Now your code for Serializing and Deserializing your objects is as simple as this:
To Serialize:

XmlSerializer s = new XmlSerializer(typeof(Request));System.IO.TextWriter w = new System.IO.StreamWriter(@"C:\Request.xml");s.Serialize(w, new Request());w.Close();


To Deserialize:

XmlSerializer s = new XmlSerializer(typeof(Request));Request request;System.IO.TextReader r = new System.IO.StreamReader("request.xml");request = (Request)s.Deserialize(r);r.Close();

Wednesday, April 21, 2010

How To Configure AllMargins Extension For Visual Studio 2010

UPDATE: Removed non AllMargins registry settings. Thanks David Pugh for letting me know.


UPDATE 2: Productivity Power Tools now combines most of the major features of AllMargins, due to David Pugh himself porting it over. Consider using it instead.


AllMargins is a VS 2010 extension that basically replaces Rockscroll from VS2005 and VS2008. It combines multiple extensions into one “All” inclusive extension. Here is an example of what mine looks like with a few features pointed out:

image

It looks pretty good now, but when you first install it, it is configured for a white background, rather than black. You can change the colors via the registry at \HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor, but I couldn’t find any documentation online as to what color controls what. I downloaded the code from code.msn.microsoft.com and discovered how it all worked.

AllMargin Extensions

AllMargins contains multiple extensions, but all of their configurable settings are located in the registry at \HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor. I’ve listed them by extension below along with the introduction and usage text I’ve pulled together from the readme files in the projects. Just edit the appropriate registry setting and open up a new window in VS2010, to see any changes you’ve made.

CaretMargin

This extension adds a margin to the OverviewMargin that shows the location of the caret and all words that match the word the caret is on.

You will see colored rectangles drawn that correspond to the location of the caret and all words that match the word the caret is in or adjacent to.

Registry Name Description
CaretMargin/MarginEnabled Should the CaretMargin be displayed
CaretMargin/MarginWidth Width of the CaretMargin (in pixels)
CaretMargin/CaretColor Color used to draw the location of the caret. Not used if Alpha = 0
CaretMargin/MatchColor Color used to draw the location of matching words. Not used if Alpha = 0


OverviewMarginImpl

This extension creates a margin displayed along the right edge of the editor. It, like the vertical scroll bar, logically maps to the entire file. By default, it acts as if all elided regions are expanded (though the portions of the file contained in a elided regions are highlighted). The OverviewMargin also can highlights the portions of the file that are above, contained in and below the text shown in the editor.

The OverviewMargin is a container for other margins (that all use a common mapping between a location on the screen and a position in the file). This extension includes two of these contained margins: the OverviewChangeTracking margin (which acts like the change tracking margin from the left side of the editor, but applied to the entire file) and the OverviewMark margin (which allows 3rd parties to draw colored marks to indicate positions of interest in the file).

Moving the mouse into the OverviewMargin will, after a brief delay, show a tooltip. The default tip for the OverviewMargin shows the contents of the file at the corresponding location but other margins contained in the OverviewMargin may provide more interesting tips.

Left clicking in the OverviewMargin to scroll to center the corresponding location in the editor. Left clicking in an elided region will center the(collapsed) elided region in the editor.

Double-left-clicking in the OverviewMargin will expand any elided regions that contain the corresponding location and then center that location in the editor.

Right-clicking in the OverviewMargin brings up an option menu that allow you to change:
Whether or not the OverviewMargin acts as if all elided regions are expanded (if they are not, then the OverviewMargin uses the same mapping between a location in the margin and a position in the file as the vertical scroll bar).
Whether or not the OverviewMargin will show the default tip when the mouse is inside the margin.

Registry Name Description
OverviewMargin/ChangeTrackingMarginEnabled Should the change tracking margin be displayed
OverviewMargin/MarkMarginEnabled Should the mark margin be displayed
OverviewMargin/ExpandElisionsInOverviewMargin Are elisions expended in the overview margin?
OverviewMargin/OverviewMarginPreviewSize Number of lines to show in the default tip for the overview margin. If 0, do not show any tip by default
OverviewMarginImpl/ChangeTrackingMarginWidth Width of the ChangeTrackingMargin (in pixels)
OverviewMarginImpl/MarkMarginWidth Width of the MarkMargin (in pixels)
OverviewMarginImpl/ElisionColor Color used to highlight elided text as ARGB. Not used if Alpha = 0.
OverviewMarginImpl/OffScreenColor Color used to highlight portions of the file not shown in the editor as ARGB. Not used if Alpha = 0.
OverviewMarginImpl/VisibleColor Color used to highlight portions of the file shown in the editor as ARGB. Not used if Alpha = 0.


Structure Adornment

This extension displays vertical lines in the editor to help show the block structure of the code file. Different colors are used to indicate different types of blocks.

Moving the mouse over one of the colored lines will, if the line extends off the top of the editor, create a tooltip at the top of the view that provides some context on what is out of view.

Registry Name Description
StructureAdornment/ClassColor Color used to show the scope of class definitions as ARGB
StructureAdornment/ConditionalColor Color used to show the scope of conditionally executed code as ARGB
StructureAdornment/LoopColor Color used to show the scope of loop bodies as ARGB
StructureAdornment/MethodColor Color used to show the scope of method bodies as ARGB
StructureAdornment/UnknownColor Color used to show the scope of unknown blocks as ARGB
StructureAdornment/Enabled Should the structure adornments be shown?


StructureMargin

This extension adds a margin to the OverviewMargin that shows the block structure of the code file. Different colors are used to indicate different types of blocks.

Moving the mouse over the structure margin will create a tooltip that provides some context on what is in the file at that location.

Registry Name Description
StructureMargin/ClassColor Color used to show the scope of class definitions as ARGB
StructureMargin/ConditionalColor Color used to show the scope of conditionally executed code as ARGB
StructureMargin/LoopColor Color used to show the scope of loop bodies as ARGB
StructureMargin/MethodColor Color used to show the scope of method bodies as ARGB
StructureMargin/UnknownColor Color used to show the scope of unknown blocks as ARGB
StructureMargin/MarginEnabled Should the StructureMargin be displayed
StructureMargin/MarginWidth Width of the StructureMargin (in pixels)
StructureMargin/MethodEllipseColor Color used to draw the ellipse that shows the scope of method bodies as ARGB. Not used if Alpha = 0


My Settings

Here are my setting that I use. If you want to use them, just save them as a .reg file to add/update them in the registry


Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Text Editor]
"StructureAdornment/ClassColor"="#FF5F7D7C"
"StructureAdornment/ConditionalColor"="#FF005300"
"StructureAdornment/LoopColor"="#FFFF0000"
"StructureAdornment/MethodColor"="#FF222290"
"StructureAdornment/UnknownColor"="#FF362844"
"StructureAdornment/Enabled"="True"
"OverviewMargin/ExpandElisionsInOverviewMargin"="True"
"OverviewMargin/PreviewSize"="20"
"OverviewMarginImpl/ElisionColor"="#404252FF"
"OverviewMarginImpl/OffScreenColor"="#FF000000"
"OverviewMarginImpl/VisibleColor"="#88444444"
"OverviewMargin/ChangeTrackingMarginEnabled"="True"
"OverviewMarginImpl/ChangeTrackingMarginWidth"="9"
"OverviewMargin/MarkMarginEnabled"="True"
"OverviewMarginImpl/MarkMarginWidth"="8"
"CaretMargin/Enabled"="True"
"CaretMargin/CaretColor"="#AADDFFFF"
"CaretMargin/MatchColor"="#FF6F8474"
"CaretMargin/MarginWidth"="7"
"StructureMargin/MarginEnabled"="True"
"StructureMargin/MarginWidth"="25"
"StructureMargin/MethodEllipseColor"="#20DDDDDD"
"StructureMargin/ClassColor"="#FF38E4DB"
"StructureMargin/ConditionalColor"="#FFAF22C8"
"StructureMargin/LoopColor"="#FF0AC034"
"StructureMargin/MethodColor"="#FFE86D0E"
"StructureMargin/UnknownColor"="#FFE8DE1B"