Ed Giardina's .NET Blog

Blogging about Hobbyist Adventures in C#, XNA, ASP.NET and other stuff
Back in the .NET Game; Plus

So I took a new job which has me writing C# code again, which is great! A lot of new developments have happened in ASP.NET land since I've been gone, such as MVC framework, ASP.NET 3.5, etc. However, I've been working to keep up with them and I'm excited about what the future holds.

The last few days I've been working on a Flex + Sharepoint project for my employer. Sharepoint has a robust set of web services, but accessing them can be a bit confusing. For the method 'GetListItems', accessing all items from a list can be pretty easy... the http://sharepointserver/_vti_bin/Lists.asmx service endpoint provides you with the ability to getListItems by listName ...however, if you want to do more than just grab one list item, you might be in for a hurting. Getting a specific subset of lists means you have to pass in a complex parameter, like viewFields ... The viewFields parameter of GetListItems is not a simple type like a string or number. Instead, its an XML subtype and you're required to pass in a specific format of XML within this node. If you pass XML that sharepoint does not expect, you'll see something like ' ViewFields is missing or invalid' ... Normally people consume web services with generated code from ASP.NET... When I found myself in Flex, however, the generated tools fell a little short.

Problem 1 is that Flex WebServices libraries by default append a namespace to each node that they send in their Soap Bodies. Usually the namespace is "tns:" ... that's fine, but it was unclear to me if the complex XML children parameters of the webservice needed this namespace too... turns out, they do not.

<soap-env:envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soap-env:body>
    <tns:getlistitems xmlns:tns="http://schemas.microsoft.com/sharepoint/soap/">
      <tns:listname>Regions</tns:listname>
      <tns:viewfields>
        <viewfields>
          <fieldref name="LinkTitle">
        </fieldref>
      </viewfields>
      <tns:rowlimit>2</tns:rowlimit>
      <tns:queryoptions>
        <queryoptions>
          <includemandatorycolumns>FALSE</includemandatorycolumns>
          <dateinutc>TRUE</dateinutc>
        </queryoptions>
      </tns:queryoptions>
    </tns:viewfields>
  </tns:getlistitems>
</soap-env:body></soap-env:envelope>

(note the lack of 'tns:' in the 'queryoptions' and 'viewfields' parameters)

Problem 2 is lack of examples of actual web requests of Sharepoint services. Which normally you wouldn't need if you're consuming generated code from Visual Studio. But if you're writing custom webservice wrappers, or doing anything nonstandard, then you need to have your hands dirty and understand what's going on at the transport level. For a while I didn't realize that the viewFields parameter needed , as its root child element, a viewFields element... the examples page was unclear; and every other code example had me writing ASP,NET go-between layers. That's not what I want, I want my Flex movie talking right to sharepoint. Anyways, what made this whole process way easier was Flash Builder 4, which generates code for you based on a WSDL; a lot like ASP.NET does. So you end up with something like this:

 

var viewFields:XML = <ViewFields><FieldRef Name="LinkTitle" /></ViewFields>;
 var queryOptions:XML =
                    <QueryOptions>
                       <IncludeMandatoryColumns>FALSE
                          </IncludeMandatoryColumns>
                       <DateInUtc>TRUE</DateInUtc>
                    </QueryOptions>;
           
var myLists:Lists = new Lists();
                               
myLists.addEventListener(FaultEvent.FAULT, handleFaults);
myLists.addEventListener(ResultEvent.RESULT, RegionsResult);
myLists.GetListItems("Regions",null, null, viewFields, "2", queryOptions, null);

Makes sense now, but if you're coming in green, Sharepoint connectivity can be a pain without raw XML examples!

StackOverFlow in mscordlib.dll and SiteMap Providers

Word to the wise, unless you want to spend hours tracking down idiotic Stack Overflows, check your Sitemap files in ASP.NET. Each sitemap node can contain a 'Provider' attribute. This is designed so that you can set up several sitemaps, and have a master sitemap file pull submenuitems from seperate sitemap files (or databases, depending on your sitemap provider configuration, and whether you rolled your own. However, if you mistakenly point a sitemap provider node to the same provider that references the sitemap file you're in, you'll get a stack overflow error. This was tricky because its actually the webserver that blows the stack, so you can't debug or trace your way out of it. What a fool I've been :)

