SQL Server 2008, Entity Framework, DAL, LINQ and Unit Test

Microsoft is making our lives too easy :)  Below I show all of the components necessary to transfer a XML Parameter to a SQL Server 2008 stored procedure:

Once this code is in place there are only two places that will require update if there is a structure change - the following data class (implements IXMLSqlParameter) and the actual stored procedure that recieves the resulting XML record.

namespace Business.Interface.Entities

{

    /// <summary>

    /// Facility Data object

    /// </summary>

    public class FacilityData : IXMLSqlParameter

    {

        public int ID { get; set; }

        public string Name { get; set; }

        /// <summary>

        /// Overridden for easier viewing in debugger

        /// </summary>

        /// <returns></returns>

        public override string ToString()

        {

            return string.Format("ID:{0} Name:{1}", ID, Name);

        }

        #region IXMLSqlParameter Members

        /// <summary>

        /// Build XML Parameter string

        /// </summary>

        /// <returns></returns>

        public string GetXMLSqlParameter()

        {

            XElement customer = new XElement("facility");

            customer.Add(new XElement("facilityID"new XAttribute("value", ID)));

            customer.Add(new XElement("facilityName"new XAttribute("value", Name)));

            return customer.ToString();

        }

        #endregion

    }

}

 

 

Below I show the unit test that will initialize the above class, create a new record from it and then modify the newly created record:

 

 

[TestMethod]

public void CanCreateFacilityRecordEF()

{

    // Get configuration string (CnEF - base pulls from Web.Config)

    using (PatientDBEntities context = new PatientDBEntities(CnEF))

    {

        string MockCreate = "UnitTestFacility";

        string MockUpdate = "UPDATEUnitTestFacility";

 

        // TEST CREATE

        FacilityData data = new FacilityData { ID = 0, Name = MockCreate };

        // Call Facility Create/Update method (ID=0 is CREATE)

        context.Facility_CU(thisnew FacilityDataEventArgs { FacilityData = data });

        // Find the record we just created

        FacilityData recordExists = context.GetFacilityList()

            .FirstOrDefault<FacilityData>(f => f.Name == MockCreate);

        Assert.IsNotNull(recordExists, "Could not find CREATE record!");

        // TEST UPDATE

        data.ID = recordExists.ID;

        data.Name = MockUpdate;

        context.Facility_CU(thisnew FacilityDataEventArgs { FacilityData = data });

        recordExists = context.GetFacilityList()

            .FirstOrDefault<FacilityData>(f => f.Name == MockUpdate);

        Assert.IsNotNull(recordExists, "Could not find UPDATE record!");

    }

}

 

 

 

 

To simplify life my DAL is actually an Entity Framework context partial class - it follows:

  

 

 

namespace Data.WCF

{

    public partial class PatientDBEntities : ObjectContext

    {

        public ResponseEventArgs Facility_CU(
               object sender, 
               FacilityDataEventArgs e)

        {

            ResponseEventArgs args =
                new ResponseEventArgs { Message = "Updating Facility" };

            try

            {

                // Setup SQL XML Parameter

                object[] para = { new SqlParameter("xmlData",

                                              e.FacilityData.GetXMLSqlParameter()) };

                // Create command for stored procedure

                DbCommand command = this.CreateStoreCommand(
                      "usp_Facility_CU", CommandType.StoredProcedure, para);

                // Execute stored procedure

                using (Connection.CreateConnectionScope())

                    args.StatusID = command.ExecuteNonQuery();

            }

            catch (Exception ex)

            {

                args.Exception = ex;

            }

            return args;

        }

    }

}

 

 

All that remains is a stored procedure CLICK HERE FOR WEBCAST of stored procedure in action:

 

 

 

ALTER PROCEDURE [dbo].[usp_Facility_CU](@xmlData xml) AS

BEGIN

-- Retrieve FacilityID and FacilityName fields from XML parameter

declare @id int = @xmlData.value('/facility[1]/facilityID[1]/@value', 'int')

declare @name varchar(50) = @xmlData.value('/facility[1]/facilityName[1]/@value', 'varchar(50)')

-- If the ID already exists then update the record

if EXISTS (select @id from Facility where FacilityID = @id)

  BEGIN

    update Facility

      set FacilityName = @name

    where FacilityID = @id

    -- return ID for consistent return value

    select @id as PrimaryKey

  END

else -- If the ID doesn't exists then create it

 

  BEGIN

    insert into Facility (FacilityName) values (@name)

    -- return newly inserted record ID

    select @@IDENTITY as PrimaryKey

  END 

END

 


Tags: , , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQ to XML - creating an XML file from data collection

One of the final requirements for our MAMLConverter project is to output the processed html tags (which resides in a List<ElementEnt> collection) and generate a XML file from it. 

Our Data Layer (of type IHTMLData) has a SaveData() method which does the job nicely using LINQ to XML:

public bool SaveData(List<ElementEnt> dataList)

