DCSIMG
May 2009 - Posts - Baruch Frei
Sign in | Join | Help

May 2009 - Posts

XML Empty Namespace Attributes

Published at May 19 2009, 09:37 AM by BaruchF

When you create new node either using XmlDocument.CreateElement either using

XmlDocument.CreateNode and append it to some other node sometimes you get the new

node with an empty xmlns attribute.

For example this code:

string xmlStream = "<Root "+
                   "xmlns=\"http://blogs.microsoft.co.il/BLOGS/BaruchF\">"+
                   "<Node />"+
                   "</Root>";
XmlDocument doc = newXmlDocument();
doc.LoadXml(xmlStream);
XmlElement newNode = doc.CreateElement("Node");
doc.ChildNodes[0].AppendChild(newNode);

Will cause the xml to look like this:

<Root xmlns=http://blogs.microsoft.co.il/BLOGS/BaruchF>
    <
Node
/>
    <
Node xmlns=""
/>
</
Root>

This happens if up in the XmlDom tree at one of the predecessors of the new node

location there is a xml namespace declaration. what means that this code:

string xmlStream = "<Root>"+
      "<Node xmlns=\"http://blogs.microsoft.co.il/BLOGS/BaruchF\"/>"+
      "</Root>";
XmlDocument doc = newXmlDocument();
doc.LoadXml(xmlStream);
XmlElement newNode = doc.CreateElement("Node");
doc.ChildNodes[0].AppendChild(newNode);

Will result this xml:

<Root>
    <
Node xmlns="http://blogs.microsoft.co.il/BLOGS/BaruchF"
/>
    <
Node
/>
</
Root>

Of course sometimes you have to prevent it ,for example in the last post I spoke about

adding new file to csproj file programmatically if this empty xmlns attribute will appear

the project file will become corrupted and you want be able to load it.

How to avoid it ?

Calling the equivalent overloaded methods with null namespace doesn’t help,

neither removing the attribute:

XmlElement newNode = doc.CreateElement("Node",null);
newNode.Attributes.RemoveAll();

The only way to avoid it is to create the new node with exactly the same namespace as the

predecessor:

string xmlStream = "<Root "+
    "xmlns=\"http://blogs.microsoft.co.il/BLOGS/BaruchF\">"+
    "<Node />"+
    "</Root>";
XmlDocument doc = newXmlDocument();
doc.LoadXml(xmlStream);
XmlElement newNode = doc.CreateElement("Node",
                                   "http://blogs.microsoft.co.il/BLOGS/BaruchF");
doc.ChildNodes[0].AppendChild(newNode);

Will result the expected xml:

<Root xmlns=http://blogs.microsoft.co.il/BLOGS/BaruchF>
    <
Node
/>
    <
Node
/>
</
Root>

Tip: since the xml namespace is case sensitive the best way to do it is :

string xmlStream = "<Root " +
        "xmlns=\"http://blogs.microsoft.co.il/BLOGS/BaruchF\">"+
        "<Node />" +
        "</Root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlStream);
string namspaceURI = doc.ChildNodes[0].Attributes["xmlns"].Value;
XmlElement newNode = doc.CreateElement("Node", namspaceURI);
doc.ChildNodes[0].AppendChild(newNode);

Code Generation – Part 2:Project file parsing

Published at May 16 2009, 10:28 PM by BaruchF

As I mentioned in the last post adding file to the proj file (csproj) it’s  subject to another

post. So here it comes:

The project file in C# (with extension csproj) is an xml file that holds all information

about your project (configuration,references,files etc).

The files included in the project are stored in item group like this:

  <ItemGroup>
    <
Compile Include="Class1.cs" />

    <
Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup
>

So here is the way to programmatically  add new file to the project :

  • Get the list of current included files
  • check if the new file already exist
  • create new node named compile
  • append attribute named include with the new file path
  • append the new node to the item group
