Showing posts with label LINQ. Show all posts
Showing posts with label LINQ. Show all posts

Thursday, 23 April 2009

Looking for a URL using Linq and SiteMaps

I've been off sick from work with Man Flu but this afternoon I was getting bored of staying in bed for the second day so I got out my laptop just to have a play in between blowing my nose.


I wanted to write a quick way of looking up the full url path for a page within a sitemap. The only bit of information I know is the key of the page. Now I knew that I could possibly make use of the IndexOf method. This expects a SiteMapNode of the value you are looking for and returns the index of the node, you then need to get the node out of your collection of nodes, example below.


SiteMapNodeCollection nodes = SiteMap.RootNode.GetAllNodes();
int nodeIndex = nodes.IndexOf(new SiteMapNode(SiteMap.Provider, pagekey));
return nodes[nodeIndex].Url ?? String.Empty;

Now that method does work, however it felt dead clunky, I was sure I could write a more .Net 3.5 shiny one line way of doing this using Linq. In fact it was dead easy. First I still needed to get all the nodes, SiteMap.RootNode.GetAllNodes(), but I then cast these to SiteMapNode, I could then use FirstOrDefault with a nice lambda expression that matches the key property. Code below.


SiteMapNode node = SiteMap.RootNode.GetAllNodes().Cast().FirstOrDefault(n => n.Key.Equals(pagekey));
return node != null ? node.Url : String.Empty;
//note: the </sitemapnode> is not part of the code, the code highlighter JS is randomly adding it.

Simple, my only concern is that the bigger the sitemap becomes the more memory / slower this becomes, especially if called multiple times per page. It may make sense to cache the sitemapnodecollection for the duration of the page however that's beyond the scope of this brief article.


What hopefully you can see though is that a little bit of Linq and Lambda expressions can take chunks of code that seem long winded and turn them into nice neat one liners, which I think is usually more readable.

Friday, 25 July 2008

LinqDataSource and Could not find a row that matches the given keys in the original values stored in ViewState.

Today I was working on something quickly where I used a LinqDataSource, to keep things simple I attached a simple list view to it and had insert, update and delete operations.


Insert operations seemed to work OK however when I came to do an update to an existing item it crashed out

   Could not find a row that matches the given keys in the original values stored in ViewState.

Initially I thought that maybe some databinding had gone astray but this wasn't the issue. As I hadn't used design view but source view to create the ListView had missed off the DataKeyNames attribute, this meant that upon update the LinqDataSource was trying to match all my values against the database, of course it couldnt do this. By specifying the DataKeyName to match against, usually your Primary Key if using LinqtoSql, not only do you limit how much matching and work your LinqDataSource has to do, but also you'll find you can update :D


So quick tip, if you get "Could not find a row that matches the given keys in the original values stored in ViewState." double check your ListView, FormView, GridView etc has the DataKeyNames set.

Monday, 21 July 2008

System.Web.Query.Dynamic.ParseException: Digit expected

Today I wrote a quick piece of code using Linq to Sql that would pull out some records from a table given a provided where clause
To make my life easy, I used a LinqDataSource within a web form and then databound a form view to it, this was only a quick prototype of an idea so I felt these were appropriate.


The where clause for my LinqDataSource was being assigned programatically and consisted of a GUID value from a user object I had knocked together. Upon compiling and running the code though I got an odd error: System.Web.Query.Dynamic.ParseException: Digit expected


First thing to check was that the value I was giving the where clause was actually an GUID value, debugging showed this fine, I next looked at putting a hard coded GUID where clause in to see if it was some weirdness caused by my user object, but this wasn't the case. I finally googled the error message, and found 1 meesly result which pointed to the MSDN forums.


Here someone posted that you can convert to various types and to try explicitly converting to a GUID value


   lds.Where = string.Format("user_id == Guid({0})", user_id);

This worked fine, and it made sense too, as when you declare where parameters on the linqdatasource control you normally define its type, as I wasn't doing this nor programtically it was type casting by itself.

Friday, 30 May 2008

Linq + Failed to set one or more properties on type

So I have had quite an interesting hour or so tracking down the following Linq error:


Failed to set one or more properties on type [namespace].EntityContexts.project. Ensure that the input values are valid and can be converted to the corresponding property types.


This was occurring whilst performing an update in a formview. Previously the form had been behaving properly, but this morning I added a new field to the database and updated the LinqtoSQL file. I also added a new databound field to my formview, however since this every time i tried to update the form it crashed out.


A few people have suggested to delete your LinqtoSQL file and readd, this didn't work for me, I even created a new page with just the datasource and formview for testing purposes.


The datasource was configured to select all from a table and had auto insert, update delete set. The formview was just generated.


It was at this point that I noticed the foreign key relationships I had setup were child entities in my entitycontext. This meant that my formview was generating databound controls for my lookup table etc. In my original page these didn't exist as I had manually removed them in sourceview.
So I again removed these controls within my new page and tried again, still no joy, I then wondered if removing them in Design view instead of Source had any impact, and it did. What was happening was my designer code file was not being updated upon rebuilding from the pages source view.