{

    // TODO: Use filename (xml extension) of file submitted to workflow

    string fileName = "temp.xml";

 

    var xml = new XElement("developerConceptualDocument",

            new XAttribute("snlmx", Resources.snlmx),

            new XAttribute("snlmx_xlink", Resources.snlmx_xlink),

        (from d in dataList

        where d.MAMLTag != null  && !d.IsIgnore  && !d.IsCloseTag

        select new XElement(d.MAMLTag, d.Content)));

 

    // Final cleanup.  LINQ XML complains if we attempt to use

    // xmlns as a tag so we'll resolve after it is done above.

    xml.ToString()

        .Replace("snlmx_xlink", "xmlns:xlink")

        .Replace("snlmx", "xmlns")

        .StrToFile(fileName);

 

    // TODO: Remove - FOR DEVELOPMENT PURPOSES

    Process.Start(fileName);

    return true;

}
The data structure it has to work with follows:

 

 

[TestMethod]

public void TestSaveProcessedListAsXML()

{

    BootStrapper bootStrap = new BootStrapper();

    string fileName = string.Format(mockFileName, CurrentDir);

 

    IHTMLData dll = bootStrap.UnityContainer.Resolve<IHTMLData>();

    if (dll.LoadData(fileName))

    {

        MAMLState state = bootStrap.UnityContainer.Resolve<MAMLState>();

        state.HtmlData = dll.GetHtmlData();

 

        using (MAMLWorkflowRuntime wfrt =

            new MAMLWorkflowRuntime(bootStrap.UnityContainer))

        {

            wfrt.Start(typeof(HtmlToMAML));

            Assert.IsTrue(dll.SaveData(wfrt.State.Data.HtmlData)); 

            Assert.AreEqual(22, wfrt.State.Data.HtmlData.Count); 

        }

    }

    else

        Assert.Fail(dll.Exception.Message);

 

    Assert.Fail("Keep Unit Test checked for development");

}

Note below that we only have a few tags currently defined (resource file and TagCommands) so we still have some work to do.  We'll also have to do some special post processing but for the most part have the core requirement's infrastructure in place.  I've been able to complete the input/output of data easily/efficiently because of LINQ to XML.

 BEFORE

AFTER
 


Tags: , , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQ - Projection eases parsing HTML content into data collection

In regards to my http://www.CodePlex.com/MAMLConverter project I have completed the Workflow infrastructure (integrated Unity and logging).  The next requirement is to pass the Workflow a collection of HTML data so that it can translate HTML tags to MAML tags.   The entity I'm going to start with follows:

namespace Workflow.Library.Entities

{

    public class ElementEnt

    {

        public bool IsCloseTag { get; set; }

        public string Tag { get; set; }

        public int TagLen { get; set; }

        public int Len { get; set; }

        public string Content { get; set; }

        public string OriginalContent { get; set; }

    }

}

This Use Case requirement will have me convert the following HTML into a List<ElementEnt> collection:

With the help of my StringExtensions class (many of the extensions written specifically for this task) I'm able to get my results with the query that follows:

Now all that remains is to populate my List<ElementEnt> collection.   LINQ Projection makes this task easy with the few following code changes:

All that remains is to move this code into its own class and create the Unit Test.  I'll then be ready to create the Workflow activities that will handle each of the tags.

String Extensions source follows:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;

using System.Web;

 

namespace Workflow.Library.Extensions

{

    public static class StringExtensions

    {

        /// <summary>

        /// HTMLs the decode.

        /// </summary>

        /// <param name="data">The data.</param>

        /// <returns></returns>

        public static string HtmlDecode(this string data)

        {

            return HttpUtility.HtmlDecode(data);

        }

        /// <summary>

        /// HTMLs the encode.

        /// </summary>

        /// <param name="data">The data.</param>

        /// <returns></returns>

        public static string HtmlEncode(this string data)

        {

            return HttpUtility.HtmlEncode(data);

        }

 

 

        /// <summary>

        /// Gets the length of the tag.

        /// </summary>

        /// <param name="da">The da.</param>

        /// <returns></returns>

        public static int GetTagLength(this string da)

        {

            int offsetSP = da.IndexOf(' ');

            int offset = da.IndexOf('>');

            if (offsetSP > 0 && offsetSP < offset)

                offset = offsetSP;

            if (offset < 1)

                offset = da.Length;

            //return offset;

            return offset;

        }

 

        /// <summary>

        /// Gets the tag.

        /// </summary>

        /// <param name="da">The da.</param>

        /// <returns></returns>

        public static string GetTag(this string da)

        {

            int offset = GetTagLength(da);

            string retValue = da.Substring(0, offset);

            if (retValue.StartsWith("/"))

                retValue = retValue.Substring(1);

            if (retValue.EndsWith(">"))

                retValue = retValue.Substring(0, retValue.Length - 1);

            return retValue;

        }

 

        /// <summary>

        /// Gets the content.

        /// </summary>

        /// <param name="da">The da.</param>

        /// <returns></returns>

        public static string GetContent(this string da)

        {

            int offset = da.GetTagLength() + 1;

            if (offset >= da.Length)

                return "";

            else

                return da.Substring(offset);

        }

 

 

