Sloan's profileGranada Coder, C#, Sql S...PhotosBlogLists Tools Help

Blog


    5/6/2009

    One True Lookup Table Discussion

     
    I recently saw an article on the "One True Lookup Table".
     
     
    Code Sample Here (Right-click, Save As works best)
     
    The author makes 2 points.
    //
    In most cases, neither of the above applies, and responsibility for data integrity resides solely in the application code.
    Show me such a database, and I’ll show you some data where the code does not match anything in the lookup table.
    //
     
    Well, I can provide a solution that uses a UDF (user defined function) and a check constraint.
    And the statement "and I’ll show you some data where the code does not match anything in the lookup table" would not be valid if this solution is employed.
    The FK exists to keep integrity.<<(This exists in addtion to the constraint)
    And the check constraint makes sure that you only can apply values of the correct "CodeCategory".
    (Aka, I cannot put in a OrderStatus value into a column wanting a Country or similar).
     
    The solution is Sql Server specific, but the idea is there.
     
    Keep in mind I use this for ~most tables.  I still create a few standalone lookup tables when the cookie cutter does not work.
     
    I am not saying this is the best solution in every scenario.  There is a performance issue with validating the check constraint for every INSERT/UPDATE action.
    But for bulk data inserts (or updates), you can drop the constraint and readd it if that becomes an issue.
     
    1/27/2009

    Bulk Insert Example, using an IDataReader To Strong DataSet to Sql Server XML

    Here is a sample I wrote that someone requested I post.
     
    Right click HERE and do a "Save As" or "Save Target As".
     
     
    The sample gets an IDataReader on a Source Datastore (Access/Jet database), populates a strong dataset, then after so many records, ships the DataSet.GetXml() to a sql server stored procedure.
    This should run faster than doing a row by row get/insert that alot of people code up.
     
    Yes, there are many ways to do bulk inserts/imports with Sql Server.  I chose this one because in my real life code, I need to run the rows through some business rules before importing. 
     
    Enjoy.
     
    12/10/2008

    SqlServer2005/2008 // OUTPUT clause in INSERT/UPDATE/DELETE statements

    SqlServer2005/2008 // OUTPUT clause in INSERT/UPDATE/DELETE statements

    These types of samples are all over the place on the web, but here is my original example for which I believe is better clarity.

    Original Example(s) at:
    http://blogs.msdn.com/sqltips/archive/2005/06/13/OUTPUT-clause.aspx

    create table PrimaryHolderTable ( i int identity (1001,2) not null primary key, j int not null unique )
    create table #OutputResultsHolder ( i int not null, j int not null)
     
    insert into PrimaryHolderTable (j)
    output inserted.i, inserted.j into #OutputResultsHolder
    select top 10 o.object_id from sys.objects as o order by o.object_id desc --<< from sys.objects is there just to provide some rows
     

    select * from #OutputResultsHolder
    drop table #OutputResultsHolder, PrimaryHolderTable;

    go
     
     

    create table dbo.EmployeeTable ( EmpKey int identity(1001,2) ,  EmpAge int not null );
    create table dbo.AuditTable ( EntityKey int not null default -1  ,  OldValue int null, NewValue int null , Tag varchar(64)  );
     
    insert into dbo.EmployeeTable (EmpAge)
    output inserted.EmpKey , null , inserted.EmpAge , 'Employee Inserted' into dbo.AuditTable ( EntityKey , OldValue , NewValue , Tag)
     values( 18 );

    insert into dbo.EmployeeTable (EmpAge)
    output inserted.EmpKey , null , inserted.EmpAge , 'Employee Inserted' into dbo.AuditTable ( EntityKey , OldValue , NewValue , Tag)
     values( 20 );

    insert into dbo.EmployeeTable (EmpAge)
    output inserted.EmpKey , null , inserted.EmpAge , 'Employee Inserted' into dbo.AuditTable ( EntityKey , OldValue , NewValue , Tag)
     values( 22 );
     
     
    update dbo.EmployeeTable
       set EmpAge  = EmpAge + 1
    output inserted.EmpKey , deleted.EmpAge, inserted.EmpAge , 'Employee Updated' into dbo.AuditTable ( EntityKey , OldValue , NewValue , Tag)
     where EmpAge <=20;
     
    delete from dbo.EmployeeTable
    output deleted.EmpKey , deleted.EmpAge, NULL , 'Employee Deleted'  into dbo.AuditTable (EntityKey , OldValue , NewValue , Tag)
     where EmpAge > 0;--Test multi rows
     
    select * from dbo.EmployeeTable;--<<will be empty at this point
    select * from dbo.AuditTable;
     
    drop table dbo.EmployeeTable, dbo.AuditTable;
    go
     

    7/24/2008

    Cursors, SetBased, and Scalar UDF

    This is an oldie but a goodie.

    If you're doing CURSOR development for Sql Server to solve business problems, then STOP.

    DOWNLOAD this file (right-click and save-as works best) for an example of non cursor based logic solving.

    03 and 04 solve the same business problem, but 2 different ways. 

    03 is the cursor model.  04 is the udf model.

    Read the comments, and try to make a decent sized database (row count) so you see the massive and significant time difference.

     

    6/13/2008

    Software Quote

    Usually I don't post quotes, but this one has gotten enough positive feedback, I guess I'll post it.
     
    The conversation context:  A VB6 developer moving to C# and posed the question "Where is the Resume Next in C#?".
     
    Here was my reply:
     
    My VB.NET suggestions:

    Exception Handling?  (formally know as ErrorHandling)

    Take the vb6 mentality of exception handling and do the 4 following things:

    1.  Put it in a brown paper bag.
    2.  Take it to someone's house you don't like.
    3.  Set it on fire.
    4.  Run away from it as fast as you can.

    Check here:
    http://blogs.msdn.com/kcwalina/archive/2005/03/16/396787.aspx
    (A great post by Krzysztof Cwalina on exceptional handling)

    I was able to attend Krzysztof's presentation on Reusable Frameworks at TechEd2008, and he mentioned a future version (V2) of his book Framework Design Guidelines.  Sweet!
    3/4/2008

    Multiple RDBMS Support and the Factory Design Pattern

     
    August, 2008 Update.
    I added in SqlServerCE support (version 3.1 for desktop).
    Now there is SqlServer, Access, Excel, Text and SqlServerCE.
    SqlServerCE was a little tricky at first, but I got it running.
     
     
    This is an early preview, and I don't have alot of comments or explanation yet.
      
    Download the code HERE (Right Click / Save As works best)
    One of my first real jobs my company had to support multiple RDBMS (Relational Database Management Systems).
    Sql Server 6.5, 7.0, Sybase, and Access/Jet (Although I don't consider Access/Jet a real RDBMS).
     
    Oh, the countless number of "IF gDataBaseType="Access" then" statements.  YUCK!
     
    I had been advising people to use the Factory Pattern for multiple RDBMS support, but finally decided to author a demo.
     
    You'll need Sql Server 2000,2005 or 2008.  You don't have to have those, but it helps.
     
     
    Most important instruction.  Read the README.txt file before unzipping.
    Again, read the README.txt file BEFORE unzipping.
     
    I'll followup with this at some point, but wanted to get the sneak preview out the door.
     
    ..
     
     

    11/29/2007

    MembershipProvider Helper To Transfer Data

    If you're using the MembershipProvider (actually the concrete SqlMembershipProvider), then you may have run into an issue where you develop all the Users and Roles on your local machine, using the asp_net.mdf in the Express version of Sql Server 2005.

    All of the sudden, you need to deploy your application and you need a different database to store your information.
    You'll probably find this resource for deploying on a different version of Sql Server
    http://weblogs.asp.net/scottgu/archive/2005/08/25/423703.aspx

    Ok.  You run the aspnet_regsql.exe utility, and you have the correct database, but it is empty.

    What do you do now?

    I've created this helper script to enable you to move all your applications/users/roles to a new database.
    This script doesn't actually INSERT the new data, but creates the TSQL code that you can run on the new database.

    I've posted the code here (as a text file).

    Hopefully, it will make your life a little easier when you need to copy to a new database all the Membership information.

    10/18/2007

    WCF with Interface Development

    I'll provide more info later, but wanted to get this entry up.
     
    I've recently did a sample application for my local user group.
     
    It's a layered and tiered setup.  The "traditional" layers are there, but now you can tier this application thanks to WCF.
     
    Also included is an alternate way to avoid creating proxy classes on the client.  I call this the "DotNet to DotNet" method.
    Ultimately, this life saving blog entry made this possible:
     
    The sample is complete.  You'll have to create a simple Sql Server Database, and run a few .tsql files (3) to have a working example.
     
    A IsOneWay method is also included (aka, using WCF with MSMQ).  And an error handling (IErrorHandler) example is provided as well, when a "IsOneWay" message fails.
    (Naturally, you'll need MSMQ 3.0 or 4.0 installed for this example to work).
     
    Please look at the root directory README.txt files, it answers most questions about the setup, and where I downloaded Juval's excellent ServiceEx project.
     
    If it helps, please leave a comment.  Thanks.
     
    I am currently leading the discussion about StockTrader (msdn.microsoft.com/stocktrader) for my local user group (www.trinug.org).
    I am also attempting to integrate my sample application into Greg Leake's (stock trader author) Configuration Framework.
    If you haven't seen the StockTrader videos on Channel9/Wiki, check them out.

    Now load the example which you can download here. (A "right-click" and "save-as" works best).  

    PS
    If you need a good reference for getting started (aka, the nuts and bolts of getting WCF working on your development machine),
    try this:
    http://www.thoughtshapes.com/WCF/GettingStarted.htm


    DOWNLOAD NOTE:

    If you have not installed WF (WorkFlow) onto your system, you will need to make the following changes:

    Open up (in notepad.exe) the following file:
    \BusinessLogic\GranadaCoder.Applications.TrinugWCFDemoVersion1.BusinessLogic.csproj

    Find the lines below and REMOVE them.

    (Line 12)
        <ProjectTypeGuids>{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

    (Line 14)
      <Import Project="$(MSBuildExtensionsPath)\Microsoft\Windows Workflow Foundation\v3.0\Workflow.Targets" />


    Save the file.  Close notepad.exe.  Find and open the .sln file.  That should work.
    I realized after the fact that I had a WF reference in there, and not everyone has WF installed.


    5/15/2007

    Xml To Xml Conversion

    We all know you can do an xml to html conversion. Did you know you can do an xml to xml conversion also?
    Why?
    If you ever get data as xml, but you cannot control the formatting, you can alter it into another xml document.
    Why?
    One way to use this is to think about a DataSet object. (You're using strongly typed datasets and not loosely typed datasets, right?)
    If you remember that a DataSet is just some fancied up Xml, you can convert your original xml into one that matches your dataset schema.

    Create a new strongly typed dataset. Call it MyStronglyTypedDS.
    Add an "Author" table.
    Add elements : MyUniqueID, LastName, FirstName, TheBookCount. ( string, string, string, int )
    Save your file.

    Now load the example which you can download here.

    There are 3 files. myxml.xml, myxsl.xsl, runit.vbs. (The vbs is to keep it simple, but you can easily find the DotNet code to do this).

    Run runit.vbs. It will create a new file called results.xml.

    Voila! An xml to xml conversion, which outputs to the matching schema of the (strong) dataset you created above.

    MyStronglyTypedDS ds = new MyStronglyTypedDS();
    MyStronglyTypedDS.ReadXml(@"c:\mypath\results.xml");

    Or check this MS KB article:
    http://support.microsoft.com/kb/311566

    The original need I had for this method was that I was getting the same data from customers, however, each customer sent the data just a little differently. They were actually sending excel files. I saved off the excel (as xml) programatically. Then I had one xsl file per customer, that translated the data into a common xml schema. And Boom! When a new customer had a new and different format, all I had to do was write 1 xsl file, and I could use their data, without altering any of my original code (It's kind of an OO concept of Open-Closed-Principal (OCP), but with xsl)  (<< How's that for a stretch?). Writing an xsl file for excel/xml is a little trickier, but can be done. See a previous post from me and you can see some of that syntax and namespace issues.
    6/19/2006

    Advanced IComparer // Sorting on Multiple Values

    I've been writing IComparer's for a while, and could never figure out a "clean" way to write one which handled mutliple values to compare against.
    One time I tried stringing together the multi values, but that was deficient fairly quickly, when you went to datatypes outside of strings.
    ( If you sort the numbers 1-15 with a string, you get : 1,10,11,12,13,14,15,2,3,4,5,6,7,8,9, which is not what you want).  Throw some DateTime's in the mix, and you see that can get ugly very quickly.
    Well, no longer.  The following sample shows how to have your cake and eat it too.
    The EmployeeComparer can handle strings, times, ints (or others).  It can handle 1:N number of sort parameters, where N is probably the overall number of properties you have on your object.
    And it only does the work needed, aka, it doesn't run 5 comparing operations when 1 will suffice.
    The little trick is to nest some comparisons, in case there is a "tie" ( compare value of 0 ).
    The other good news is that this solution is not limited to 1, 2 or 3 levels of comparing values.
    The limit is your imagination.
    However, the solution does not do unnecessary compares either.
    I think the solution is pretty elegant.
    The example can sort ASC or DESC.  However, it sorts ASC and DESC for the entire set of SortColumns.
    I'm working on my own solution for this.  (Just a wrapper object to hold the SortColumn and the direction).
    But I will leave that as an exercise for the reader.

    Here is the downloadable example.  (Right Click and "Save Target As" is your best bet).  This is 1.1 code, fyi.

    6/5/2006

    Custom Objects and Tiered Development II // 2.0

    I've taken the 1.1 example (Custom Objects and Tiered Development below), and translated it into a 2.0 example, to take advantage of generics.
     
    First, thanks to Ludwig Stuyck ( http://www.coders-lab.be ) for his well laid out Article on Generics.
    Most of the translation of my 1.1 example to 2.0 came from knowledge gained from his article.
    The link to the original location of the article is below.
     
     
    There is not that much different between the 1.1 example and 2.0 example, except the use of Generics.
    For example:
     

    [Serializable]

    public class Customer

    {

    private string m_customerId;

    private string m_contactName;

    private string m_city;

    private List<BusinessObjects.Order> m_orders = new List<BusinessObjects.Order>();

     

    public Customer()

    {

    }

    public Customer(string custId)

    {

    this.m_customerId = custId;

    }

    public Customer(string custId, string contactName, string city)

    {

    this.m_customerId = custId;

    this.m_contactName = contactName;

    this.m_city = city;

    }

    public string CustomerID

    {

    get { return this.m_customerId; }

    set { this.m_customerId = value; }

    }

    public string ContactName

    {

    get { return this.m_contactName; }

    set { this.m_contactName = value; }

    }

    public string City

    {

    get { return this.m_city; }

    set { this.m_city = value; }

    }

    public List<BusinessObjects.Order > AllOrders

    {

    get { return this.m_orders; }

    set { this.m_orders = value; }

    }

    public int CompareTo(Customer cust )

    {

    return this.CustomerID.CompareTo(cust.CustomerID );

    }

    // Special implementation to be called by Customer comparer

    public int CompareTo(Customer cust, Comparers.CustomerComparerType comparisonType)

    {

    switch (comparisonType)

    {

    case Comparers.CustomerComparerType.CustomerId :

    return this.m_customerId.CompareTo(cust.CustomerID );

    case Comparers.CustomerComparerType.ContactName :

    return this.m_contactName.CompareTo(cust.ContactName);

    case Comparers.CustomerComparerType.City :

    return this.m_city.CompareTo(cust.City );

    }

    return 0;

    }

    }

     
     
    OrderCollection (1.1) has been replaced with
    public List<BusinessObjects.Order >
     
    The code sample can be downloaded Here.
     (Right Click , Save As probably works best)
     
     
     
    References:
    Also see the references in the 1.1 example below.
    5/24/2006

    Custom Objects/Collections and Tiered Development

     
    Recently I was involved in a discussion on creating BusinessObjects, BusinessObject Collections, and where one should keep the code to create the objects.
     
    In a N-tiered system, you want to create seperate tiers.  While most people can quote the name of the tiers, understanding them and actually keeping them seperated is the fall short area.
     
    I have created a sample which has a:
    Presentation Tier ( a Console App )
    Business Tier.
    DataLayer Tier.
     
    I am piggy-backing off the NorthWind database for my example. 
    I have created 2 strong objects (Customer and Order), and 2 Collection objects (CustomerCollection and OrderCollection).
    I have also created a CustomerController object to handle the creation of Customer's and Order's.
    The CustomerController is a business object, which calls the DataLayer object, and gets an IDataReader to populate the data.
    (Sql Server 2000/7.0/2005 scripts are included in the download as well).
     
    I am also using the DAAB 2.0 "SQLHelper" class to assist with the datalayer object.  While I use the EnterpriseLibrary project now in my production code, the SQLHelper is good for this demo, as it is Sql Server (2000 usually) specific.  It also provides an easier mechanism for implementing the connection string property.
    However, it points out the fact that I should be able to move from one DataHelper (SQLHelper) to another (EnterpriseLibrary.Data) and *never* have to touch/recompile the
    Presentation Code or the
    BusinessLogic Code
    In fact, you could switch from Sql Server to Oracle, and never have to recompile these 2 upper tiers (again, Presentation and BusinessLogic should be isolated from the database vendor of choice)
     
     
    Hopefully the sample will provide an illustration to avoid common mistakes.
    Those mistakes are:
     
    1.  Intertwined business layer logic and database connectivity.
    2.  Substituting the SQLHelper class ~in place of~ the application's DataLayer object.  The SQLHelper (as the name implies) is there to assist/help the DataLayer object, not replace it.
    3.  Keeping objects (their properties, methods and sometimes events) seperate from the code which creates them.
     
     
    I show how to create a sub collection of Orders per Customer, and how to accomplish this with one database call.  (IDataReader.NextResult();)
     
    I show how the DataAccessLayer should be simple, and pretty much return only data.
    My rule of thumb is that:
    The DataLayer object should return:
    IDataReader's
    DataSet's (typed and untyped)
    Scalars ( count(*) is an example here)
    void/Nothing (aka, you just call a stored procedure which does something, but does not return anything)
     
    That's it.  That's what the DataLayer should return.
    The obvious reason is that any RDBMS or datastore should be able to return DataSet's and IDataReaders.  That's the goal, to provide data to the business layer without having intricate knowledge of the backend database.
     
    ..
     
    As a bonus, I include some time-test code to compare:
     
    Custom Objects/Collections being populated with an IDataReader
    Typed DataSet object, with Constraints enabled.
    Typed DataSet object, with no Constaints (in the xsd definition)
     
    You can view the results.
     
    In regards to Custom Objects/Collections vs Typed DataSet's vs other methods:
    Like in most areas of software development, there are not "blanket statements" for which method to use.
    There are pro's and con's, and it depends.
    I usually go with the CustomObjects/Collections.
    I jokingly refer to DataSet's as the "poor man's business object".  However, there are times when they make good sense.
     
     
    You can download the example code here. ("Right Click"/"Save Target As" is the best approach I think)
     
     
    I am working up a DotNet 2.0 example also, where I replace the CollectionBase objects with generics.  Look for that code soon.
     
     
    References:
     
     
     
    And a reference to read from start to finish, aka, very informative for a bird's eye view:
    2/8/2006

    Smarter Email/Smtp setup with DotNet Configuration Sections (1.1 and 2.0)

     
    How many times have you written something like this:
     <appSettings>
    <add key="smtpserver" value="smtp.myisp.com"/>
     </appSettings>
     
    And then, when you need to send email, you read the value from the config file, and write code to send email.
     
    Perhaps you deploy to a web hosting company, and realize they have a different smtp authentication routine.
    Then you go back to your code, and make adjustments, so you can send from your local setup, and then your hosting company as well.
     
    So you put together something for the 1.1 Framework.  Then 2.0 comes out, and you repeat using the new email object in 2.0.
     
    1.1 Framework uses the

    System.Web.Mail.MailMessage

    object.

    Then along comes 2.0, and thank goodness they clean up sending emails.

    2.0 uses the

    System.Net.Mail.MailMessage

    object.

     

    After looking at the 2.0 library, I realized it would be much cleaner to seperate the code which instantiates these 2 objects, and the smtp server settings.

    I basically have created a custom configuration section handler, to encapsulate most/all of the different settings one might need to send Smtp Emails.

    The best part of this custom handler, is that I can reuse it for both 1.1 and 2.0 email sending.

    I've also included a 1.1 sample project, and a 2.0 sample project.

    Let's take a quick look at the App.config (or Web.config) file.

     

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

    <configSections>

    <section name="EmailSettingsSectionName" type="GranadaCoder.Email.Settings.EmailSmtpSettingsHandler,GranadaCoder.Email.Settings" />

    </configSections>

    <EmailSettingsSectionName defaultEmailFrom="donotreply@donotreply.com" portNumber="25">

    <!--You need to add a reference to GranadaCoder.Email.Settings.EmailSmtpSettingsHandler.dll since this dll lives outside this (Presentation) assembly-->

    <!--Comments can go here-->

    <!--SSL example. Google(gmail)Mail is a good (free) example of this. Naturally, you need to provide a legitimate username and password-->

    <!--Note, with the 2.0 Framework, my tests show that gmail likes port 587-->

    <smtpServer enabled="true" smtpServerName="smtp.gmail.com" defaultEmailFrom="donotreply@gmail.com" portNumber="465" authenicationMode="SSL" smtpUserName="mygmailaddress@gmail.com" smtpUserPassword="mygmailpassword" executeOrder="3"/>

    <!--Basic authentication. Passing in a username (and sometimes a password) are used here.-->

    <smtpServer enabled="true" smtpServerName="smtp-server.nc.rr.com" defaultEmailFrom="donotreply@rr.com" portNumber="25" authenicationMode="basic" smtpUserName="myemail@rr.com" executeOrder="2"/>

    <!--None authentication. Nothing but the smtp-server name is provided-->

    <smtpServer enabled="false" smtpServerName="smtp.noauthenticationneeded.com" authenicationMode="none" executeOrder="1"/>

     

    </EmailSettingsSectionName>

    </configuration>

     

    I have all 3 scenarios here.

    (No) authentication.

    Basic authenticaton.

    SSL authentication.

     

    You'll also notice I have a Collection of SmtpServers.  Why?  One way I use this class is to have back-up email senders.  In case the first smtp server is down, I use the 2nd (or the 3rd or 4th) email to send the email.  If the first one is down, I also send an admin email saying "The primary smtp server was down".  This is why I included the 'executeOrder' attribute.  But that is outside the scope of this blog.

     

    The config section makes quick work of deploying my solution to a hosting or production server.

     

    By seperating the setup information from the 1.1 or 2.0 code to send emails, your 1.1 or 2.0 code becomes cleaner.

     

    I also like the Collection of SmtpServers idea.  Using <app key values> means you get one smtp server to use.

    Even with 2.0:

     

      <system.net>

        <mailSettings>
          <smtp from="
    your@email.com">
            <network host="smtp-server.rr.com" password="" userName="myaccount
    @rr.com" />
          </smtp>
        </mailSettings>
       
      </system.net>

     

    It looks like a 1 smtp server setup.

     

    You can download the code HERE. (Right-Click and "Save As" works best)

     

    The code is in 1.1, except for the code in the TestApp_2_0 directory.  There are 2 solution files, one in 1.1 (VS 2003) and one in 2.0 (VS 2005).

     

    For 1.1, you will want to open up the:

    \Settings\TestApp_1_1\GranadaCoder.Email.Settings.TestApp.sln

    solution. (In VS 2003 of course.)

     

    For 2.0, you will want to open up the:

    \Settings\TestApp_2_0\TestApp_2_0.sln

    solution file. (In VS 2005 of course.)

    (Note, the 2.0 solution does not refer to the GranadaCoder.Email.Settings.dll file "By Project", so you may need to re-add the reference (as in... browse/file...add reference) to get it to work.

     

    This solution offers these advantages:

    Encapsulation and seperation of the Smtp Server Settings from the code to send an Email through DotNet.

    (Optionally) a Collection of SmtpServer, to allow for a backup plan, if the primary smtp server is unavailable.

    Easy setup through a config file.

     

    The solution will not work out of the box.  You need to put in your smtp server settings.  You also need to change the To address in the Console.Application code.  And don't forget the <smtpServer enabled="false"> setting, which needs to be "true" if you want that smtp-server to be considered.

     

    Even if you don't need the smtp server, you can use the project to learn how to write a customized Configuration Handler.

    Also included are:

    IComparer, to handle different attributes/properties to sort by.

    CollectionBase object, and a Sort method using your custom IComparer


    12/16/2005

    How to Install MSDE (Sql Server 2000 Desktop)

    While not cutting edge OO programming, I've instructed people so many times how to do this, that I decided to add it to the blog.
     
    MSDE is a "free" and "lite" version of Sql Server 2000.  It has a few articial govenors in it, and a few features disabled.  However, for most developers, it is a great way to get the Sql Server 2000 engine.
    The great thing about it, is that any code (tsql) written for MSDE will work on Sql Server 2000.  It is much much better than going from Access to Sql Server.
     
     

    Go Here to get to the download. Or try www.microsoft.com/sql and search for "MSDE".

     

    You will download the file.  Once downloaded, you will run the exe and it will decompress the files.

    Let's assume you extract the files to c:\wutemp\msde\

     

    The trick with the install is that running the "setup.exe" file (in c:\wutemp\msde\ folder) won't really do anything.

    The program installs from the command line prompt.  Here are some short cuts.

     

    Inside the c:\wutemp\msde\ folder, create 2 new files.

    InstallMSDEWithMyParameters.bat

    MyParameters.INI

     

    The contents of InstallMSDEWithMyParameters.bat will be:

    setup /settings "MyParameters.ini" SAPWD="sapassword" /L*v C:/MSDELog.log

    The contents of MyParameters.ini will be:

    [Options]
    DISABLENETWORKPROTOCOLS=0
    SECURITYMODE=SQL

     

    You can read more about the options in the ReadmeMSDE2000A.htm file (which is a part of the extraction process)

     

    Now, double click the InstallMSDEWithMyParameters.bat file, and it will be off to the races.

    You'll notice that your sa password will be "sapassword".  You can alter this if you'd like, but make it something you can remember, because you're dead in the water without it.

     

    I'd reboot after the install, so the MSSQLSERVER services will start up.

     

    Now you have a functional, local, legal development server.

     

    You main tool of trade will be osql.exe.  Google it, and you'll find more info.

    While MSDE is free, Microsoft makes you jump thru some hoops to use it.  They really want you to buy the full version.  But the hoops are much better than using Access for any decent sized relational database.

     

     

    12/1/2005

    Understanding the Simple Factory Pattern

    There are many sites out there showing examples of the Simple Factory Pattern.
    This blog entry takes the simple examples a little further by giving 3 different ways to make the decision on which Concrete Object is returned.
     
    While I have experience with all 3, the use of reflection seems to be the most powerful one.
     
    Alot of times, you may be using this pattern (and specifically the #3 option) without really knowing it.
    The Enterprise Library from Microsoft is one very well known Framework.  The DataAccess objects actually are using reflection to determine which concrete Database object (Microsoft.Practices.EnterpriseLibrary.Data.Database) to use.  The Microsoft.Practices.EnterpriseLibrary.Data.Database is an abstract class, and the concrete class is determined by some string settings in the dataConfiguration.config file.
    (Look closely at the line
    <databaseType name="Sql Server" type="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, Microsoft.EntLib.Data" />
    That's reflection giving back a concrete version of a Microsoft.Practices.EnterpriseLibrary.Data.Database object)
    Here is another link, discussing the ASP.NET 2.0 Provider Model.
     
    So what are the 3 methods?
     
    1.  Using a "key".  Alot of web samples use this method.  The example here uses a string letter to give back a concrete IAnimal.
     
    2.  Use the Environment.  I have to thank a previous colleague for showing me this one.  Well, he actually showed me the Factory Pattern to begin with.  So there's some dap for him.
    This example will return a different concrete class based on Web Environment or a Winforms environment.
     
    3.  Using reflection.  You can setup the concrete class in a configuration file.  The code will use the Interface or Abstract Class, but you can switch out the functionality with just a tweak of a config file.
    This is very flexible, and really keeps your options open for future development.
    In fact, the concrete class can live in its own assembly, just as long as it implements the Interface or Abstract class.  My example shows how you can switch out to a different "RateQuoter" (the mock example uses PostalService and UPS to give different shipping rates).  The switch can occur without recompile, and only a few tweaks of a configuration file.
     
    Thanks to all my colleagues with their mad OO skilllzzzz.
     
    Here's some code snipplets, the download will give you a complete C# code sample to show you how to better use the Factory Design Pattern.
    As bonus material, you'll also see a:
    Web version of the Singleton Pattern.
    A configuration handler, for encapsulating information in your App.config or Web.config file.
     
    #1 Key Method
     
     public class AnimalFactory
     {
     
      public static IAnimal GetAnimal(string key)
      {
       switch (key.ToUpper())
       {
        case "B":
         return new Bird();
        case "C":
         return new Cat();
        case "D":
         return new Dog();
        default:
         throw new ArgumentException("The AnimalFactory was given a Bad Key");
        
       }
      }
     }
     
     
     #2  Environment Method
     
     public class ObjectHolderFactory
     {
      private ObjectHolderFactory()
      {
      }

      public static IObjectHolder GetObjectHolder( )
      {
       //This is a perfect example of a Simple Factory need.
       //When in a non web arena, the singleton is a simple
       //Hashtable or HybridDictionary object
       //When I am in the web arena, I need to piggyback off the
       //web Session object, aka, the WebSessionObjectHolder
       //uses session variables to store the singleton
       //Singleton
       if ( null == System.Web.HttpContext.Current ) // Add a reference to System.Web.dll
       { //Non Web Environment
        return InMemoryObjectHolder.GetInstance();
       }
       else
       { //Web Environment
        return WebSessionObjectHolder.GetInstance();
       }

      }
     }
     
     #3  Reflection
     
     public class RateQuoterFactory
     {
      private static readonly string CONFIG_SECTION_NAME = "RateQuotersSectionName";

      private RateQuoterFactory()
      {
      }

      public static IRateQuoter GetARateQuoter()
      {
       //The RateQuoterSettings encapulates the information found in the App.Config file
       //The "Handler" reads the xml ... to create a RateQuoterSettings instance.
       RateQuoterSettings settings = ((RateQuoterSettings)System.Configuration.ConfigurationSettings.GetConfig(CONFIG_SECTION_NAME));
       //Now you have a RateQuoterSettings instance.
       //use the assembly and class name from the RateQuoterSettings instance. to dynamically create the object
       return CreateInstance( settings.AssemblyName, settings.ClassName , settings.ShippingCompanyHomeState  );
       
      }

      private static IRateQuoter CreateInstance( string assemblyName, string className , string homeState)
      {
       IRateQuoter returnObject = null;
       Assembly assem = Assembly.Load( assemblyName );
       if(null != assem) 
       {
        Type objectType = assem.GetType( className , true , true );
        //The use of "homeState" is here... to show how the CreateInstance can have non default constructors.
        //Notice the second argument is an array of objects.. the array of objects ... needs to match one of the contructor-method-signatures
        returnObject = (IRateQuoter)Activator.CreateInstance( objectType , new object[] {homeState} );
       
       }
       return returnObject;
      
      }

     }
     
     
     <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
     <configSections>
      <section name="RateQuotersSectionName" type="GranadaCoder.Applications.FactoryPatternExamples.FactoryWithReflection.ConfigurationLib.RateQuoterHandler,GranadaCoder.Applications.FactoryPatternExamples" />
     </configSections>
     <RateQuotersSectionName>

      <rateQuoterObject enabled="true" assemblyName="GranadaCoder.Applications.FactoryPatternExamples.ConcreteObjectsOutsideBaseAssembly"
       className="GranadaCoder.Applications.FactoryPatternExamples.ConcreteObjectsOutsideBaseAssembly.ConcreteRateQuoters.UPSRateQuoter" ShippingCompanyHomeState="NC"/>

    </configuration>
     
     
    So there's the summary.  Check out the code, and see for yourself.
      
    Right click HERE and click 'Save As'.
    10/24/2005

    Web Session Wrapper for storing and retrieving objects

    I'd like to take full credit for this, but cannot.  A former colleague inspired the idea via a Winforms application.  I ran with the idea on my next job, where asp.net is my main role.
     
    The idea is basically:
     
    WebSession object, as a Singleton
    With Add/Remove/Indexer(Item) properties.
     
    My contributions were:
    The port over to the web environment.
    I also added a more java-like ".Remove" method, where the object you remove is returned.
    This is good for not "orphaning" objects.  If you only need it once, then .Remove it.  If you need to keep it around, then get it thru the indexer (this).
     
     
    I don't remember the exact reason for "HybridDictionary".  I think the documentation says, when the item count is low, it is one type of collection, and when the item count is high, it is another type of collection.  That sounds right.
     
    Right now, when you get the object back, its an "object".  So you'll have to cast it.
    I haven't had time with my .NET 2.0 Release Candidate (its still about 3 weeks til the official release of 2.0) to port this over, and see if I can use Generics with it.  But that's another day.
     
    There is one caveat with this code.  I don't think you can reference on the first page of your asp.net application.  As in, on your default.aspx page (start up page), don't reference this object.  Any and all pages after the startup page, you'll be fine.  I'll leave the experimenting with you.
     
     
    PS
    Don't forget that you are putting objects in Server memory.  Use cautiously, and don't forget that the indexer will not remove the object, thus orphan them.  I always use the .Remove method (instead of the indexer), as much as possible.
    ..................
     
    using System;
    using System.Collections.Specialized;

    namespace GranadaCoder.CachingFramework
    {
     /// <summary>
     /// Encapsulates storing objects thru Web Sessions.
     /// </summary>

     public class WebSessionDataStore
     {
      //Guid ensures that uniqueid that won't be duplicated by accident
      private static readonly string SESSION_OBJECT_GUID  = "{E338686A-7D68-4fd2-8663-DAFC107AD334}";
      private static WebSessionDataStore   m_singletonInstance = null;
      private HybridDictionary    m_itemCollection  = null;
      #region Singleton Area
      private WebSessionDataStore()
      {
       this.m_itemCollection = new HybridDictionary();
      }
      /// <summary>
      /// Singleton representing WebSessionDataStore.
      /// </summary>
      /// <returns></returns>

      public static WebSessionDataStore GetInstance()
      {
       
       if (null != System.Web.HttpContext.Current)
       {
        if (null != System.Web.HttpContext.Current.Session)
        {
         if (null != System.Web.HttpContext.Current.Session[SESSION_OBJECT_GUID])
         {
          m_singletonInstance = (WebSessionDataStore)System.Web.HttpContext.Current.Session[SESSION_OBJECT_GUID];
         }
        }
       }
       if( null == m_singletonInstance )
       {
        m_singletonInstance = new WebSessionDataStore(  );
        System.Web.HttpContext.Current.Session[SESSION_OBJECT_GUID] = m_singletonInstance;
       }
       return m_singletonInstance;
        
      }
      #endregion

      #region Add/Remove/Indexer area
      public void Clear()
      {
       m_itemCollection.Clear( );
      }
      public void Add(string key, object value)
      {
       //this will overwrite an existing entry
       if( m_itemCollection.Contains( key ) )
       {
        m_itemCollection.Remove( key );
       }
       m_itemCollection.Add( key, value );
      }
     

      public object Remove(string key)
      {
       // see http://java.sun.com/j2se/1.4.2/docs/api/java/util/Hashtable.html#remove(java.lang.Object)
       // for java method, which returns the object you removed, in case
       // you want to do something with it
       object returnObject = null;
       if (null != this.m_itemCollection)
       {
        if( m_itemCollection.Contains( key ) )
        {
         returnObject = this.m_itemCollection[key];
         m_itemCollection.Remove( key );
        }
        
       }
       return returnObject;
      }
      public object this[string key]
      {
       get
       {
        if( null != m_itemCollection[ key ] )
        {
         return m_itemCollection[ key ];
        }
        return null;
       }
      }
      public int Size
      {
       get
       {
        if( null != m_itemCollection )
        {
         return m_itemCollection.Count ;
        }
        return 0;
       }
      }
      #endregion
     }
    }
     
     
     //--------------------
     
    Example Usage:
     
    Start a Web Project.
    Add a TextBox to to WebForm1.aspx
        (Set it to be a Multiline TextBox)
    Add a Button to WebForm1.aspx
     
    Add the code above...as a new Class in the Project.
     
    Add the following code to the code behind:

    private void Page_Load(object sender, System.EventArgs e)

    {

    // Put user code to initialize the page here

    if (!Page.IsPostBack)

    {

    LoadData();

    }

    }

     

    public void LoadData()

    {

    // TO PLACE ITEMS IN THE CONTAINER

    string KEY_NAME = "exception_key";

    //Exception ex = new Exception ("This is a test object");

    //WebSessionDataStore ids = WebSessionDataStore.GetInstance();

    //ids.Add(KEY_NAME, ex);

    //OR

    Exception ex =

    new Exception ("This is a test object");

    WebSessionDataStore.GetInstance().Add(KEY_NAME, ex);

    }

     

    public void PullData()

    {

    this.TextBox1.Text += System.Environment.NewLine + WebSessionDataStore.GetInstance().Size.ToString() + " items in the cache (before getting the item)";

     

    string KEY_NAME = "exception_key";

    Exception ex = null;

     

    //ex = WebSessionDataStore.GetInstance()[KEY_NAME] as Exception; //indexer reads the item, but does not remove it

    //this.TextBox1.Text += System.Environment.NewLine + System.Environment.NewLine + " indexer was used ";

     

    //or

    ex = WebSessionDataStore.GetInstance().Remove(KEY_NAME) as Exception; //remove will get the item one time and remove it

    this.TextBox1.Text += System.Environment.NewLine + System.Environment.NewLine + " .Remove was used ";

    if (null!=ex)

    {

    Console.WriteLine(ex.Message);

    this.TextBox1.Text += System.Environment.NewLine + System.Environment.NewLine + ex.Message ;

    }

    else

    {

    this.TextBox1.Text += System.Environment.NewLine + System.Environment.NewLine + "Item is no longer in the cache!";

    }

    this.TextBox1.Text += System.Environment.NewLine + System.Environment.NewLine + WebSessionDataStore.GetInstance().Size.ToString() + " items in the cache (after getting the item)";

    }

     

    private void Button1_Click(object sender, System.EventArgs e)

    {

    this.TextBox1.Text = "";

    PullData();

    }

    9/27/2005

    Leveraging Dot Net Remoting To Keep Your "Secret Code" Safe


    Leveraging Dot Net Remoting To Keep Your "Secret Code" Safe

    One of the reasons to use Dot Net Remoting is for security reasons.
    A way to implement this is to give your clients the assemblies which contain
    ~Interfaces, but the Concrete classes actually run on the Remoting Server.
    You will supply the Interfaces for the clients to use and invoke, but you actually have the Concrete classes running on the Remoting Server.
    Here is a quote from another web site:  (Thanks Leon for that post)
    From:
    http://secretgeek.net/QAD_Remoting.asp
    You want your clients to be able to call some of your secret proprietary functionality but you don't want to give them the modules that perform this functionality.

    On the surface, it may appear I haven't done anything outside of his example.
    However, if you dig deep into the code, there is a subtle difference.
    It involves how the object populates itself.  While its sounds trivial to create a "READONLY" object, any READONLY object must have
    an entry point.  I've created the Solution(s) so that you can create a truly READONLY object, with only 1 entry point.  In my sample, it is
    the startZip , endZip and weight values.  The example could easily be changed to 1 entry point containing userName and password, and you can develop an object with contains all the UserRoles, etc, which are available only as READONLY.  Like I said, you have to really dig into the code to see what I am talking about.

    There are 2 methods of using Remoting.  You can post the Remoting Service on a tcp port, or you can piggyback off the IIS.
    Each has its own advantages and disadvantages.  There are other methods, these are the 2 with which I have experience.
    With IIS Hosting, you get all the built in features of IIS.  But you sacrafice some performance.  TCP is a little more manual,
    in that you will probably have it running as a Windows Service, and you have to take into consideration the pitfalls of running a Windows Service.

    The sample code works on this premise.  You want to ship an item.  The parameters are startZip, endZip, weightOfPackage.  You want to get the shipping costs for this package.
    The formula for calculating the shipping costs is the "secret code".  You (as the parcel company) want to allow clients to determine shipping costs, but you do not want them (an honest client or a malicious competitor) to know or be able to disassemble your code to figure out what formula you use to calculate the shipping costs.  Thus you need a way for the client to invoke code on a Remoting Server.
    An additional need is that you are superparanoid, and you don't want anyone to be able to {set} the .TotalShippingCost property of your object manually (for whatever reason?).  Thus you need a way to expose the .TotalShippingCost property (with a {get}), but need a way to set the property (internally in the code) using the data collected from the Remoting Server.
    Yes, I know what Web Services are, and perhaps that's the best way to address ~this~ specific business problem.  The code is given as an example for Remoting, and the Shipping Costs Calculator was the generic example which came to mind.
    There is an README_OVERALL.txt, and a README.txt for each project.  I'd check those out first, then step thru the code.
    To get the real feel for Remoting, I'd suggest posting the Remoting Service on another machine, and then calling from you client application.
    The setup in IIS for remoting is ~very simple, once you figure out the steps.  But you can deploy and IIS Remoting Service with as little as 2 files.  (Web.Config and \bin\MyAssembly.dll).  See the README.txt in IISRemotingSampleDeploy for more info.

    Here is glimpse of the code used (on the client):

    Key namespaces (for those who google)
    using System;
    using System.Collections;
    using System.IO;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;  // From System.Runtime.Remoting
     

     public  static IShippingCostsCalculatorLoader loaderILoaderRetrieve()
      {
       
       //Use this syntax for a TCP deployed Remoting Service
       //string sourceURL = "tcp://localhost:9932/ShippingCostsCalculatorTCPListener";
       //Use this syntax for a IIS deployed Remoting Service
       string sourceURL = "http://localhost/DotNetAssemblies/GranadaCoder/Applications/RemotingExample/IISRemotingSampleDeploy/MyFirstRem.rem";
       //notice that the Interface is used here, thus the code is running on the server
       IShippingCostsCalculatorLoader loader = (IShippingCostsCalculatorLoader)Activator.GetObject(
        typeof(IShippingCostsCalculatorLoader), sourceURL);
       return loader;
      }
     
    Happy Trails.  I think the example code will at least get you started on the right path to better understanding of the Dot Net Remoting Framework.
     

    (Right Click , and 'Save Target As' is probably the best approach)
    Download the code HERE

    9/22/2005

    Leveraging XP Excel , Xml, and OPENXML for Data Imports

    NOTE

    Before reading or implementing this strategy, please review the following Microsoft KB's.
    Considerations for server-side Automation of Office
    http://support.microsoft.com/kb/257757/

    My original need was an importer for a winforms application, and NOT an asp.net application.

     Now to the post:

    (Hey, I made the "big time".  Here is a line to this blog getting posted at somebody's site besides my own.)

    http://www.sqlservercentral.com/columnists/sholliday/leveragingxpexcelxmlandopenxmlfordataimports.asp

    (Now, onto the blog.)

     

     

    If you deal with clients, then you've probably at some point or another dealt with importing "their" data.
    Alot of times, this data will come as a Microsoft Excel document.

    I've had my tricks in the past.  Opening Excel in one window, and opening an Access database in another window (linked to Sql Server), I could copy and paste.

    But this was very, very manual.  With the release of Excel XP, I have a new trick.

    Excel XP comes with the ability to save to Xml.  Thus, I decided to match this new functionality with the wonderful ability of Sql Server 2000's ability to use xml data, as relational data, with the OPENXML statement.

    Below is some sample code to get you started.  The C# code will save an Excel file to Xml.  I then have a usp (Sql Server User Stored Procedure) which can import that data.

    Getting the namespace syntax inside the stored procedure was the only thing that took a little time to wade through.  But now that I have it, importing Excel data to Sql Server 2000 as quick as editing some columnnames in the stored procedure.

     

    ..


     

    // Start ExcelXMLConverter.cs file

    using System;

    namespace GranadaCoder.ImportExportLib.ExcelLib

    {

    /// <summary>

    /// Summary description for ExcelXMLConverter.

    /// This class is used as a wrapper to take an existing excel spreadsheet

    /// and to save it as a xml file using the

    /// Microsoft Excel XP ability (aka, "Save As Xml" is a MS Excel XP new functionality)

    ///

    /// Microsoft URL: for reference

    /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_wrcore/html/wrtskhowtosaveworkbooks.asp

    /// and

    /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_vsto2003_ta/html/excelobj.asp

    ///

    ///

    /// You'll need to add a reference to the (COM object) Microsoft Excel library.

    /// This will NOT work with Excel 2000 or Excel 97.

    ///

    ///

    /// </summary>

    public class ExcelXMLConverter

    {

    //Excel variables, Worksheet is not needed for a simple "Save As XML" function

    Excel.Application xlApp = null;

    Excel.Workbook xlBook = null;

    //Excel.Worksheet xlSheet =null;

    string m_inputExcelFileName = null;

    string m_outputXMLFileName = null;

    private bool m_overwriteExistingFile = true;

    string debugMsg = null;

    private void checkParameters()

    {

    string error_msg=string.Empty;

    bool return_val = true; //default is actually true...

    //there are at least 8 chars in the smallest of filenames //ex: (c:\a.ext) has 8 chars

    if (this.m_outputXMLFileName.Length < 8)

    {

    error_msg +="Output File '" + this.m_outputXMLFileName + "' does\n.not appear to be a valid filename.";

    return_val= false;

    }

    System.IO.FileInfo fi = new System.IO.FileInfo(this.m_inputExcelFileName );

    if (!fi.Exists)

    {

    // the input file (excel) does not exist

    error_msg +="Input File '" + this.m_inputExcelFileName + "' does not exist\n.";

    return_val= false;

    }

    if (!this.m_overwriteExistingFile)

    {

    // the bool check is on the "outerloop" to prevent

    // a new fi object, if the check is not needed

    // this says "the user said 'do not overwrite', yet the file does exist

    fi = new System.IO.FileInfo(this.m_outputXMLFileName );

    if (fi.Exists)

    {

    error_msg +="Output File '" + this.m_outputXMLFileName + "' exists and\n'OverWriteExisting' flag set to false.";

    return_val= false;

    }

    }

     

    if (return_val == false)

    {

    throw new ArgumentException(error_msg);

    }

    return;

    }

     

    public void ConvertExcelToXml()

    {

    checkParameters(); // make sure the file names passed in are good

    try

    {

    xlApp = null;

    //xlSheet = null;

    xlApp = new Excel.Application();

    //this turns OFF message box alerts, especially in case of existing files.

    xlApp.DisplayAlerts=false;

    Console.Out.WriteLine (xlApp.Name);

    Console.Out.WriteLine(xlApp.Workbooks.Count);

     

    xlBook = xlApp.Workbooks.Open(@m_inputExcelFileName,

    Type.Missing, Type.Missing, Type.Missing, Type.Missing,

    Type.Missing, Type.Missing, Type.Missing, Type.Missing,

    Type.Missing, Type.Missing, Type.Missing, Type.Missing,

    Type.Missing, Type.Missing);

     

    //Notice the Second argument, its the magic flag for XML

    xlApp.ActiveWorkbook.SaveAs(this.m_outputXMLFileName,

    Excel.XlFileFormat.xlXMLSpreadsheet, Type.Missing, Type.Missing,

    Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange,

    Type.Missing, Type.Missing, Type.Missing, Type.Missing,

    Type.Missing);

    xlBook.Close(false, Type.Missing, Type.Missing);

    xlApp.Quit();

    xlApp = null;

    xlBook = null;

    //xlSheet = null;

    }

    catch (System.Runtime.InteropServices.COMException)

    {

    // ignore this error, user probably cancelled a file overwrithe

    }

    catch(Exception ex)

    {

    // better error handling and log event needed here

    debugMsg = ex.Message;

    throw ex;

    }

    }

    public ExcelXMLConverter(string inputExcelFileName , string outputXMLFileName , bool overwriteExistingFile)

    {

    this.m_inputExcelFileName = inputExcelFileName;

    this.m_outputXMLFileName = outputXMLFileName;

    this.m_overwriteExistingFile = overwriteExistingFile;

    }

    }

    }

     

     

    // End ExcelXMLConverter.cs file

     
     
     
     
     
     
    --//Start tsql (.sql) Code.  There is a sample at the beginning, and then a CREATE PROCEDURE after the sample.
     

    --This sample doc (@doc) is a trimmed up version
    --of a "Save as XML" excel spreadsheet (Excel XP and up)
    --But the core xml information is in present
    --(it was trimmed because of the 8000 character limit
    --normally, @doc would be the input parameter of a
    --stored procedure and would be of type "text")
    --State another way, if you take an ordinary Excel file, open it up on Excel XP, and do a "save as xml", and then open the file in notepad, you'll see something like the xml document below
    --See the bottom of this file to see what the data sample would look like in Excel (the program)
    declare @doc varchar (8000)
    select @doc =
    '
    <?xml version="1.0"?>
    <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
     xmlns:o="urn:schemas-microsoft-com:office:office"
     xmlns:x="urn:schemas-microsoft-com:office:excel"
     xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
     xmlns:html="http://www.w3.org/TR/REC-html40">
     <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
      <Author>sholliday</Author>
      <LastAuthor>Administrator</LastAuthor>
      <Created>2003-12-22T18:58:08Z</Created>
      <LastSaved>2003-12-23T15:51:38Z</LastSaved>
      <Company>organization</Company>
      <Version>10.4219</Version>
     </DocumentProperties>
     <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
      <DownloadComponents/>
      <LocationOfComponents HRef="file:///\\"/>
     </OfficeDocumentSettings>
     <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
      <WindowHeight>8835</WindowHeight>
      <WindowWidth>11340</WindowWidth>
      <WindowTopX>480</WindowTopX>
      <WindowTopY>120</WindowTopY>
      <ProtectStructure>False</ProtectStructure>
      <ProtectWindows>False</ProtectWindows>
     </ExcelWorkbook>
     <Styles>
      <Style ss:ID="Default" ss:Name="Normal">
       <Alignment ss:Vertical="Bottom"/>
       <Borders/>
       <Font/>
       <Interior/>
       <NumberFormat/>
       <Protection/>
      </Style>
     </Styles>
     <Worksheet ss:Name="Sheet1">
      <Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="4" x:FullColumns="1"
       x:FullRows="1">
       <Column ss:Index="2" ss:AutoFitWidth="0" ss:Width="78"/>
       <Column ss:AutoFitWidth="0" ss:Width="71.25"/>
       <Column ss:AutoFitWidth="0" ss:Width="72"/>
       <Column ss:Index="6" ss:AutoFitWidth="0" ss:Width="54.75"/>
       <Column ss:AutoFitWidth="0" ss:Width="60.75"/>
       <Row>
        <Cell><Data ss:Type="String">myuid</Data></Cell>
        <Cell><Data ss:Type="String">mylastname</Data></Cell>
        <Cell><Data ss:Type="String">myfirstname</Data></Cell>
        <Cell><Data ss:Type="String">myaddress1</Data></Cell>
        <Cell><Data ss:Type="String">mycity</Data></Cell>
        <Cell><Data ss:Type="String">mystate</Data></Cell>
        <Cell><Data ss:Type="String">myzip</Data></Cell>
        <Cell><Data ss:Type="String">mygender</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">123</Data></Cell>
        <Cell><Data ss:Type="String">smith</Data></Cell>
        <Cell><Data ss:Type="String">john</Data></Cell>
        <Cell><Data ss:Type="String">123 main</Data></Cell>
        <Cell><Data ss:Type="String">charlotte</Data></Cell>
        <Cell><Data ss:Type="String">nc</Data></Cell>
        <Cell><Data ss:Type="Number">55555</Data></Cell>
        <Cell><Data ss:Type="String">M</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">234</Data></Cell>
        <Cell><Data ss:Type="String">jones</Data></Cell>
        <Cell><Data ss:Type="String">mary</Data></Cell>
        <Cell><Data ss:Type="String">543 hickory</Data></Cell>
        <Cell><Data ss:Type="String">statesville</Data></Cell>
        <Cell><Data ss:Type="String">nc</Data></Cell>
        <Cell><Data ss:Type="Number">44444</Data></Cell>
        <Cell><Data ss:Type="String">F</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">345</Data></Cell>
        <Cell><Data ss:Type="String">moore</Data></Cell>
        <Cell><Data ss:Type="String">pat</Data></Cell>
        <Cell><Data ss:Type="String">342 sycamore</Data></Cell>
        <Cell><Data ss:Type="String">asheville</Data></Cell>
        <Cell><Data ss:Type="String">nc</Data></Cell>
        <Cell><Data ss:Type="Number">33333</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
      </Table>
      <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
       <Selected/>
       <Panes>
        <Pane>
         <Number>3</Number>
         <ActiveCol>1</ActiveCol>
        </Pane>
       </Panes>
       <ProtectObjects>False</ProtectObjects>
       <ProtectScenarios>False</ProtectScenarios>
      </WorksheetOptions>
     </Worksheet>
     <Worksheet ss:Name="Sheet2">
      <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
       <ProtectObjects>False</ProtectObjects>
       <ProtectScenarios>False</ProtectScenarios>
      </WorksheetOptions>
     </Worksheet>
     <Worksheet ss:Name="Sheet3">
      <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
       <ProtectObjects>False</ProtectObjects>
       <ProtectScenarios>False</ProtectScenarios>
      </WorksheetOptions>
     </Worksheet>
    </Workbook>
    '
    --print @doc
    declare @idoc int
    --Create an internal representation of the XML document.
    exec sp_xml_preparedocument @idoc OUTPUT, @doc , '<root xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"/>'

    --Ok, the data above (in xml format) represents (from this example) (which would be originally in an excel file)

    declare @holder table (UniqueID varchar(32), LastName varchar(32) , FirstName varchar(32) ,Street varchar(32) , City  varchar(32), State varchar(2) , Zip varchar(12) , Gender  char(1)  )
    Insert into @holder  -- This will put the results of the Select FROM OPENXML into the table variable (@holder)
    -- SELECT statement using OPENXML rowset provider
    SELECT *
    FROM   OPENXML (@idoc, '/ss:Workbook/ss:Worksheet[position()=1]/ss:Table/ss:Row[position()!=1]',2) --//Remove [position()!=1] if there is no header row
             WITH (
      UniqueID varchar(32) './ss:Cell[position()=1]/ss:Data',
                    LastName  varchar(32) './ss:Cell[position()=2]/ss:Data' ,
      FirstName  varchar(32) './ss:Cell[position()=3]/ss:Data' ,
      Street  varchar(32) './ss:Cell[position()=4]/ss:Data' ,
      City  varchar(32) './ss:Cell[position()=5]/ss:Data' ,
      State  varchar(2) './ss:Cell[position()=6]/ss:Data' ,
      Zip  varchar(12) './ss:Cell[position()=7]/ss:Data' ,
      Gender  char(1) './ss:Cell[position()=8]/ss:Data'
      
      )
    --The keys above are:
    --1.  The namespace issue, notice the "ss" prefix(es)... and also the third argument of the sp_xml_preparedocument procedure above
    --2.  The "position()=1 and position()=2 are for distinquishing "ColumnA" from "ColumnB" in the excel spreadsheet
    --    The "Cell position() is the best way I've determined to distinquish the two columns of data
    --3.  Side note, the Worksheet[position()=1] is in there because a default excel
    --    spreadsheet usually has 3 Worksheets ("Sheet1, Sheet2, Sheet3" on the tabs at the bottom left of Excel)
    --4.  The Row[position()!=1] lets you decide upon a header(label) row or not
    --Other Notes:
    --The sample uses @varchar(8000).  If you create a stored procedure (dbo.uspImportExcelData (@doc text), you can get past the 8000 character limit)
    --I think at about 20meg, it becomes a little too unpredictable.  I have done stuff <4 meg all the time.
     
    --kill off the object
    EXEC sp_xml_removedocument @idoc

    Select * from @holder
     
    --Finally, this will help you setup the Excel file if you want to duplicate
    --The whole situation
    --How the Excel Data would look (in the actual program Microsoft Excel)
    --   [A]                              [B]                              [C]                              [D]                              [E]                              [F]  [G]          [H] 
    --1  myuid                            mylastname                       myfirstname                      myaddress1                       mycity                           my   myzip        m
    --2  123                              smith                            john                             123 main                         charlotte                        nc   55555        M
    --3  234                              jones                            mary                             543 hickory                      statesville                      nc   44444        F
    --4  345                              moore                            pat                              342 sycamore                     asheville                        nc   33333        U
     
    --If you took this simple data and "Save(d) as XML" in Excel XP,
    --you would get something similar to the @doc contents above
     

    Select '--1' ,  UniqueID as '[A]' , LastName  as '[B]'  , FirstName   as '[C]' ,Street  as '[D]'  , City   as '[E]' , State as '[F]'   , Zip as '[G]'   , Gender  as '[H]'  from @holder
     
     
     
     
     
    ---------------------------------------------------------

    if exists (select sysstat & 0xf ,  * from sysobjects
     where id = object_id('dbo.uspExcelImportExample') and sysstat & 0xf = 4)
     drop procedure dbo.uspExcelImportExample
    GO

    CREATE Procedure dbo.uspExcelImportExample ( @xmlDoc text )
    AS
    SET NOCOUNT ON
    --print @doc
    declare @idoc int
    --Create an internal representation of the XML document.
    exec sp_xml_preparedocument @idoc OUTPUT, @xmlDoc , '<root xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"/>'
    print 'Number of characters in the @xmlDoc'
    print DataLength(@xmlDoc)
    print ''

    declare @holder table (UniqueID varchar(32), LastName varchar(32) , FirstName varchar(32) ,Street varchar(32) , City  varchar(32), State varchar(2) , Zip varchar(12) , Gender  char(1))
    Insert into @holder  -- This will put the results of the Select FROM OPENXML into the table variable (@holder)
    -- SELECT statement using OPENXML rowset provider
    SELECT *
    FROM   OPENXML (@idoc, '/ss:Workbook/ss:Worksheet[position()=1]/ss:Table/ss:Row[position()!=1]',2) --//Remove [position()!=1] if there is no header row
             WITH (
      UniqueID varchar(32) './ss:Cell[position()=1]/ss:Data',
                    LastName  varchar(32) './ss:Cell[position()=2]/ss:Data' ,
      FirstName  varchar(32) './ss:Cell[position()=3]/ss:Data' ,
      Street  varchar(32) './ss:Cell[position()=4]/ss:Data' ,
      City  varchar(32) './ss:Cell[position()=5]/ss:Data' ,
      State  varchar(2) './ss:Cell[position()=6]/ss:Data' ,
      Zip  varchar(12) './ss:Cell[position()=7]/ss:Data' ,
      Gender  char(1) './ss:Cell[position()=8]/ss:Data'
      
      )
     
    --kill off the object
    EXEC sp_xml_removedocument @idoc
     
     
    --The keys above are:
    --1.  The namespace issue, notice the "ss" prefix(es)... and also the third argument of the sp_xml_preparedocument procedure above
    --2.  The "position()=1 and position()=2 are for distinquishing "ColumnA" from "ColumnB" in the excel spreadsheet
    --    The "Cell position() is the best way I've determined to distinquish the two columns of data
    --3.  Side note, the Worksheet[position()=1] is in there because a default excel
    --    spreadsheet usually has 3 Worksheets ("Sheet1, Sheet2, Sheet3" on the tabs at the bottom left of Excel)
    --4.  The Row[position()!=1] lets you decide upon a header(label) row or not
    --Other Notes:
    --The sample uses @varchar(8000).  If you create a stored procedure (dbo.uspImportExcelData (@doc text), you can get past the 8000 character limit)
    --I think at about 20meg, it becomes a little too unpredictable.  I have done stuff <4 meg all the time.
    --If your data gets really, really big, consider switching to a #temp table.  That's a discussion for another time.

    Select * from @holder
    --Of course, you gotta do something with the data in the @holder variable table
    --Something like
    --Insert into dbo.User (UniqueID,LastName,FirstName,Street,City,State,Zip,Gender)
    --Select UniqueID,LastName,FirstName,Street,City,State,Zip,Gender from @holder
    --where dbo.User is a "real" table in the database.
    --You can go straight from the OPENXML to the "real" table, using the @holder makes debugging a little easier.
     
     
    SET NOCOUNT OFF

    GO
     
     
     
    --//Let's call the stored procedure we just created, using more than 8000 characters of text
    --//Notice below I have redundant data, but it shows you can put alot more rows..when using the @xmlDoc (text datatype) as an input parameter to a stored procedure
    dbo.uspExcelImportExample
    '
    <?xml version="1.0"?>
    <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
     xmlns:o="urn:schemas-microsoft-com:office:office"
     xmlns:x="urn:schemas-microsoft-com:office:excel"
     xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
     xmlns:html="http://www.w3.org/TR/REC-html40">
     <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
      <Author>sholliday</Author>
      <LastAuthor>Administrator</LastAuthor>
      <Created>2003-12-22T18:58:08Z</Created>
      <LastSaved>2003-12-23T15:51:38Z</LastSaved>
      <Company>organization</Company>
      <Version>10.4219</Version>
     </DocumentProperties>
     <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
      <DownloadComponents/>
      <LocationOfComponents HRef="file:///\\"/>
     </OfficeDocumentSettings>
     <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
      <WindowHeight>8835</WindowHeight>
      <WindowWidth>11340</WindowWidth>
      <WindowTopX>480</WindowTopX>
      <WindowTopY>120</WindowTopY>
      <ProtectStructure>False</ProtectStructure>
      <ProtectWindows>False</ProtectWindows>
     </ExcelWorkbook>
     <Styles>
      <Style ss:ID="Default" ss:Name="Normal">
       <Alignment ss:Vertical="Bottom"/>
       <Borders/>
       <Font/>
       <Interior/>
       <NumberFormat/>
       <Protection/>
      </Style>
     </Styles>
     <Worksheet ss:Name="Sheet1">
      <Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="4" x:FullColumns="1"
       x:FullRows="1">
       <Column ss:Index="2" ss:AutoFitWidth="0" ss:Width="78"/>
       <Column ss:AutoFitWidth="0" ss:Width="71.25"/>
       <Column ss:AutoFitWidth="0" ss:Width="72"/>
       <Column ss:Index="6" ss:AutoFitWidth="0" ss:Width="54.75"/>
       <Column ss:AutoFitWidth="0" ss:Width="60.75"/>
       <Row>
        <Cell><Data ss:Type="String">myuid</Data></Cell>
        <Cell><Data ss:Type="String">mylastname</Data></Cell>
        <Cell><Data ss:Type="String">myfirstname</Data></Cell>
        <Cell><Data ss:Type="String">myaddress1</Data></Cell>
        <Cell><Data ss:Type="String">mycity</Data></Cell>
        <Cell><Data ss:Type="String">mystate</Data></Cell>
        <Cell><Data ss:Type="String">myzip</Data></Cell>
        <Cell><Data ss:Type="String">mygender</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">123</Data></Cell>
        <Cell><Data ss:Type="String">smith</Data></Cell>
        <Cell><Data ss:Type="String">john</Data></Cell>
        <Cell><Data ss:Type="String">123 main</Data></Cell>
        <Cell><Data ss:Type="String">charlotte</Data></Cell>
        <Cell><Data ss:Type="String">nc</Data></Cell>
        <Cell><Data ss:Type="Number">55555</Data></Cell>
        <Cell><Data ss:Type="String">M</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">234</Data></Cell>
        <Cell><Data ss:Type="String">jones</Data></Cell>
        <Cell><Data ss:Type="String">mary</Data></Cell>
        <Cell><Data ss:Type="String">543 hickory</Data></Cell>
        <Cell><Data ss:Type="String">statesville</Data></Cell>
        <Cell><Data ss:Type="String">nc</Data></Cell>
        <Cell><Data ss:Type="Number">44444</Data></Cell>
        <Cell><Data ss:Type="String">F</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">345</Data></Cell>
        <Cell><Data ss:Type="String">moore</Data></Cell>
        <Cell><Data ss:Type="String">pat</Data></Cell>
        <Cell><Data ss:Type="String">342 sycamore</Data></Cell>
        <Cell><Data ss:Type="String">asheville</Data></Cell>
        <Cell><Data ss:Type="String">nc</Data></Cell>
        <Cell><Data ss:Type="Number">33333</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>
       <Row>
        <Cell><Data ss:Type="Number">000</Data></Cell>
        <Cell><Data ss:Type="String">Redundant</Data></Cell>
        <Cell><Data ss:Type="String">Ralph</Data></Cell>
        <Cell><Data ss:Type="String">000 Main St</Data></Cell>
        <Cell><Data ss:Type="String">Raleigh</Data></Cell>
        <Cell><Data ss:Type="String">NC</Data></Cell>
        <Cell><Data ss:Type="Number">00000</Data></Cell>
        <Cell><Data ss:Type="String">U</Data></Cell>
       </Row>

      </Table>
      <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
       <Selected/>
       <Panes>
        <Pane>
         <Number>3</Number>
         <ActiveCol>1</ActiveCol>
        </Pane>
       </Panes>
       <ProtectObjects>False</ProtectObjects>
       <ProtectScenarios>False</ProtectScenarios>
      </WorksheetOptions>
     </Worksheet>
     <Worksheet ss:Name="Sheet2">
      <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
       <ProtectObjects>False</ProtectObjects>
       <ProtectScenarios>False</ProtectScenarios>
      </WorksheetOptions>
     </Worksheet>
     <Worksheet ss:Name="Sheet3">
      <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
       <ProtectObjects>False</ProtectObjects>
       <ProtectScenarios>False</ProtectScenarios>
      </WorksheetOptions>
     </Worksheet>
    </Workbook>
    '
    --//End tsql (.sql) Code.

    9/21/2005

    XmlSerialization with IDictionary and CollectionBase Objects

    Code Download HERE:

     

    I recently was trying to create a class (a subclassed EventArgs class in my case) where I needed the class to contain a property which housed a few named valued pairs.
    Additionally, I wanted to be able to XmlSerialize the class also, so I could store it for later retrieval.  The reasons for that are not really that important.

    Of course, when one thinks of named value pairs, something like a Hashtable comes to mind.
    So there I went off coding, making a class, which contained one public property, which was a hashtable.

    Something like this:


     [Serializable]
     public class ReportRequestEventArgs : System.EventArgs
     {

      private string m_name;
      private Hashtable m_myHash = new Hashtable();

      public string Name   { get { return this.m_name; }  set { this.m_name = value; } }
      public Hashtable NameValuePairsViaAHashTable { get { return this.m_myHash; }  set { this.m_name = m_myHash; } }
    }

     

    Then you become quickly aware that you cannot XmlSerialize an object which is or contains anything of the IDictionary variety.
    And you'll find a post like this one:
    http://www.mattberther.com/2004/06/000487.html

    So I said "No problem, the value part of Hashtable can be complex objects, and all I need is a some string (keys) and some string (values).
    So I'll create a "Item" object, and then a "ItemCollection" object which implements CollectionBase.  Then I'll serialize that the collection object.  Surely that can't be an issue.

    Well, I should have done more reading at Matt Berther's site. Cuz here you go:
    http://www.mattberther.com/2003/04/000052.html

     

    So let me be the first one to say that I really haven't "discovered" anything new, I'm just relaying my struggle with it, and some information that I found.  By reading other sites, you can probably discern the same things I have in this particuliar area.  I have tried to layout some examples so that you can get a better feel for what is happening.  And if you're trying to just figure out some XmlSerialization in general, the examples are a good starting point.

    I did "bring together" 2 different inputs, in regards to the CollectionBase serialization, and Nitin Pande's article on encoding.  When I was saving the xml to a database (text object in sql server 2000), I was finding that the encoding was switching up on me, unless I explicitly set it.

    Any, on to my "Non-Discoveries" (aka, summing up what I figured out from others).

     

    The issue is that everything works, except you can't put an Xml tag on the collection declaration.
    Here is a snipplet:


     [Serializable]
     //[System.Xml.Serialization.XmlRoot("MyCollectionXmlRoot")]
     public class MyCustomCollection : System.Collections.CollectionBase 
     {

     }

    Which is what Matt is talking about at his (second) post listed above.
    Thus he figured out that at least he could put the MyCollection in a Wrapper object.

    I'm with him when he says "All in all, I dont see why the serialization of collections is so different for collections as opposed to other classes."  I don't get on the "that is a bug/oversite" wagon too often, but this one might be it.
    The exact error is "There was an error reflecting type" and then lists the MyCollection object afterwards (that's for googlers trying to find the exact phrase match).


    I actually thought I had figured something out, but turns out I just had a DataContainer myself, it just wasn't as apparent at first.


    Anyway.  Thanks Matt for the DataContainer idea.


    For the rest of you, I've composed a little example C# solution to try and figure some of this stuff out. (Again, I haven't "discovered" anything new).
    I have purposely tried to name things differently, and not to standard, so you can see what is going on.
    I find that sometimes the code becomes very ambigious when the same names are used over and over again.
    You should be able to figure out how to XmlSerialize an:
    Object
    ObjectCollection
    An ObjectTwo which houses an ObjectCollection
    and finally a
    ObjectTwoCollection which hold multiple ObjectTwo's, (and the ObjectTwo's contain the original ObjectCollection).
    Let the example do the talking, and you'll see what I mean.


    Back to my original issue, which was I needed a subclassed EventArgs, which contained a (sub)Collection of key/value pairs (the key and value were just strings in this case).
    (If you're looking to do something like this, just replace StateObject.StateAbbrevation and StateObject.StateFullname with "KeyValuePair.Key and KeyValuePair.Value" , and that's about what I have in my code)

    I was able to do this, and in essence my (subclassed)EventArgs class became what Matt is calling the DataContainer.

    My conclusion is that you CAN XmlSerialize a custom (CollectionBase) object, you just can't give it the Xml name you want, UNLESS you do the Matt DataContainer work around.

    The sample code will hopefully help you get a better understanding of what is going on.

    My need was solved, and now I have a XmlSerialized version of my EventArgs class.
    And I learned more about how XmlSerialization works and doesn't work.
    I might email the CRL team at Microsoft and ask them "What gives?"


    ///////////////////////////////////////

    Some other posts of interest:

    http://forums.asp.net/936361/ShowPost.aspx
    http://geekswithblogs.net/kinger/archive/2005/07/04/45407.aspx
    http://weblogs.asp.net/wim/archive/2004/08/12/213700.aspx
    http://www.codeguru.com/forum/showthread.php?threadid=356926
    http://www.ondotnet.com/pub/a/dotnet/2003/03/10/collections.html

     

     

    /////////////// Start Code // (Use the attached image CodeLayOut.gif to help recreate it in your project)

     

    // Start PersonObject.cs

    using System;
    using System.Xml.Serialization;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.BusinessObjects
    {
     /// <summary>
     /// Summary description for PersonObject.
     /// </summary>
     ///

     [Serializable]
     [System.Xml.Serialization.XmlRootAttribute("PersonRootXmlNode")]
     public class PersonObject
     {

      private string m_firstName;
      private string m_lastName;
      private Collections.StateCollection m_stateIHaveLivedIn = new GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.Collections.StateCollection ();

      [XmlElementAttribute("XmlFirstName")] //Keep in mind you can put anything inside the quotes to name the element.
      public string FirstName
      {
       get{ return this.m_firstName; }
       set { this.m_firstName = value;}
      }
      
      [XmlElementAttribute("XmlLastName")]
      public string LastName
      {
       get{ return this.m_lastName; }
       set { this.m_lastName = value;}
      }


      [XmlArray("StatesIHaveLivedIn"), XmlArrayItem("HeresOne")]
      public Collections.StateCollection StatesIHaveLivedIn
      {
       get{ return this.m_stateIHaveLivedIn;}
       set {this.m_stateIHaveLivedIn = value;}
      }

      public PersonObject()
      {
       //the XmlSerializer requires a default Constructor   
      }
      
      public PersonObject(string fn , string ln)
      {
       this.m_firstName = fn;
       this.m_lastName = ln;
      }
     }
    }


    // End PersonObject.cs

     

    // Start StateObject.cs

    using System;
    using System.Xml.Serialization;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.BusinessObjects
    {
     /// <summary>
     /// Summary description for StateObject.
     /// </summary>
     ///

     [Serializable]
     [System.Xml.Serialization.XmlRootAttribute("StateRootXmlNode")]
     public class StateObject
     {
      private string m_stateAbbreviation;
      private string m_stateFullName;
      private string m_secretInformation="Let's throw a non serialized string in for kicks";


      [XmlElementAttribute("XmlStateAbbrev")] //Keep in mind you can put anything inside the quotes to name the element.
      public string StateAbbreviation
      {
       get{return this.m_stateAbbreviation; }
       set{this.m_stateAbbreviation = value;}
      }

      [XmlElementAttribute("XmlFullName_CallMeWhatever")]
      public string StateFullName
      {
       get{return this.m_stateFullName; }
       set{this.m_stateFullName = value;}
      } 

      [System.Xml.Serialization.XmlIgnoreAttribute()] //I just threw this in to complete the example a little more, with an ignore attribute
      public string PropertyWhichWontBeSerialized
      {
       get{return this.m_secretInformation; }
       set{this.m_secretInformation = value;}
      } 
      
      public StateObject()
      {
       //the XmlSerializer requires a default Constructor   
      }

      public StateObject(string abbr , string fullName)
      {
       this.m_stateAbbreviation = abbr;
       this.m_stateFullName = fullName;
      }
     }
    }


    // End StateObject.cs

     

    // Start PersonCollection.cs


    using System;
    using System.Xml.Serialization;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.Collections
    {
     /// <summary>
     /// Summary description for PersonCollection.
     /// </summary>
     ///

     [Serializable]
     // ***** The next line is the issue.  When you uncomment, you get the Reflection error. // see comment in the CollectionOfStatesObjectsExample procedure
     //[System.Xml.Serialization.XmlRootAttribute("PersonCollectionRootXmlNode")]


     public class PersonCollection : System.Collections.CollectionBase 

     {
     
      public void Add (BusinessObjects.PersonObject per )
      {
       base.InnerList.Add(per);
      }

      public BusinessObjects.PersonObject this[int index]
      {
       get
       {
        return (BusinessObjects.PersonObject)base.InnerList[index];
       }
      }
     
     }
    }

     

    // End PersonCollection.cs

     


    // Start PersonCollectionWithArraySubCollection.cs

    using System;
    using System.Xml.Serialization;
    using System.Collections ;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.Collections
    {
     /// <summary>
     /// Summary description for PersonCollectionWithArraySubCollection.
     /// This really isn't a collection, so roll with the punches on this one.
     /// </summary>
     ///


     //Now this works, but relies on an Array (not to be confused with an ArrayList) of PersonObjects.
     [Serializable]
     [System.Xml.Serialization.XmlRootAttribute("PersonCollWithArrayRootXmlNode")]
     public class PersonCollectionWithArraySubCollection
     {

      private BusinessObjects.PersonObject[] m_personArray;
      private string m_messageToUser = "This class is not a true collection, its a wrapper to hold multiple people";

     

      [XmlElementAttribute("XmlSpecialMessage")]
      public string MessageAboutThisClassToTheDeveloper
      {
       get{return this.m_messageToUser; }
       set{this.m_messageToUser = value;}
      } 


      [XmlArray("ArrayofPersons")] [XmlArrayItem("PersonInArray")] //Keep in mind you can put anything inside the quotes to name the element(s).

      public BusinessObjects.PersonObject[] PersonArray
      {
       get { return this.m_personArray; }
       set { this.m_personArray = value; }
      }


     }
    }

     

    // End PersonCollectionWithArraySubCollection.cs

     

    // Start StateCollection.cs


    using System;
    using System.Xml.Serialization;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.Collections
    {
     /// <summary>
     /// Summary description for StateCollection.
     /// </summary>
     ///

     [Serializable]

     // ***** The next line is the issue.  When you uncomment, you get the Reflection error. //see the comment in the CollectionOfStatesObjectsExample procedure
     //[System.Xml.Serialization.XmlRootAttribute("StateCollectionRootXmlNode")]

     
     public class StateCollection : System.Collections.CollectionBase 
     {
     
      public void Add (BusinessObjects.StateObject sta )
      {
       base.InnerList.Add(sta);
      }

      public BusinessObjects.StateObject this[int index]
      {
       get
       {
        return (BusinessObjects.StateObject)base.InnerList[index];
       }
      }
     
     }
    }

    // End StateCollection.cs

     

     

    // Start StateCollectionWrapperForXmlSerialization.cs

    using System;
    using System.Xml.Serialization;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.CollectionWrappers
    {
     /// <summary>
     /// Summary description for StateCollectionWrapperForXmlSerialization.
     /// this really isn't a collection, so roll with the punches
     /// </summary>
     ///
     [Serializable]
     //[System.Xml.Serialization.XmlRootAttribute("StateCollectionWrapper")]  // this might work too (XmlRootAttribute instead of XmlRoot), if you uncomment this line AND comment out the line immediately below
     [System.Xml.Serialization.XmlRoot ("WrapperRoot")]
     public class StateCollectionWrapperForXmlSerialization
     {
      
      private Collections.StateCollection m_states = new Collections.StateCollection ();

      [XmlArray("WrapperXmlArray"), XmlArrayItem("StateItem")]
      public Collections.StateCollection StateCollectionForTheWrapper
      {
       get{ return this.m_states;}
       set {this.m_states = value;}
      }

      
      public StateCollectionWrapperForXmlSerialization()
      {
       
      }
     
     
     }
    }

    // End StateCollectionWrapperForXmlSerialization.cs

     

     

    // Start SerializationHelper.cs

    using System;

    using System.IO;

    using System.Xml;

    using System.Xml.Serialization;

    using System.Collections;

    using System.Reflection;

    using System.Data ;

    using System.Text;

    using System.Runtime.Serialization;

    using System.Runtime.Serialization.Formatters.Binary;

     

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.SerializationLib

    {

    /// <summary>

    /// Summary description for Main.

    /// </summary>

    public class SerializationHelper

    {

    private SerializationHelper()

    {

    }

     

     

    // Hints from http://www.eggheadcafe.com/articles/system.xml.xmlserialization.asp

    //When I was using the "OLD" methods, I found the encoding would change as I saved it to and from a database.

     

    private static String UnicodeByteArrayToString(Byte[] characters)

    {

    UnicodeEncoding encoding = new UnicodeEncoding ();

    String constructedString = encoding.GetString(characters);

    return (constructedString);

    }

    private static Byte[] StringToUnicodeByteArray(String pXmlString)

    {

    UnicodeEncoding encoding = new UnicodeEncoding ();

    Byte[] byteArray = encoding.GetBytes(pXmlString);

    return byteArray;

    }

     

     

     

    public static string SerializeAnyObject(object o , System.Type t)

    {

    String XmlizedString = null;

    MemoryStream memoryStream = new MemoryStream();

    XmlSerializer xs = new XmlSerializer(t);

    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.Unicode);

    xs.Serialize(xmlTextWriter, o);

    memoryStream = (MemoryStream)xmlTextWriter.BaseStream;

    XmlizedString = UnicodeByteArrayToString(memoryStream.ToArray());

    return XmlizedString;

    }

    public static object DeSerializeAnObject(string xml , System.Type t)

    {

    XmlSerializer xs = new XmlSerializer(t);

    MemoryStream memoryStream = new MemoryStream(StringToUnicodeByteArray(xml));

    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.Unicode );

    return xs.Deserialize(memoryStream);

    }

     

     

    public static string SerializeAnyObjectOLD(object o , System.Type t)

    {

    // serialize to xml

    StringWriter sw = new StringWriter();

    XmlSerializer ser = new XmlSerializer(t);

    ser.Serialize(sw, o);

    // return the xml as a string

    return sw.ToString();

     

    //PS

    //You can actually remove the "t" argument, and

    //replace the "(t)" to "o.GetType()"

    //I wrote it like this , so I explicitly set the type when I call the method

    //I'll probably change it later, or overload it?

    }

    public static object DeSerializeAnObjectOLD(string xml , System.Type t)

    {

    //I didnt' really test this for the example.

    //If you want this to be generic, you'll have to cast it (on the outside of this method)

    StringReader reader = new StringReader(xml);

    XmlSerializer ser = new XmlSerializer(t);

    //usually, the code looks like this, its left here for showing what usually happens

    // MyCustomObject returnCustomObject = ( MyCustomObject )ser.Deserialize(reader); //notice the cast

    object returnObject = ser.Deserialize(reader);

    reader.Close();

    return returnObject;

    }

     

     

     

    }

    }

     

    // End SerializationHelper.cs

     


    // Start EntryPoint.cs

    using System;

    namespace GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS
    {
     /// <summary>
     /// Summary description for EntryPoint.
     /// </summary>
     class EntryPoint
     {
      /// <summary>
      /// The main entry point for the application.
      /// </summary>
      [STAThread]
      static void Main(string[] args)
      {
       try
       {

        SingleStateObjectExample();
        CollectionOfStatesObjectsExample();
        SinglePersonExample();
        CollectionOfPersonObjectsExample();
        StateWrapperCollection();
        PersonArrayAttempt();

        //and for kicks, lets deserialize something
        DerserializeSomethingForKicks();


       }

       catch(Exception ex)
       {
        Console.WriteLine (ex.Message );
       }

       Console.WriteLine ("Press Enter to Exit");
       Console.ReadLine();
      }

     
      public static void SingleStateObjectExample()
      {


       Console.WriteLine ("");
       Console.WriteLine ("");

       Console.WriteLine ("Start Single State " );
       BusinessObjects.StateObject sta1 = new BusinessObjects.StateObject("NC", "North Carolina");

       Console.WriteLine( SerializationLib.SerializationHelper.SerializeAnyObject( sta1 , sta1.GetType()));


       Console.WriteLine ("End Single State " );


       /////////////////////////////////////////
      }
     

      public static void CollectionOfStatesObjectsExample()
      {

       //Notice the output here
       //specifically "ArrayOfStateObject"
       //this is the name .NET gives it
       //this is the issue, when you try to say "I want to call it something else"
       //you get the reflection error.
       //Notice that "ArrayOfPersonObject" when the CollectionOfPersonObjectsExample method runs


       Console.WriteLine ("");
       Console.WriteLine ("");


       Console.WriteLine ("Start State Collection " );

       BusinessObjects.StateObject staNC = new BusinessObjects.StateObject("NC", "North Carolina");
       BusinessObjects.StateObject staVA = new BusinessObjects.StateObject("VA", "Virginia");
       BusinessObjects.StateObject staKY = new BusinessObjects.StateObject("KY", "Kentucky");

       Collections.StateCollection stateCollec1 = new Collections.StateCollection();
       stateCollec1.Add(staNC);
       stateCollec1.Add(staVA);
       stateCollec1.Add(staKY);


       Console.WriteLine (SerializationLib.SerializationHelper.SerializeAnyObject (  stateCollec1 , stateCollec1.GetType() ));
       Console.WriteLine ("End State  Collection " );

     

       //////////////////////////////////
       ///
      }


      public static void SinglePersonExample()
      {

       Console.WriteLine ("");
       Console.WriteLine ("");

     

       BusinessObjects.StateObject staNC = new BusinessObjects.StateObject("NC", "North Carolina");
       BusinessObjects.StateObject staVA = new BusinessObjects.StateObject("VA", "Virginia");
       BusinessObjects.StateObject staKY = new BusinessObjects.StateObject("KY", "Kentucky");

       Collections.StateCollection stateCollec1 = new Collections.StateCollection();
       stateCollec1.Add(staNC);
       stateCollec1.Add(staVA);
       stateCollec1.Add(staKY);

     

       Console.WriteLine ("Start Single Person " );
       BusinessObjects.PersonObject per1 = new BusinessObjects.PersonObject("Granada", "Coder");
       per1.StatesIHaveLivedIn = stateCollec1;

       Console.WriteLine (SerializationLib.SerializationHelper.SerializeAnyObject  ( per1 , per1.GetType() ));
       Console.WriteLine ("End Single Person " );


      }

      public static void CollectionOfPersonObjectsExample()
      {


       Console.WriteLine ("");
       Console.WriteLine ("");

     

       Console.WriteLine ("Start Person Collection " );


       BusinessObjects.StateObject staNC = new BusinessObjects.StateObject("NC", "North Carolina");
       BusinessObjects.StateObject staVA = new BusinessObjects.StateObject("VA", "Virginia");
       BusinessObjects.StateObject staKY = new BusinessObjects.StateObject("KY", "Kentucky");

       Collections.StateCollection stateCollec1 = new Collections.StateCollection();
       stateCollec1.Add(staNC);
       stateCollec1.Add(staVA);
       stateCollec1.Add(staKY);

     


       BusinessObjects.StateObject staAL = new BusinessObjects.StateObject("AL", "Punt Bama Punt");
       BusinessObjects.StateObject staWV = new BusinessObjects.StateObject("WV", "West Virginia");
       BusinessObjects.StateObject staTN = new BusinessObjects.StateObject("TN", "Tennessee");

       Collections.StateCollection stateCollec2 = new Collections.StateCollection();
       stateCollec2.Add(staAL);
       stateCollec2.Add(staWV);
       stateCollec2.Add(staTN);

       BusinessObjects.PersonObject perOne = new BusinessObjects.PersonObject("Granada", "Coder");
       perOne.StatesIHaveLivedIn = stateCollec1;

       BusinessObjects.PersonObject perTwo = new BusinessObjects.PersonObject("Jimmy", "CrackCorn");
       perTwo.StatesIHaveLivedIn = stateCollec2;

       Collections.PersonCollection perCollec = new Collections.PersonCollection();
       perCollec.Add (perOne);
       perCollec.Add (perTwo);

     

       Console.WriteLine (SerializationLib.SerializationHelper.SerializeAnyObject   (  perCollec , perCollec.GetType() ));
       Console.WriteLine ("End Person  Collection " );
       //////////////////////////////


      }


      public static void StateWrapperCollection()
      {


       Console.WriteLine ("");
       Console.WriteLine ("");


       Console.WriteLine ("Start State Wrapper Collection " );


       BusinessObjects.StateObject staNC = new BusinessObjects.StateObject("NC", "North Carolina");
       BusinessObjects.StateObject staVA = new BusinessObjects.StateObject("VA", "Virginia");
       BusinessObjects.StateObject staKY = new BusinessObjects.StateObject("KY", "Kentucky");

       Collections.StateCollection stateCollec1 = new Collections.StateCollection();
       stateCollec1.Add(staNC);
       stateCollec1.Add(staVA);
       stateCollec1.Add(staKY);

     


       CollectionWrappers.StateCollectionWrapperForXmlSerialization wrap1 = new GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.CollectionWrappers.StateCollectionWrapperForXmlSerialization ();
       wrap1.StateCollectionForTheWrapper = stateCollec1;


       Console.WriteLine( SerializationLib.SerializationHelper.SerializeAnyObject( wrap1 , wrap1.GetType()));


       Console.WriteLine ("End State Wrapper Collection " );
       /////////////////////////////

     

      }


      
      public static void PersonArrayAttempt()
      {

       Console.WriteLine ("");
       Console.WriteLine ("");


       Console.WriteLine ("Start Person Collection Array Attempt  " );

       BusinessObjects.StateObject staSC = new BusinessObjects.StateObject("SC", "South Carolina");
       BusinessObjects.StateObject staGA = new BusinessObjects.StateObject("GA", "Georgia");
       BusinessObjects.StateObject staFL = new BusinessObjects.StateObject("FL", "Florida");
       
       Collections.StateCollection southStatesCollec = new GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.Collections.StateCollection ();
       southStatesCollec.Add(staSC);
       southStatesCollec.Add(staGA);
       southStatesCollec.Add(staFL);


       BusinessObjects.StateObject staNY = new BusinessObjects.StateObject("NY", "New York");
       BusinessObjects.StateObject staVT = new BusinessObjects.StateObject("VT", "Vermont");
       BusinessObjects.StateObject staNH = new BusinessObjects.StateObject("NH", "New Hampshire");

       Collections.StateCollection northStatesCollec = new Collections.StateCollection ();
       northStatesCollec.Add(staNY);
       northStatesCollec.Add(staVT);
       northStatesCollec.Add(staNH);

     

       BusinessObjects.PersonObject perFromSouth = new BusinessObjects.PersonObject("Deputy", "Dawg");
       BusinessObjects.PersonObject perFromNorth = new BusinessObjects.PersonObject("Roger", "Williams");

       perFromSouth.StatesIHaveLivedIn = southStatesCollec;
       perFromNorth.StatesIHaveLivedIn = northStatesCollec;


       Collections.PersonCollectionWithArraySubCollection personCollecWithArray = new GranadaCoder.Applications.CollectionBaseXmlSerializeExampleCS.Collections.PersonCollectionWithArraySubCollection ();
       personCollecWithArray.PersonArray =new BusinessObjects.PersonObject[] {perFromSouth , perFromNorth};


       Console.WriteLine (SerializationLib.SerializationHelper.SerializeAnyObject  ( personCollecWithArray , personCollecWithArray.GetType() ));
       Console.WriteLine ("End Person Collection Array Attempt  " );

      }


      public static void DerserializeSomethingForKicks()
      {


       Console.WriteLine ("");
       Console.WriteLine ("");


       Console.WriteLine ("Start Deserialize Sample" );
       BusinessObjects.StateObject sta1 = new BusinessObjects.StateObject("NC", "North Carolina");

       string xmlText = SerializationLib.SerializationHelper.SerializeAnyObject( sta1 , sta1.GetType());

       BusinessObjects.StateObject stateFromXml = (BusinessObjects.StateObject)SerializationLib.SerializationHelper.DeSerializeAnObject(xmlText, sta1.GetType());

       Console.WriteLine ( stateFromXml.StateAbbreviation + " " + stateFromXml.StateFullName  );

       Console.WriteLine ("End Deserialize Sample " );


      }

     
     }
    }


    // End EntryPoint.cs


     

    9/1/2005

    Welcome

    Welcome.  I finally figured it was time to start putting some stuff on the web, as I've been leaching for many years now.
     
    Come back from time to time to find some DotNet and Sql Server 2000 discoveries.
     
    ..