bool AddFileToProject(
                               string projectFile,
                               string filePath)
{
   
try
   
{
        XmlDocument csproj = new XmlDocument();
        csproj.Load(projectFile);
       
         //get list of included files
       
XmlNodeList filesIncluded =
            csproj.GetElementsByTagName("Compile");

       
//check if the file not already exist
       
if(filesIncluded.Cast<XmlNode>().Where(
                 f => f.Attributes["Include"].Value ==
                fileUrl).Select(f => f).Count() == 0)
        {
           
//save the project namespace
           
XmlAttribute namespaceURI = 
                               csproj.GetElementsByTagName(
                               "Project")[0].Attributes["xmlns"]; 
           //new file node
           XmlElement newFileNode =
                            csproj.CreateElement("Compile",
                                         namespaceURI.Value); 
            //new file path
           XmlAttribute newFilAettr = 
                             csproj.CreateAttribute("Include"); 
           
            newFilAettr.Value = filePath;
            newFileNode.Attributes.Append(newFilAettr);
            filesIncluded[0].ParentNode.AppendChild(newFileNode);
            csproj.Save(projectFile);
        }
        return true;
    }
    catch(Exception e)
    {
        Console.WriteLine(e.Message);
        return false;
    }
}

But, here some points to pay attention :

The new file path you send to the method is the relative path from the csproj to the file.

XmlNodeList implements only the IEnumerable interface and does not have implementation

of the generic IEnumerable<T> there for you can not query directly against the collection, 

and you have to explicitly cast it to XmlNode.

Here is more readable version of the query:

bool allreadyExists = (from file in filesIncluded.Cast<XmlNode>()
                                   where file.Attributes["Include"].
                                                      Value == filePath
                                   select file).Count() != 0;

As you can see in the new element creation I had to initialize it with namespace even

though the node of the file itself does not contain namespace attribute.

To understand why is that you will have to wait to the next post … coming soon. 

 

 

 

Simple Code Generator

Published at May 14 2009, 11:14 PM by BaruchF

פוסט זה נכתב בעברית מכיון שהוא המשך לפוסט שנכתב בעברית.

שלמה כתב על הנושא שמעצבן אותו לכתוב את כל קוד ההשמה של הערכים

מתוך ה Resources. והעלה פתרון של הגדרת ה Key של  ה Resource בפורמט

ControlID_ControlProperty"  ומתודה שקוראת בזמן העליה ב Reflection

את ה Resources ומשימה כל ערך למקומו המתאים.

אמממה… שלמה בעצמו העלה את בעית הביצועים כאשר משתמשים

ב Reflection ב Run Time.

כאשר קראתי את הפוסט, הדבר הראשון שעלה בדעתי הוא “רגע למה שלא נעשה

את זה ב Compile Time ?” איך ? ע”י Code Generation (מחולל קוד בעברית).

חילול קוד ניתן לממש בכמה דרכים: ע”י CodeDom (הסבר יפה למתחילים

כאן) אבל זה נושא שלם לפוסט אחר,או ע”י כתיבת קובץ קוד ע”י StringWriter רגיל.

מכיון שהפתרון כאן הוא מאד פשוט בחרתי להשתמש בStringWriter עטוף

ב Helper Class שנקראת IndentWriter שמצאתי פה היודעת לשמור את עומק

ההזחה ע”מ שהקוד גם ייראה יפה.

זה הקוד של ה IndentWriter :

using System;
using System.Collections.Generic;
using System.Text;

namespace CodeGenerator
{
    public class
IndentingWriter
  
{
        System.IO.StringWriter Writer = new System.IO.StringWriter();
        int Depth;
        public int IndentChars = 4;
        public void OutDent()
        {
            Depth--;
            if (Depth < 0) Depth = 0;
        }
        public void Indent()
        {
            Depth++;
        }
        public void Write(string value)
        {
            Writer.Write(value);
        }
        public void Write(string format, params object[] values)
        {
            Writer.Write(format, values);
        }
        public void WriteLine()
        {
            printIndenting();
            Writer.WriteLine();
        }
        public void WriteLine(string format, params object[] values)
        {
            printIndenting();
            Writer.WriteLine(format, values);
        }
        public void WriteLine(string value)
        {
            printIndenting();
            Writer.WriteLine(value);
        }
        void printIndenting()
        {
            for (int x = 0; x < Depth; x++)
            {
                for (int y = 0; y < IndentChars; y++)
                    Writer.Write(' ');
            }
        }
        public new string ToString()
        {
            return Writer.ToString();
        }
    }
}