        /// <summary>

        /// Send string to specified filename

        /// </summary>

        /// <param name="data"></param>

        /// <param name="fileName"></param>

        /// <returns></returns>

        public static bool StrToFile(this string data, string fileName)

        {

            //Check if the sepcified file exists

            if (System.IO.File.Exists(fileName) == true)

            {

                // If so then Erase the file first as in this case

                // we are overwriting

                System.IO.File.Delete(fileName);

            }

 

            //Create the file if it does not exist and open it

            FileStream oFs = new

                FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite);

 

            //Create a writer for the file

            StreamWriter oWriter = new StreamWriter(oFs);

 

            //Write the contents

            oWriter.Write(data);

            oWriter.Flush();

            oWriter.Close();

 

            oFs.Close();

 

            return true;

        }

 

        /// <summary>

        /// Return file contents as string

        /// </summary>

        /// <param name="cFileName"></param>

        /// <returns></returns>

        public static string FileToStr(this string cFileName)

        {

            //Create a StreamReader and open the file

            StreamReader oReader = System.IO.File.OpenText(cFileName);

 

            //Read all the contents of the file in a string

            string lcString = oReader.ReadToEnd();

 

            //Close the StreamReader and return the string

            oReader.Close();

            return lcString;

        }

    }

}

 


Tags: , , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQPad - debugging imported DLLs in Vista environment

In an earlier BLOG I show how extensible LINQPad is.  I encountered an odd behavior when attempting to attach to LINQPad while debugging from my Vista box.  It didn't work; at least not directly (works great from my XP box at work).

Oddly, the behavior under Vista, when the following steps are not complied with, is that the Visual Studio Debugger treats LINQPad like a Web application; a Script Documents folder appears and there will be two scripts shown.   The scripts contain the HTML contents of the top and bottom LINQPad windows and my breakpoints in code are not respected.

The work-around requires me to follow these steps - when complied with I can consistently step into my code.

  1. With Visual Studio 2008 opened set my breakpoint
  2. Launch LINQPad and open script (do not run)
  3. In Visual Studio
    • Click on Debug
    • Attach to process
    • Select LINQPad
    • Click Attach button
    • The breakpoint in Visual Studio will complain (won't be solid circle)
  4. Switch back to LINQPad and run script.  For the duration of the attached debugging session debugging will work.  Once you stop debugging you'll have to exit LINQPad and start over from step 2 above.

Note: I thought perhaps the problem was that I hadn't yet installed Visual Studio SP1 (long process) so I installed it and have the same behavior. 

 


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQ(PAD) to the rescue

The requirement - move Amarillo College Lamplight's newsletter users from a system I wrote a few years ago (in .NET 1.1) to the new CASK Newsletter; we're moving data from a XML file to a SQL Server table.   Since this is a labor of love (we volunteer our services to the children's theatre) it would be nice to keep it short and simple; otherwise higher priorities have a tendency to back-burner their requirements.

Enter stage-left, LINQ and LINQPad!  With a few lines of code the following:

 <?xml version="1.0" standalone="yes"?>
<Newsletter>
  <User>
    <Email>Diane@Di-ForGod.com</Email>
    <Name>Diane Kratochvil</Name>
    <Relation>Robert's Mom</Relation>
    <Validated>:)</Validated>
    <guid>fbbf0f8a-91a9-4d5d-93a5-94b26b47943f</guid>
  </User>
  <User>
    <Email>bill@global-webnet.com</Email>
    <Name>Bill Kratochvil</Name>
    <Relation>Robert's Dad</Relation>
    <Validated>:)</Validated>
    <guid>37922a89-b5d6-42bb-a34a-282ff5eea439</guid>
  </User>
  <User>
    <Email>Koetting-cd@actx.edu</Email>
    <Name>Cyndie Koetting</Name>
    <Relation>Director</Relation>
    <Validated>:)</Validated>
    <guid>fc574089-4655-466e-8b52-485684b0e168</guid>
  </User>
  <User>

Becomes:

While the conversion was taking place the director needed to send a newsletter out; we weren't ready yet so we had to generate a list of email addresses for her to paste into outlook.  LinqPad comes to the rescue:

With a couple lines of code (literally) I was able to generate a lit of all email addresses (comma delimited) and copy them into the clipboard (highlighted in blue above); we emailed the director the list.


Tags: ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQ to XML - Loading an XML file

Edited 2009.11.19 -- Andrew provides valuable information on the topic discussed in this blog.

From: Andrew Baines 
Sent: Thursday, November 19, 2009 12:02 PM
To: bill@global-webnet.com
Subject: Suggestion:LINQ to XML - Loading an XML file
 Hi Bill, in regards to the following blog: http://www.global-webnet.net/blogengine/post/2008/09/12/LINQ-to-XML-Loading-an-XML-file.aspx
 
The issue here is something that I had problems with … the fact that the Newsletter node has the xmlns attribute means that every attribute has a namespace appended to the beginning. Therefore you can’t just ask for the Email element … you have to ask for the namespace + Email element.
 
This can be done like so:
 