asp:menu control and SiteMap, 'Selected' Item must match exact url?

 So I am back into making .NET websites... and I love the web.sitemap feature of .NET 2.0+ However, I like my web folder structure to be semantic. This means that instead of clunky files with extensions, I like to have a directory for every portion of the site. Example:

http://www.website.com/products/, http://www.website.com/aboutus/

And then every folder has a Default.aspx ... but the problem with this is that the sitemap, when connected to an ASP.NET Menu control, is that the selected page is only recognized if the page name is included... so my sitemap has to be /products/Default.aspx , instead of just /products/... that's a bummer, because then I lose my semantic 'feel'.

Anyone have any ideas on how to modify this? I'd prefer not to override the Menu control if I don't have to.

Update: So I dug a little deeper and found a solution. First, in your web.sitemap file, keep all the URLs to be exact, including the Default.aspx file. Then, in your menu control, add an onDataBound event handler as such:

    protected void SubMenu_DataBound(object sender, EventArgs e)

    {

        foreach (MenuItem Mi in ((Menu)sender).Items)

        {

            //Trim default.aspx from Menu Item URL; this is for SEO

            Mi.NavigateUrl = Mi.NavigateUrl.Replace("Default.aspx", "");

        }

    }

 

 

GamerServices Game Components don't respond!

So I've been re-tooling my demo-game in XNA to take advantage of XNA 2.0 enhancements. So one thing I noticed is that my very simple code that I lifted from NetRumble project wasn't working in my project. It was a simple sign-in invocation:

if (!Guide.IsVisible)

{

Guide.ShowSignIn(1, false);

}

It was displaying, but not responding to input. (although my underlying components were responding to input.)

I had included the addition of the proper component in the Game's constructor:

Components.Add(new GamerServicesComponent(this));

but still no luck. Finally, even though it wasn't in the NetRumble game, I found in the MSDN documentation that "However, in some cases a program might not use the XNA Framework application model or component infrastructure." Whatever. I thought I was doing everything right. But turns out I needed to manually 'prime the pump', so to speak.

http://msdn2.microsoft.com/en-us/library/bb975692.aspx

 Adding the following line in the Game's Update method:

GamerServicesDispatcher.Update();

 Solved my problem. Unsure what I did to hose this component though. Or automatically adding components. Probably somewhere deep in my code I have the Game Components object do a .Clear or something

Check intersection of two date ranges in SQL

Sometimes we have objects which have timespans in our database, such as valid dates for coupons, or say active dates of a sale, or trigger, or some other event. in searching through these, we may want to give a user the ability to see all of these objects which were 'active', or whose timespan intersects  a user's search criteria. So the user gives a set of dates, and we want to see if any date inside the user's date at all also exists inside the date that the object in our DB was active. The following pseudo-code should hash out how I solved this problem. 

AND( 
   (userselected_start_date <= dbobject_end_date AND userselected_end_date >= dbobject_start_date )
   OR
   (userselected_start_date <= dbobject_start_date AND userselected_end_date >= dbobject_end_date)
   OR
   ((userselected_start_date <= dbobject_end_date AND userselected_start_date >= dbobject_start_date) OR

   (userselected_end_date >= dbobject_start_date AND userselected_end_date <= dbobject_end_date))
   )

These three OR statements essentially fill the three criteria about the user's timespan and how it relates to the timespan in the database. There are four possibilities for the set theory relationship between the date ranges.

  1. The date ranges share no common dates. This means the sets are disjoint, and in the above where clause, the rows would not be returned
  2. The date range the user selects is a superset of the database range
  3. The date range the user selects is a subset of the database range
  4. The date range the user selects has some members, but not all members, which are also found in the date range in our database

This is a bit obscure, but in writing web reports this has come up a few times. Hope it helps someone.

Update:

Ruud Campsteijn points out that it is much easier to check if dates don't intersect, and invert the results.