הפתרון מורכב מהוספת פרויקט exe ל sln שיחזיק reference לפרויקט שלנו

וכאשר נריץ אותו הוא ידע לכתוב partial class המכיל את ההשמה של כל

הקונטרולים על הדף.

ראשית עלינו לדאוג שהפרויקט שלנו יתקמפל גם ללא מימוש המתודה של ההשמה

שאותה נממש אוטומטית בהמשך ולכן אם נקרא ב Page_Load למתודה שתעשה את

ההשמה מכיון שהיא עדיין לא מוגדרת הפרויקט לא יתקמפל.

לכן נשתמש ביכולת חדשה של .NET 3.5. הגדרת partial type כלומר כמו שניתן להגדיר

partial class בכמה מקומות והקומפיילר יודע למזג אותם לClass אחד  כעת ניתן להגדיר

כל type כ partial.

ולכן נגדיר את הProtoType של המתודה ואז הפרויקט יתקמפל:

partial void AssignValuesFromResource(); 
protected void Page_Load(object sender, EventArgs e)
{
    AssignValuesFromResource(); 
}
נוסיף קובץ ריק לפרויקט בשם Default.part.cs הקובץ כרגע נשאר ריק
 
(ניתן להוסיף גם אוטומטית ע”י ה Code Generator הדבר דורש פירסור של
 
קובץ הcsproj אבל גם זה כבר חומר לפוסט אחר…)
 
כעת נשלח ל Generator שלנו את הnamespace ושם הclass ולאיזה קובץ לשמור את התוצר
 
class Program ייראה כך :
 
namespace CodeGenerator
{
    class
Program
  
{
        static void Main(string[] args)
        {
            GenerateClass();
        }

        private static void GenerateClass()
        {
            ClassGenerator cg = new ClassGenerator(
                   "CodeGeneration", "_Default");
            cg.Build();
            cg.SaveCode(
            @"..\..\..\CodeGeneration\Default.part.cs");
        }
    }
}
 
ועכשיו החלק הכי מעניין והוא ה Class Generator:
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Web.UI;
using TestCodeGenerator;

namespace CodeGenerator
{
    public class
ClassGenerator
  
{
        string ClassNamespace;
        string ClassName;

        public ClassGenerator( string NameSpace,string Name)
        {
            ClassNamespace = NameSpace;
            ClassName=Name;
        }

        public string SourceCode = null;

        public void Build()
        {
            IndentingWriter tw = new IndentingWriter();
            tw.WriteLine("//-----------------------------------"+
                              "--------------------------//");
            tw.WriteLine("// ....... This Class Was Written By "+
                                "Automated Tool .......... //");
            tw.WriteLine("// ............. Class Code Generator "+
                                "Example ................ //");
            tw.WriteLine("//------------------------------------"+
                                "-------------------------//");
            tw.WriteLine();           
            tw.WriteLine();
            tw.WriteLine("namespace {0}", ClassNamespace);
            tw.WriteLine("{");
            tw.Indent();
            tw.WriteLine("public partial class {0}", ClassName);
            tw.WriteLine("{");
            tw.Indent();
            tw.WriteLine("partial void AssignValuesFromResource()");
            tw.WriteLine("{");
            tw.Indent();           
           
            Typetype = typeof(TestCodeGenerator.Default);
            PropertyInfo[] properties = type.GetProperties(
                   BindingFlags.Public | BindingFlags.Static);
            var stringProperties = from property in properties
                                   where property.PropertyType ==
                                   typeof(string)
                                   select new Pair(
                                       property.Name,
                                       property.GetValue(null, null));

            foreach (var item instringProperties)
            {
                string[] arr = item.First.ToString().Split('_');
                if(arr.Length == 2)
                {
                    tw.WriteLine("{0}.{1}={2}.{3};",
                    arr[0],arr[1],"Default",item.First.ToString());                   
                }
            }                       
            tw.OutDent();
            tw.WriteLine("}");
//method  
            tw.OutDent();
            tw.WriteLine("}");
//class
          
tw.OutDent();
            tw.WriteLine("}");
// namespace

          
SourceCode = tw.ToString();
            return;
        }

        public void SaveCode(stringPath)
        {
            if(string.IsNullOrEmpty(SourceCode)) this.Build();          
            System.IO.StreamWriter sw =
                newSystem.IO.StreamWriter(Path);
            sw.Write(SourceCode);
            sw.Flush();
            sw.Close();
        }
    }
}
 