string filename = @"d:\projects\newsletterxml\newsletter.xml";
XDocument UsersXML = XDocument.Load(filename);
XNamespace xsi =
"http://tempuri.org/Newsletter.xsd";
var Users = from user in UsersXML.Descendants(xsi + "User")
        
select new
        {
            Validate = user.Element(xsi +
"Validated").Value,
            Name = user.Element(xsi +
"Name").Value,
            Relation = user.Element(xsi +
"Relation").Value,
            Email = user.Element(xsi +
"Email").Value,
            Guid = user.Element(xsi +
"guid").Value
        };



Back before .NET 2.0 I had written Amarillo College Lamplight Youth Theatre a Newsletter program.   A couple of years ago we moved them over to our Community Advanced Starter Kit (CASK) application and simply provided a link to their .NET 1.1 Newsletter.  The only thing preventing us from moving them into the CASKDotNet newsletter is their large contact list and time (we donate our services and maintaining their site can keep us pretty busy at times). 

We're getting ready to release our next version of the CASK (upgrading them) and it seems an appropriate time to get them migrated over.   I have to load their XML Newsletter file and update their SQL Database as applicable to import their contact list.

Since LINQ has dramatically reduced coding with tables and stored procedures, I figured I'd give it a whirl for this XML requirement. Note: I had been avoiding LINQ as long as I could.  When .NET 3.5/Visual Studio SP1 was released I started using the Entity Framework and it was time to roll up my sleeves - wish I had started earlier...

So I broke out the LINQPAD, googled the topic and found an excellent article from Scott Guthrie that cover's the topic nicely HERE so the question was "Why wasn't it working!".  I throught perhaps LINQPAD was broken so I went into Visual Studio 2008 and it yielded the same results - nothing.

So what is wrong with the following picture?

string filename = @"d:\projects\newsletterxml\newsletter.xml";
XDocument UsersXML = XDocument.Load(filename);
var Users = from user in UsersXML.Descendants("User")
        select new
        {
            Validate = user.Element(
"Validated").Value,
            Name = user.Element(
"Name").Value,
            Relation = user.Element(
"Relation").Value,
            Email = user.Element(
"Email").Value,
            Guid = user.Element(
"guid").Value
        };

It was the .XSD statement!  I didn't need it for the migration tool so I didn't pull it into the project.  To get my query working I simply had to remove the xmlns .XSD attribute from the Newsletter element and walla! I had my list.   I guess now you can see why I wished I had started using LINQ earlier!


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

CompositeWPF / LINQPAD - Dissecting the GetModuleInfos(string path) method

LinkPad is a free utility that is available at http://www.LinqPad.net

I use the DirectlyLookupModuleEnumerator class for my SDMS application (blogged HERE and HERE) so when I stumbled across the following my first reaction was Foot in mouth followed by Yell --- the pit-bull side of me said "you have to understand what this is doing".

Enter stage left - LINQPAD!!!  I emulated the above LINQ Query, step-by-step, so that I could see what the query was doing.  You'll see I pretty much evolve the same lines (in below image) to see what is returned.  I was particularly interested in what line 185 was doing above.

For your convenience (COPY / PASTE)

/*000*/     string searchFolder = @"C:\Windows\System", fileFilter="*.DLL";
/*001*/   Directory.GetFiles(searchFolder, fileFilter).Dump("All files in folder");
/*002*/
/*003*/  string[] filter = {"avi","ole"}; // files we want to filter (include)

/*005*/  // Get all files in the debug folder
/*006*/  Directory.GetFiles(searchFolder, fileFilter)
/*007*/   // Each file in our filter will be processed against fileName
/*008*/   .Where(filterName => filter  // Note: Where clause expects bool result
/*009*/    .FirstOrDefault(fileName => filterName.ToLower().Contains(fileName))!=null).Dump("Files to be processed");

/*006a*/  Directory.GetFiles(searchFolder, fileFilter)
/*007a*/   // Each file in our filter will be processed against fileName
/*008a*/  .Where(filterName => filter  // Note: Where clause expects bool result
/*009a*/   .FirstOrDefault(fileName => filterName.ToLower().Contains(fileName))!=null)
/*010a*/         .SelectMany( file => file.Split('\\')
    ).Dump("SelectMany results without .Where() clause");

/*006b*/  Directory.GetFiles(searchFolder, fileFilter)
/*007b*/   // Each file in our filter will be processed against fileName
/*008b*/  .Where(filterName => filter  // Note: Where clause expects bool result
/*009b*/   .FirstOrDefault(fileName => filterName.ToLower().Contains(fileName))!=null)
/*010b*/         .SelectMany( file => file.Split('\\').Where(segment=> segment.Contains(".DLL"))
    ).Dump("SelectMany results with .Where() clause to only pull .DLL entries");

/*999*/
/*999*/  (filter.FirstOrDefault(listname => listname.Contains("ole"))      )
/*999*/   .Dump("Line 009 returns string if found without {!= null} which will crash .Where() clause");
/*999*/  (filter.FirstOrDefault(listname => listname.Contains("ole"))!=null)
/*999*/    .Dump("Line 009 returns bool with {!= null} which is what .Where() clause is expecting");
/*999*/

 