AND NOT (daterange1_startdate > daterange2_end_date OR daterange1_endate < daterange2_startdate)

Prevent Self-Abuse: Process The entire Analysis Project!

So I'm working on a small SQL Server 2005 Analysis Services Project in Visual Studio 2005. Anyone who's ever used this tool knows that its less than forgiving if you have foreign key issues. Namely, if you have a table with a foreign key , and the associated key ID doesn't exist in the other table, you'll get an error. So I have not only a data pull into a warehouse, but a second set of scripts which normalize any rouge data / orphaned IDs.

However, after I thought I fixed all the 'gotchas', I was still getting errors that said that the DB was unable to find the associated key. After much soul searching, I saw the key it was referring to was in the same table? how can a Primary key be looked at as a foreign key? Later I found that the answer was, re-process the entire solution, not just the specific cube. Turns out, if you make dimension changes and just try to process the cube, the cube won't process some of the time, depending on the change. So now, even though its more time consuming, no matter what, I reprocess my entire solution to make sure I don't run into these little inconsistencies.

Creating a Linked Server to a MySQL Server in SQL Server

I am writing a Business Intelligence Project and I found I needed to be able to warehouse data from a MySQL Server. Rather than spend tons of money for a DB conversion software kit, I decided to use a linked server to communicate between the two. Luckily, a generous blogger at Windows Live Spaces provided me with all the steps, pretty much. Thanks Dinesh!

 Also, please note, he is wrong on a few items. You don't need to use OPENQUERY, at least not for SQL Server 2005. You just need to use the triple-dot notation, AKA

SELECT * FROM LINKEDSERVER...tblname

And I wasn't able to get the generic DSN method working. I created a local SYSTEM DSN first, then just put the system DSN name in the appropriate linked server slot.

XNA 2.0 and Drawable Game Components

So I recently updated my XNA project to 2.0 to take advantage of the cool networking libraries and such. However, now I've noticed that game components I add to the component list no longer automatically begin calling their draw methods once they're added. Perhaps previously I was implementing DrawableGameComponents wrong, but I was under the assumption that once you add them to the components list, they automagically draw themselves. They did in 1.0, and now in 2.0 I have to manually call their .Draw method. Maybe the XNA community can help out with this?

 

UPDATE: This article by a member of the XNA team may shed light on my problem. there's a property of a DrawableGameComponent called 'Visible'... it may have been set default true in 1.0 and 2.0 its no longer default enabled.

http://blogs.msdn.com/etayrien/archive/2007/02/02/first-person-shooter-cameras.aspx

Away from .NET for a bit

So I just took a new job at a new company, and of course, it wouldn't be the same if I were still doing the same type of technology. Unfourunately I've taken a position developing in Coldfusion, so I'll be doing less .NET work and that means less blog posts. I still have personal .NET projects I need to get done (including a MySQL -> SQL Server integration  / reporting server deployment I want to get up and running), so expect sporadic updates. I'll respond to any questions via email, though. Keep rocking C#!

 

Posted: Tue, Oct 16 2007 3:06 PM by Richthofen | with no comments
Filed under:
How fickle the SQL!

So I was optimizing a query that displays a timetable of television programming. I'm working to try to optimize it because its currently taking 3-10 seconds per execution, which is less than optimal, considering that the query needs to be made 48 times per pageload, to load 48 timetables.

 The query looks something like this:

SELECT * FROM TimeSlotsInADay CROSS JOIN (

SELECT  DISTINCT dayofweek FROM Dashboard_TEST.dbo.broadcastDates 

)

INNER JOIN ProgrammingData

 Now normally you'd think that wouldn't be too bad. Basically we have a list of timeslots, and we make an entry for every timeslot, every day. But it turns out, the bottleneck according to the Execution Planner was actually the cross join. So I ran the subquery and found out that the subquery was returning days in reverse order, while all my Indexes on those day fields were set to ASC order. So basically when the JOIN happened on programming data, because the list was reversed, the index scanning ended up being equivalent to a table scan.

 So I modified the list to ORDER BY on the subquery (which requires a TOP statement), so the Subquery now looks like:

SELECT DISTINCT TOP 7 dayofweek FROM Dashboard_TEST.dbo.broadcastDates ORDER BY 1

And now the entire parent query executes in under a second. Hope this helps someone
No Identity Specification on DB Copy in SQL Mgmt Studio?

So slowly but surely some of our databases are making it from SQL Server 2000 to SQL Server 2005. In that process, some of our databases are moved using the quick 'copy' method.  Simply right click on the DB in SQL Management Studio, go to tasks, select Copy Database.

However, on my setup:

Microsoft SQL Server Management Studio                        9.00.1399.00
Microsoft Analysis Services Client Tools                        2005.090.1399.00
Microsoft Data Access Components (MDAC)                        2000.085.1117.00 (xpsp_sp2_rtm.040803-2158)
Microsoft MSXML                        2.6 3.0 4.0 5.0 6.0
Microsoft Internet Explorer                        7.0.5730.11
Microsoft .NET Framework                        2.0.50727.832
Operating System                        5.1.2600

When moving from a SQL 2000 -> 2005 copy, the new database fails to maintain the 'identity specification' as YES on the primary keys of my tables. Subsequently, I had to manually add the identity spec. back on about 10 tables. Lame!

I wanted to report this bug to Microsoft, but figuring out how to report a bug is like pulling teeth! There's no general public bug submission form; you can go through their 'connect' program but its a huge pain in the ass. If they want to write better software, Microsoft should at least give me a bug report form.

 

SQL Server Reporting Services - Optional Parameters

Right now, for our most quick-and-dirty reporting needs from Relational and OLAP databases, we are toying with the idea of using SQL Server 2005 Reporting Services to generate our web reports. Using this server means we get ubiquitous access to our reports in an easy-to-use, consistent manner.

 A major hurdle to using Reporting Services is that by default, Parameters cannot be flagged as 'optional'. After much delay, I found there is a way to 'Hack' a report to have an optional Parameter.

 First, place your parameter in the Query as such:

 SELECT * FROM Table
WHERE
(AssetID = @AssetID OR @AssetID IS NULL)

(Coincidentially, this is the same method I used to make parameters optional in .NET DataSets for web applications).

 Second, in the dataset to populate the Parameters, make sure at least one row returns NULL. You'll need this to have the null row appear in the dropdown that the parameter creates. It might look like this:

SELECT DISTINCT AssetID, AssetName FROM Table
UNION
SELECT NULL, 'All'
ORDER BY 1,2

 

Third, configure the parameter as such; with the default value as null, to allow you to select 'All' 

 
 

 Hope this helps someone!


 

Excluding Items from visual Studio 2005 Publish
    Our current software development lifecycle involves developers working on code locally, then publishing to a test server location for Q/A. Using the 'Publish' command in Visual Studio 2005 is a lifesaver. But sometimes you want to only publish part of the project, excluding specific files.

Unlike building a windows application or DLL, there is no verbose build language screen when you go to the properties of a web project. Instead, a mysterious vwd.webinfo file is generated that contains instructions for Visual Studio. You can manually edit this file. Opening it in VS will reveal that it's just an XML file. And reading for a while on the web, I found that you can add an ItemGroup node within the VisualWebDeveloper tag.

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<VisualWebDeveloper>
<!--
  Visual Studio global web project settings.
-->
<ItemGroup>
<ExcludeFromBuild Include="$(SourceWebPhysicalPath)\**\*.config" />
</ItemGroup>
<StartupServices>
<Service ID="{967B4E0D-AD0C-4609-AB67-0FA40C0206D8}"/>
</StartupServices>
</VisualWebDeveloper>

this command excludes .config files from being updated on a publish. hope this helps!
Embedding .NET code and Windows Forms controls into ASPX pages
Sometimes you can't trust the users of your web application to be savvy enough to navigate away from your application. Most likely, what happens is that a user must do a third party process in order to pass data to your web application, such as scanning in an image before uploading it. Having the user need to minimize the browser window, then open a scanning application, then save the file to a remember-able place, then restore the browser window and 'Browse' to that location can be very overwhelming to people not necessarily technical.