מה שבעצם עושה הקוד הוא כותב את  ה Partial Class ומשתמש במתודה ששלמה
 
הגדיר רק שה Iterator במקום להשים את הערכים כותב את קוד ההשמה …
 
(שיניתי את ה Accses Modifier של ה Resources ל Public אבל באותה מידה ניתן
 
להשתמש ב Attribute : InternalVisibleTo ע”מ לגשת לערכים )
 
וזה התוצר של המחולל :
 
namespace TestCodeGenerator
{
    public partial class
Default
  
{
        partial voidAssignValuesFromResource()
        {
            lblBye.Text=Default.lblBye_Text;
            lblHello.Text=Default.lblHello_Text;
            rfvUserName.ErrorMessage=
                Default.rfvUserName_ErrorMessage;
        }
       
    }
}

כעת נותר רק לקמפל שוב את הפרויקט ביחד עם הClass החדש.
 
 
דוגמא בסיסית ניתן להוריד מכאן.
 
הוסיפו לדף ה asp קונטרולים דאגו להוסיף את ה Resource בפורמט המתאים
 
“ControlID_ControlProperty” קמפלו והריצו את ה ClassGenerator ולאחר מכן
 
קמפלו והריצו את CodeGeneration.

TFS 2008 – Get Latest Version when Check Out

Published at May 04 2009, 11:08 PM by BaruchF

As TFS users probably know TFS 2005 (unlike SourceSafe and other Source Control

tools) didn’t perform get latest version when checking out.The net is full with

discussions is it by design ? is this right behavior ?

(We also developed a little add in for VSTS 2005 to support this behavior).

 

Any way, in TFS 2008 this option is built in.

Can be found in Tools—>Options—>Source Control—>

Visual Studio Team Foundation Server—>Get latest version of item when check out

image

NOTE: It works only on TFS 2008 and VS 2008 client.

 

But, there is project setting that overrides your environment setting:

right click on team project:

image

And enable/disable this option:

image

 

Pay attention, if you enable this option on team project level, when user

checks out any old version he will get latest version without prompting .

The only way to know which version will be really check out is to notice

the bottom line on the Check out window:

Checking out latest version from source control

versus:

Checking out local version from source control

 

Now,if you are developer and your project admin enabled this option,

and you really have/want to check out an old version (by design remember ?).

There is no way you can do it unless you get an old version, save local copy,

check out the latest version and override it with your local copy - annoying.

 

The behavior I was expecting is to inform the user that he is about to check

out an old version and ask him if he wants to perform Get latest or not.

Instead of automatically override his environment or on the other hand

check out an old version without prompting. (BTW, this is exactly the way we

implemented it on TFS 2005 add in).

 

As developers always say “it’s not a bug it’s a feature…

maybe in the next version” :-)

בלוגר החודש

Published at May 03 2009, 09:48 PM by BaruchF

האמת, די הופתעתי למצוא את עצמי כבלוגר החודש.

 

בהתחלה בתור בלוגר חדש אתה מציץ מדי פעם, אולי החודש…

אבל אח”כ אתה שוכח עד כדי כך שחבר צריך להתקשר ולשאול :

“ראית מי בלוגר החודש ?”.

 

אז תודה רבה לכולם וכמובן לגיא שנפגשנו בDevAcademy רק שמע

שאני מעונין לפתוח בלוג: “שלח לי מייל” ולמחרת היה לי בלוג.

 

אני מקוה שהבחירה תדרבן אותי לעשות סוף סוף את כל מה שתכננתי:

  • לפרסם את הפוסטים שבQueue (עדיין לא בשלים)
  • לשפצר את הבלוג (כמו ששי דורש)
  • להחליף את התמונה (בשביל רועי שטוען שהיא משמינה)

וכל הדברים החשובים שאיכשהו בין המשפחה לבין העבודה נדחקים מעט.

אבל עכשיו, המעמד מחייב…