Tags: , , ,
Categories: CompositeWPF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQPAD - Using Stored Procedures / Accessing a DataSet

LINQPAD has quickly become my best friend - I think it is inevitable that everyone, like myself, will need to work with stored procedures.  In my case I have Linked Servers that I want to access from a single database and have configured a stored procedure to return me the data I need from the applicable Server(s) / Table(s).

The question when I left work today was - how do I make LINQPAD work with Stored Procedures?  All my efforts failed as shown below:

Prior to the above error I was getting a DataSet message (I hoped DataTable might have more success).  The only thing I could get to work was the following, very basic, command:

When I got home my research resumed and I stumbled upon the following link:

http://msdn.microsoft.com/en-us/library/system.data.datatableextensions.asenumerable.aspx

It had exactly what I was looking for:

Language-Integrated Query (LINQ) queries work on data sources that implement the IEnumerable<(Of <(T>)>) interface or the IQueryable interface. The DataTable class does not implement either interface, so you must call the AsEnumerable method to use the DataTable as a source in the From clause of a LINQ query. You can also obtain custom, domain-specific operators, such as CopyToDataTable, by returning an IEnumerable<(Of <(T>)>) object.

However I quickly ran into the following response when I added AsEnumerable() to the query:

 

A little more research led me to this link which suggested (once again) that this should be working...

http://msdn.microsoft.com/en-us/library/bb386910.aspx which has the following content:

 


It was when I drilled down into the AsEnumerable() method that I realized that it was an Extension!  The LINQPAD message, "press F4 to add a using directive..." had me very hopeful.  I hit F4 and added the System.Data.DataSetExtensions.dll

 

Walla! I'm using a Stored Procedure within LINQPAD!

I strongly recommend that you buy the book - I got mine in yesterday and the quality and usefulness of the content matches the LINQPAD application.


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

SDMS / VisioTool - Controller completed w/unit test - MAF

If like me, you were dismayed to learn that Visio for Architects was not supported in Visual Studio 2008  you'll appreciate this upcoming tool; particularly if you want to reverse engineer your code so that you can create your UML diagrams without having to manually input all the class information.  

Fortunately there is a workaround (I posted it HERE) which this utility performs.  Without it there was quite a bit of grunt work involved - enough for me to push architecture to the back-burner.   With a new requirement on the front burner I wasn't afforded this opportunity (to back-burner architecture).  There are to many components for me to wrap my mind around the solution, so I'll have to architect the existing WPF Calculator to prepare a gap analysis so that I can successfully create a Managed Add-in Framework (MAF) module loader for my CompositeWPF SDMS solution. 

For those that can't wait for the UI to be completed (like me) I completed the TDD of the controller - the process is fully functional.   The two test that actually perform the work are remarked out (not shown in the screenshot below) because I don't want to inadvertantly convert the solution; I have my Visio document and have to back burner this utility for now.

Note below that the logger trapped some warnings.  I created test for invalid files as well as non-existent files and the test passed as I attempted to crash the process. 

After manually running the "Controller_CanPrepareVS2008SolutionForArchitecture" test I was able to open up my solution in Visual Studio 2005 - note that there are two projects that did not open - at a glance it looks like the WPF related projects (I have some more research to do).  Since CALDemoApplication is on the "New" side of the gap analysis and the DemoApplication is mostly UI stuff I can worry about them later - what I needed was the main solution projects (which I have below).  Updated: unfortunately UI has a great deal of logic in it (more below).

With my Visio document in hand I manually ran the "Controller_CanRestoreFromArchitecture" unit test and was able to successfully reopen my solution in Visual Studio 2008, compile and run the application with no issues.

IMPORTANT NOTE:  when I was closing the Visual Studio 2005 solution (after creating the visio document) I responded "No" to save solution updates.

Note: I manually added the newly created DemoApplication.vsd document to my solution
 

Source code for above available at HERE - Change Set 23906.    App | VisioTool | VisioToolModule and VisioToolModule.Tests

This is somewhat off topic but very cool so I'll mention it - below is the LINQ code I use to get my project list out of a Visual Studio 2005 / 2008 solution:

I created the query using LINQPAD - more about this HERE

UPDATED:  I investigated the two projects that won't open.  It appears the .NET 3.5 WPF Projects have a totally different header - it follows:

<Project ToolsVersion="3.5"
             DefaultTargets="Build"
             xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

The return on investment for supporting this new format (any efforts to convert it) are not worth it; particularly since I simply have to continue good programming practices and patterns for it not to be a problem; XAML projects will contain only UI code and I'll have all the supporting logic in class libraries (an example for my SDMS application is in the following image).

This does however cause a problem for me with the WPF Calculator program that I am architecting for my gap analysis.  Most, if not all, of the wiring-up logic is sitting in the DemoApplication's CalculatorHost.xaml code behind file - not good...   This is not good for reverse engineering nor does it promise to offer any reusable components (chomping at the bit for MEF to be released).  So now I'm in refactoring mode - pulling all of the code out of code-behind and placing it in an external library.  It's a painfully slow process because I have to refactor, test everything (there are no unit test), check-in and move another component and start the cycle over again....  