Eliminating the steps above and encompassing as much of the functionality within the browser can be very convienient. As a primer, see this article: http://msdn.microsoft.com/msdnmag/issues/02/06/rich/default.aspx

There are a few interesting hiccups I ran into trying to get this to work, and I wanted to share these with you:
  • Getting the control to load can be difficult. To make sure the control itself is loading, add a simple button on the user control. If the control loads with the 'image' type square in the top left, and you don't see your button, check the path to the DLL. make sure the DLL is NOT in the Bin folder (IIS will not serve files from the Bin folder).
  • If you get an 'Object does not support this property or method' error client side in your javascript when trying to access public methods of your class, be sure you have ComVisible set to true above your definition (see below). This most often happens with .NET 2.0
The ComVisible attribute was the hardest nut to crack. Above your class definition, do as such:

1
2
[ComVisible(true)]
public class MyClass : UserControl{...}

This will get rid of the accesibility problems for javascript and the pesky 'object does not support' messages.

What did I use this all for? Well, we support a large base of users who are not technically savvy. As part of their compliance operations they need to have a document management solution that keeps a digital copy of their documents and letters. Our web application has a form to fill out document information, but instead of a 'browse' button for the file itself, we have a 'scan' button in the Windows forms control inside the webpage. When the user clicks that button, the Twain interface takes over, letting them crop the document. When they finish the scanning, Twain passes the image back to our control, which uploads it to the Web Server along with the other form information.

What? No Columns in my GridView?

Well, lesson learned. the GridView is not infalliable.

So I am building another nightmare report that will sift through reams of data. This one has two Gridviews, one nested inside of the other. The point of the Gridview will be to display an asset and have the child gridview's rows display the locations where the asset exists. Aside from the entire table being a Pivot/Crosstab, and that I have to expect, on minimum, 100 assets to be returned for any search, I am also working with a custom data layer written for the project. So binding to these grids is, well, challenging.

To keep the amount of database traffic down, I only make two queries: One to get the asset list, and one to get all locations for those assets. Storing the locations in a datatable, I can use the datatable's 'Select' method to populate the subgrid. One problem we have is that the key field we 'Select' on, the ID of the asset, is already listed in the parent table, so I want to hide it.

    <asp:TemplateField>
        <ItemTemplate>
            <td>&nbsp;<td colspan="5">
            </asp:GridView runat="server" ID="SubViewContainer" CssClass="Grid"/>
        </ItemTemplate>
    </asp:TemplateField>
</Columns>



Here is the nested grid, inside a template field in the parent grid. On the RowDataBound event of the parent grid, I bind the child grid by extracting the control from the parent grid's columns. With the grid extracted, I can bind it individually.

GridView SubView = (GridView)e.Row.Cells[6].Controls[1];
DataRow[] SubData = WeeklySubData.Tables[0].Select("ID = '" + ID + "'");
DataTable Tbl = WeeklySubData.Tables[0].Clone();

foreach (DataRow D in SubData)
{
    Tbl.ImportRow(D);
}
SubView.DataSource = Tbl;
SubView.DataBind();


So this gets us our sub-data bound to the sub-grid. Great! Now let's hide the first column. That first column is only there so we can use the Select method, we don't need to display it. So let's just do
SubView.Columns[0].visible = false;
, right?

Actually, no. For some reason, I don't have access to the Columns and that line would throw a runtime exception. The columns will have a Count of 0 and I can't do anything. So what's the solution?

Eventually I broke down and added an event handler to handle the 'OnRowCreated' event of the sub-grid. When this event fires, we're able to see the row we're in and hide the first cell of that row. I'm sure this is probably more expensive performance-wise; but it also works, which is the primary feature.

The event handler attachment code looks like 

SubView.RowCreated += new GridViewRowEventHandler(SubView_RowCreated);

And the event handler looks like:

void SubView_RowCreated(object sender, GridViewRowEventArgs e)
{
    e.Row.Cells[0].Visible = false;
}


Problem solved. now to explain to my boss what I've been doing all morning.
More Posts Next page »