I then thought further on how to avoid this, my initial thoughts were upon inserting a LinqDataSource only select the columns you need, thus skipping the child entities, this would work for read only select data sources however to use auto generate insert and update you can't use select. If you try to do this then you get the following error:


LinqDataSource 'LinqDataSource1' does not support the Select property when the Delete, Insert or Update operations are enabled


There is a way around this though, although its not pretty and i think its a massive bodge but, insert your datasource and manually select the columns you need, create your formview. Then update the datasource, you are now able to turn on auto gen insert, update and delete. However the manual select still exists on your datasource so switch to source view and remove that.


You should then finds it all works...... Total bodge, I'd sooner just ensure i switch to Design mode to remove my extra fields, but hey everyone has there own way...

Saturday, 19 April 2008

For When You Move Your Site

How many of us often end up rebuilding our site's either from one techonology to another, php to ASP.Net or ASP to ASP.Net, my guess is quite a few as I have done this a few times within the last year.


When rebuilding sites you often find that unless you map the old file extension or urls to the new ones your Google rating / ranking is hit as Google, and other search bots will encounter 404 errors instead of the pages they were expecting.

Now the best way when changing your site or even moving pages is to give the user and searchbots a pointer to where the new files are. A signpost if you will, this means they can find the new content and they will now know the new route to the content.


The HTTP specification defines two methods for these type of redirects. A 302 resposne is a temporary redirect, the content has moved for now but will be back in its original home at some point so don't update your references. The 301 status code however is a Moved Permanently redirect. If you serve this up as part of your response header and the new location then a user will be redirected to the new content. If Searchbots receieve this then they update all your stored links in their database to the new address.


What this results in is that your search listings all now point to the new address and you won't experience a drop in you page rank or have people going to 404 pages from a search listing.


Now this is great, however the issue often is how can I make these 301 redirects, do i have to manually add in pages to my new site that redirect to the new pages? In the past I have done this, where all i needed to do was redirect 1 or 2 pages, however last night I needed to cater for a site that had over 12 pages. Manually doing this was not an option.

urlRedirects

So last night I hacked together a simple HTTPHandler. urlRedirects. This HTTPHandler recieves requests and looks at XML file to see if it has a redirect for it. If it does it returns a 301 Header, if it doesn't it returns a 404 header. If the site has a custom 404 file it also returns this, otherwise a generic 404 message is returned.


The HTTP Handler is quite simple, to handle interacting with the XML file and filtering easily I used LINQ. I love LINQ at the moment and will hopefully post on how I've been using it, and learning to use it at another date. The rest of the code then just deals with serving the correct response header and content.

Using urlRedirects

Configuring Your .Net Web Application

If you want to use this project first get the DLL and reference it into your project. Next create yourself an XML file for your redirects, I called mine xmlredirects.xml


Next you need to define your redirects in the following XML Structure:


<?xml version="1.0" encoding="utf-8" ?>
<redirects> 
<redirect originalurl="/loadpage.php?id=7" redirecturl="/page.aspx?key=7" />
</redirects>

So your root node is <redirects> and this contains children node's redirect. Each redirect node has two attributes, originalurl and redirecturl. It is worth noting that the redirects uses the path and querystring so you can have page.php?key=something.


Once you have created your new file in your applications web.config you need to add a new application setting. XmlRedirectsLocation


<add key="XmlRedirectsLocation" value="~/xmlredirects.xml" />

Now you have the DLL references and the XML file created and available we now need to tell the .Net engine to pass any files of our choosing to our new handler. In my example I want to redirect PHP files so to do this I create a new HTTPHandler in my web.config


<add verb="*" path="*.php" type="mjjames.httphandlers.UrlRedirects, UrlRedirects" />

Now this tells the .Net engine what to do with PHP files but your webserver by default will not be passing PHP files to the .Net engine. The next step is to configure IIS to pass your file type to the .Net engine. Again I'll use PHP for my examples.

Configuring IIS

I assume most people will be using IIS6 however configuring it IIS7 is also as easy which I'll comment on in a moment.

IIS6

First of all bring up the properties menu for your site and navigate to the home directory tab. Once here click the configuration button which is in the application settigns area. This will bring up a new window.


The first tab mappings is the one we want to deal with. We want to map PHP files to the ASP.Net engine, so click add. In the new dialogue box we need to enter the ASP.Net isapi dll as our executable, C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll in my case. Then the file extension to send to it, in my example .PHP You can finally configure which VERBS it responds too, I went with the usual .Net ones GET,HEAD,POST,DEBUG. Click OK and then it's time to test your site.


Hopefully you'll find you new directs will kick in and you should see your new content.

IIS7

I found configuring the redirects for IIS7 easier, choose your site and then click on handler mappings.


This then brings up a list of the configured mappings, choose "add managed handler".

On the new window that pops up enter the request path, .php in my example. Then you can choose the handler from the drop down box: mjjames.httphandlers.UrlRedirects. Notice that for IIS7 you don't pass the request to the .Net isapi filter, you pass it straight to the handler. IIS7 is very cool ;) You can then name the new handler mapping for ease and if you want you can configure your request restricitons but this is optional.

That's it, I hope you find the URLRedirects useful, let me know any feedback / feature requests and I'll see if I can accommodate them.

Download urlRedirects