The ROI for all this work is that the WPF Calculator is my POC (Proof of Concept); when I'm done refactoring to get the code out of the UI I can complete the Architecture work.  My goal is to keep refactoring the WPF Calculator until it is a CompositeWPF application with reusable components that I can use in my SDMS application.   The WPF Calculator UI will have less code than my SDMS UI does below so I have no issues with manually adding these objects to Visio: 

 


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

LINQPAD - Must have for your toolkit!

Last week while I was creating the StatusBar XAML for the SDMS application I read in the WPF Unleased book that you could use the grid for specifying width percentages.   I typed in the code verbatim and for the life of me could not get my SDMS application to display my left status bar textblock 1/3 the size of the right status bar textblock; both were defaulting to auto (the size of the text).  I tried labels, textblocks, etc to no avail.  Since I was wasting a lot of time changing, compiling and running, I jumped into XAMLPad ( .NET 2.0 SDK - command prompt) and got the same behavior (as expected) - when I remarked out the StatusBar elements the Grid worked! The issue is with the statusbar and the great thing was XAMLPad showed immediate results.   But this blog isn't about XAMLPad - when I get the StatusBar figured out I plan on blogging it thoroughly then...

As I started TDD on my LINQ Query to pull all the project names out of solution file (why explained below) I found myself coding, break pointing, running, examining and doing the same over again (once again losing much time in the process).   The thought came to me "google LINQPAD"; to my surprise a program came up and it instantly became my best friend.  If the author's intent was to sell his book he succeeded because within 15 minutes of using the application I was clicking the link and purchasing it :) 

REQUIREMENT 

Architect the CLRAddin WPF Calculator so that I can perform a gap analysis to determine what will be required to update the CompositeWPF application to support a CLRAddin module loader (modules loaded in their own AppDomain).   There are to many components to wrap my brain around it so the Architecture phase is neccessary.  The problem is I can't reverse engineer the calculator application with Visio for Architects because I'm using Visual Studio 2008 (it isn't supported).  

VISION STATEMENT

Complete a Visio Tool that will convert the solution and projects to Visual Studio 2005 so that it can be reverse engineered (generate all the Visio objects required for architecture).  Architect the existing WPF Calculator program and perform a gap analysis to create a MAFModuleLoader application for the CompositeWPF.

SOLUTION

Note before I continue: after downloading the LINQPAD if you'll go to C# 3.0 in a Nutshell (bottom left pane) and select "Before You Start" and click Readme.First() it will create the data objects (top left pane in image below) that you can use to run the samples and demos against.

In my case I use the following LINQ query to pull the project names and paths from the solution file:

string filePath = @"D:\MAFCompositeWPF\WPF_Caclulator\WPF Caclulator\DemoApplication.sln";

var query =
    from line in File.ReadAllLines(filePath)
    .Where(data => data.Contains("Project("))
    let record = line.Split(',')
    select new
        {
            Name = record[0].Split('=')[1].Replace("\"",""),
            Path = record[1].Replace("\"",""),
            Guid = record[2]
        };
query.Dump();  // This is a LINQPad command


The above query works against the following data:

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoApplication", "DemoApplication\DemoApplication.csproj", "{73C60A32-09A2-45C2-8C8C-8FD2817BB087}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HostView", "HostView\HostView.csproj", "{D589DDD6-6C12-4924-8831-3F312BBEA53E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Contracts", "Calculator.Contracts\Calculator.Contracts.csproj", "{D3E90866-D7BA-4B2A-A192-99649C2734BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddInView", "AddInView\AddInView.csproj", "{A4AA9D5C-28EA-4933-B750-E434B847B66F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddInSideAdapters", "AddInSideAdapters\AddInSideAdapters.csproj", "{C7311113-761B-43CF-A327-89C884698A1D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HostSideAdapters", "HostSideAdapters\HostSideAdapters.csproj", "{16B44B99-FB0D-4C61-9780-FF3E863F9D64}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicArithmaticAddIn.cs", "BasicArithmaticAddIn.cs\BasicArithmaticAddIn.cs.csproj", "{9CF465E4-003E-451D-ACBD-526E66CBB17A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicVisualAddIn", "BasicVisualAddIn\BasicVisualAddIn.csproj", "{71E0E79C-FFF8-441D-82D2-D50FDE809C14}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphCalc", "Graphing Calculator\GraphCalc.csproj", "{1241B377-8BA7-4AE1-B6CD-66766348B521}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicStackOperations", "BasicStackOperations\BasicStackOperations.csproj", "{E8144FE1-51E4-4DD9-9620-5B3EFF5625A7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CAL", "CAL", "{28F48224-E0D4-4E43-93C6-E98D3789732E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Composite", "..\CAL\Composite\Composite.csproj", "{77138947-1D13-4E22-AEE0-5D0DD046CA34}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Composite.UnityExtensions", "..\CAL\Composite.UnityExtensions\Composite.UnityExtensions.csproj", "{17831F3B-6B82-4916-BD2B-2CE2071EA622}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Composite.Wpf", "..\CAL\Composite.Wpf\Composite.Wpf.csproj", "{F807062D-6FC9-4FF0-A9F5-5F94653EDC4D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedDLL", "SharedDLL\SharedDLL.csproj", "{7968177C-7610-4941-AB18-71BF5E2EA69D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CALDemoApplication", "CALDemoApplication\CALDemoApplication.csproj", "{6DE42D1B-BF16-48F1-8EEF-22AFE1446254}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CALDemoApplication.Infrastructure", "CALDemoApplication.Infrastructure\CALDemoApplication.Infrastructure.csproj", "{EB012DE4-816A-44A6-957C-1FED71EE1C31}"
EndProject
Global
 GlobalSection(TeamFoundationVersionControl) = preSolution
  SccNumberOfProjects = 11
  SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
  SccTeamFoundationServer = https://tfs05.codeplex.com/
  SccLocalPath0 = .
  SccProjectUniqueName1 = AddInSideAdapters\\AddInSideAdapters.csproj
  SccProjectName1 = AddInSideAdapters
  SccLocalPath1 = AddInSideAdapters
  SccProjectUniqueName2 = AddInView\\AddInView.csproj
  SccProjectName2 = AddInView
  SccLocalPath2 = AddInView
  SccProjectUniqueName3 = BasicArithmaticAddIn.cs\\BasicArithmaticAddIn.cs.csproj
  SccProjectName3 = BasicArithmaticAddIn.cs
  SccLocalPath3 = BasicArithmaticAddIn.cs
  SccProjectUniqueName4 = BasicStackOperations\\BasicStackOperations.csproj
  SccProjectName4 = BasicStackOperations
  SccLocalPath4 = BasicStackOperations
  SccProjectUniqueName5 = BasicVisualAddIn\\BasicVisualAddIn.csproj
  SccProjectName5 = BasicVisualAddIn
  SccLocalPath5 = BasicVisualAddIn
  SccProjectUniqueName6 = Calculator.Contracts\\Calculator.Contracts.csproj
  SccProjectName6 = Calculator.Contracts
  SccLocalPath6 = Calculator.Contracts
  SccProjectUniqueName7 = DemoApplication\\DemoApplication.csproj
  SccProjectName7 = DemoApplication
  SccLocalPath7 = DemoApplication
  SccProjectUniqueName8 = Graphing\u0020Calculator\\GraphCalc.csproj
  SccProjectName8 = Graphing\u0020Calculator
  SccLocalPath8 = Graphing\u0020Calculator
  SccProjectUniqueName9 = HostSideAdapters\\HostSideAdapters.csproj
  SccProjectName9 = HostSideAdapters
  SccLocalPath9 = HostSideAdapters
  SccProjectUniqueName10 = HostView\\HostView.csproj
  SccProjectName10 = HostView
  SccLocalPath10 = HostView
 EndGlobalSection
 GlobalSection(SolutionConfigurationPlatforms) = preSolution
  Debug|Any CPU = Debug|Any CPU
  Release|Any CPU = Release|Any CPU
 EndGlobalSection
 GlobalSection(ProjectConfigurationPlatforms) = postSolution
  {73C60A32-09A2-45C2-8C8C-8FD2817BB087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {73C60A32-09A2-45C2-8C8C-8FD2817BB087}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {73C60A32-09A2-45C2-8C8C-8FD2817BB087}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {73C60A32-09A2-45C2-8C8C-8FD2817BB087}.Release|Any CPU.Build.0 = Release|Any CPU
  {D589DDD6-6C12-4924-8831-3F312BBEA53E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {D589DDD6-6C12-4924-8831-3F312BBEA53E}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {D589DDD6-6C12-4924-8831-3F312BBEA53E}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {D589DDD6-6C12-4924-8831-3F312BBEA53E}.Release|Any CPU.Build.0 = Release|Any CPU
  {D3E90866-D7BA-4B2A-A192-99649C2734BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {D3E90866-D7BA-4B2A-A192-99649C2734BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {D3E90866-D7BA-4B2A-A192-99649C2734BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {D3E90866-D7BA-4B2A-A192-99649C2734BD}.Release|Any CPU.Build.0 = Release|Any CPU
  {A4AA9D5C-28EA-4933-B750-E434B847B66F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {A4AA9D5C-28EA-4933-B750-E434B847B66F}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {A4AA9D5C-28EA-4933-B750-E434B847B66F}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {A4AA9D5C-28EA-4933-B750-E434B847B66F}.Release|Any CPU.Build.0 = Release|Any CPU
  {C7311113-761B-43CF-A327-89C884698A1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {C7311113-761B-43CF-A327-89C884698A1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {C7311113-761B-43CF-A327-89C884698A1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {C7311113-761B-43CF-A327-89C884698A1D}.Release|Any CPU.Build.0 = Release|Any CPU
  {16B44B99-FB0D-4C61-9780-FF3E863F9D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {16B44B99-FB0D-4C61-9780-FF3E863F9D64}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {16B44B99-FB0D-4C61-9780-FF3E863F9D64}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {16B44B99-FB0D-4C61-9780-FF3E863F9D64}.Release|Any CPU.Build.0 = Release|Any CPU
  {9CF465E4-003E-451D-ACBD-526E66CBB17A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {9CF465E4-003E-451D-ACBD-526E66CBB17A}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {9CF465E4-003E-451D-ACBD-526E66CBB17A}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {9CF465E4-003E-451D-ACBD-526E66CBB17A}.Release|Any CPU.Build.0 = Release|Any CPU
  {71E0E79C-FFF8-441D-82D2-D50FDE809C14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {71E0E79C-FFF8-441D-82D2-D50FDE809C14}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {71E0E79C-FFF8-441D-82D2-D50FDE809C14}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {71E0E79C-FFF8-441D-82D2-D50FDE809C14}.Release|Any CPU.Build.0 = Release|Any CPU
  {1241B377-8BA7-4AE1-B6CD-66766348B521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {1241B377-8BA7-4AE1-B6CD-66766348B521}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {1241B377-8BA7-4AE1-B6CD-66766348B521}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {1241B377-8BA7-4AE1-B6CD-66766348B521}.Release|Any CPU.Build.0 = Release|Any CPU
  {E8144FE1-51E4-4DD9-9620-5B3EFF5625A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {E8144FE1-51E4-4DD9-9620-5B3EFF5625A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {E8144FE1-51E4-4DD9-9620-5B3EFF5625A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {E8144FE1-51E4-4DD9-9620-5B3EFF5625A7}.Release|Any CPU.Build.0 = Release|Any CPU
  {77138947-1D13-4E22-AEE0-5D0DD046CA34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {77138947-1D13-4E22-AEE0-5D0DD046CA34}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {77138947-1D13-4E22-AEE0-5D0DD046CA34}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {77138947-1D13-4E22-AEE0-5D0DD046CA34}.Release|Any CPU.Build.0 = Release|Any CPU
  {17831F3B-6B82-4916-BD2B-2CE2071EA622}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {17831F3B-6B82-4916-BD2B-2CE2071EA622}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {17831F3B-6B82-4916-BD2B-2CE2071EA622}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {17831F3B-6B82-4916-BD2B-2CE2071EA622}.Release|Any CPU.Build.0 = Release|Any CPU
  {F807062D-6FC9-4FF0-A9F5-5F94653EDC4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {F807062D-6FC9-4FF0-A9F5-5F94653EDC4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {F807062D-6FC9-4FF0-A9F5-5F94653EDC4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {F807062D-6FC9-4FF0-A9F5-5F94653EDC4D}.Release|Any CPU.Build.0 = Release|Any CPU
  {7968177C-7610-4941-AB18-71BF5E2EA69D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {7968177C-7610-4941-AB18-71BF5E2EA69D}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {7968177C-7610-4941-AB18-71BF5E2EA69D}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {7968177C-7610-4941-AB18-71BF5E2EA69D}.Release|Any CPU.Build.0 = Release|Any CPU
  {6DE42D1B-BF16-48F1-8EEF-22AFE1446254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {6DE42D1B-BF16-48F1-8EEF-22AFE1446254}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {6DE42D1B-BF16-48F1-8EEF-22AFE1446254}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {6DE42D1B-BF16-48F1-8EEF-22AFE1446254}.Release|Any CPU.Build.0 = Release|Any CPU
  {EB012DE4-816A-44A6-957C-1FED71EE1C31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  {EB012DE4-816A-44A6-957C-1FED71EE1C31}.Debug|Any CPU.Build.0 = Debug|Any CPU
  {EB012DE4-816A-44A6-957C-1FED71EE1C31}.Release|Any CPU.ActiveCfg = Release|Any CPU
  {EB012DE4-816A-44A6-957C-1FED71EE1C31}.Release|Any CPU.Build.0 = Release|Any CPU
 EndGlobalSection
 GlobalSection(SolutionProperties) = preSolution
  HideSolutionNode = FALSE
 EndGlobalSection
 GlobalSection(NestedProjects) = preSolution
  {77138947-1D13-4E22-AEE0-5D0DD046CA34} = {28F48224-E0D4-4E43-93C6-E98D3789732E}
  {17831F3B-6B82-4916-BD2B-2CE2071EA622} = {28F48224-E0D4-4E43-93C6-E98D3789732E}
  {F807062D-6FC9-4FF0-A9F5-5F94653EDC4D} = {28F48224-E0D4-4E43-93C6-E98D3789732E}
  {7968177C-7610-4941-AB18-71BF5E2EA69D} = {28F48224-E0D4-4E43-93C6-E98D3789732E}
 EndGlobalSection
EndGlobal 


To produce the following results (bottom pane holds the Dump() results):

I was able to tweak and tune my LINQ query (instantly seeing the results) until I had the output I was looking for.


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know