tag:blogger.com,1999:blog-65417943717515385672024-02-08T06:46:27.531-08:00SkainSezIn which skain sounds off on a myriad of things . . .steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-6541794371751538567.post-63038577771131383602009-04-13T14:14:00.000-07:002009-04-13T14:30:24.868-07:00Controls Within UpdatePanel Causing Full PostbackSo today I was wrapping up a little project I've been working on. As one of my final steps I was going through the app and wrapping ASP UpdatePanels around the parts of my pages that I didn't want to perform full postbacks. This was going just swimmingly until I hit one page where the UpdatePanel just didn't seem to want to work. It didn't cause any errors or anything but when I clicked on controls within the panel the page did a full postback every time.
Did some searching on the web and found that one likely culprit for this symptom is a dyncamically created LinkButton with no ID. Such a beast would likely be found within a data bound control for instance.
But in this case, although I certainly was using a LinkButton it clearly had an ID assigned to it already. So it didn't seem like that was my problem.
Finally through Google I found an old page at MSDN blogs that looked like it might contain information of worth but MSDN blogs is a shit site and was down for a change. Luckily, the GOOG's cache saved the day. The cached version of the page can be found here (at least as I type this it can):
<a href='http://74.125.93.104/search?q=cache:uyC_FClcCbQJ:blogs.msdn.com/bunty/archive/2009/04/07/full-postback-happens-for-controls-in-a-update-panel.aspx+updatepanel+full+postback&cd=7&hl=en&ct=clnk&gl=us&client=firefox-a'>The cached article</a>
What I found within that article is that for some reason if an UpdatePanel is the direct child of a <tr> table row element then it just doesn't work. And sure enough my code looked like this:
<pre class='coloredcode'><<span class='tag'>asp:UpdatePanel</span><span class='attr'> ID=</span><span class='attrv'>"TagsUPNL"</span><span class='attr'> runat=</span><span class='attrv'>"server"</span>>
<<span class='tag'>ContentTemplate</span>>
<<span class='tag'>tr</span>>
<<span class='tag'>td</span><span class='attr'> style=</span><span class='attrv'>"vertical-align: top;"</span><span class='attr'> class=</span><span class='attrv'>"LeftAlign"</span>>
<<span class='tag'>asp:LinkButton</span><span class='attr'> ID=</span><span class='attrv'>"myLB"</span><span class='attr'> runat=</span><span class='attrv'>"server"</span><span class='attr'> Text=</span><span class='attrv'>"some text"</span><span class='attr'> OnClick=</span><span class='attrv'>"MyLB_Click"</span> />
</<span class='tag'>td</span>>
<<span class='tag'>td</span><span class='attr'> style=</span><span class='attrv'>"vertical-align: top;"</span>>
Control to update ...
</<span class='tag'>td</span>>
</<span class='tag'>tr</span>>
</<span class='tag'>ContentTemplate</span>>
</<span class='tag'>asp:UpdatePanel</span>>
</pre>
What I intended with the above was that the entire contents of the table row (both tds in other words) would be included in the partial post-back. But due to this odd detail of the UpdatePanel's implementation this is not a valid way to achieve this.
The fix was to use two UpdatePanels and a trigger, like so:
<pre class='coloredcode'><<span class='tag'>tr</span>>
<<span class='tag'>td</span><span class='attr'> style=</span><span class='attrv'>"vertical-align: top;"</span><span class='attr'> class=</span><span class='attrv'>"LeftAlign"</span>>
<<span class='tag'>asp:UpdatePanel</span><span class='attr'> ID=</span><span class='attrv'>"TagsUPNL"</span><span class='attr'> runat=</span><span class='attrv'>"server"</span>>
<<span class='tag'>ContentTemplate</span>>
<<span class='tag'>asp:LinkButton</span><span class='attr'> ID=</span><span class='attrv'>"AddTagLBTN"</span><span class='attr'> runat=</span><span class='attrv'>"server"</span><span class='attr'> Text=</span><span class='attrv'>"Add Tag"</span><span class='attr'> OnClick=</span><span class='attrv'>"AddTagLBTN_Click"</span> />
</<span class='tag'>ContentTemplate</span>>
</<span class='tag'>asp:UpdatePanel</span>>
</<span class='tag'>td</span>>
<<span class='tag'>td</span><span class='attr'> style=</span><span class='attrv'>"vertical-align: top;"</span>>
<<span class='tag'>asp:UpdatePanel</span><span class='attr'> ID=</span><span class='attrv'>"TagsUPNL2"</span><span class='attr'> runat=</span><span class='attrv'>"server"</span>>
<<span class='tag'>Triggers</span>>
<<span class='tag'>asp:AsyncPostBackTrigger</span><span class='attr'> ControlID=</span><span class='attrv'>"AddTagLBTN"</span><span class='attr'> EventName=</span><span class='attrv'>"Click"</span> />
</<span class='tag'>Triggers</span>>
<<span class='tag'>ContentTemplate</span>>
</<span class='tag'>ContentTemplate</span>>
</<span class='tag'>asp:UpdatePanel</span>>
</<span class='tag'>td</span>>
</<span class='tag'>tr</span>>
</pre>
And that worked!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-12423775031033730032009-04-08T14:16:00.000-07:002009-04-08T14:50:21.161-07:00Declaratively Binding ASP.NET Server Control PropertiesToday I had the need to bind the 'Visible' property of an ASP.NET PlaceHolder control to a boolean property defined in my code-behind. This was certainly not the first time I've had to do this but it seems like every time I need to do it I have a hard time remembering the correct way to do it.
So now that I've figured it out (again) I'm going to make a short post here about how to do it, both for my reference and yours.
With client-side controls you can use the <%= %> operators to bind properties to code-behind generated values. For instance, let's say that for some reason I wanted to define the URL to my home page in my code-behind. I could put a property for accessing this value in the code-behind like so:
<pre class='coloredcode'><span class='kwd'>protected string</span> HomePageUrl
{
<span class='kwd'>get</span>
{
<span class='kwd'>return</span> <span class='st'>"http://mysite.com/default.aspx"</span>;
}
}
</pre>
Then in my .aspx file I could create my client-side href tag declaratively like so:
<pre class='coloredcode'><<span class='tag'>a</span><span class='attr'> href=</span><span class='attrv'>'<span class='dir'><%=</span> HomePageUrl <span class='dir'>%></span>'</span>>Home</<span class='tag'>a</span>>
</pre>
That works just fine. But with server-side controls the above method will simply not work. In my case I wanted to bind the Visible property of the PlaceHolder control to a boolean property in my code-behind. Let's assume a simple property that always returns true:
<pre class="coloredcode"><span class="kwd">protected bool</span> ShouldBeVisible
{
<span class="kwd">get</span>
{
<span class="kwd">return true</span>;
}
}
</pre>
And then we try to use the same technique we used with the anchor tag with the PlaceHolder:
<pre class="coloredcode"><<span class="tag">asp:PlaceHolder</span><span class="attr"> ID=</span><span class="attrv">"PH1"</span><span class="attr"> runat=</span><span class="attrv">"server"</span><span class="attr">
Visible=</span><span class="attrv">"<span class="dir"><%=</span> ShouldBeVisible <span class="dir">%></span>"</span> />
</pre>
This builds OK but when we try to load the page we get a Parser Error:
"Cannot create an object of type 'System.Boolean' from its string representation '<% ShouldBeVisible %>' for the 'Visible' property."
This is causing a problem for two reasons as far as I understand it. Firstly the <%= %> operator returns a string, not a boolean. Secondly the <%= %> operator doesn't get executed until the page is being rendered. This is way too late in the page lifecycle for the property to properly evaluated and applied.
Luckily there is a simple way to solve the problem and that is to use the binding operator instead: <%# %>. This operator gets processed during calls to the DataBind method of whatever it's parent is. In my case I was doing this inside a ListView and the call to the ListView's DataBind method was enough to cause the operator to be evaluated. You can however do this anywhere on your page. Just remember that to get the operator to actually be processed you'll need to call Page.DataBind() somewhere. The Page_Load event handler is usually going to be the best place for this probably.
So the proper code to bind the Visible property of my PlaceHolder to a value from the code-behind is:
<pre class="coloredcode"><<span class="tag">asp:PlaceHolder</span><span class="attr"> ID=</span><span class="attrv">"PH1"</span><span class="attr"> runat=</span><span class="attrv">"server"</span><span class="attr">
Visible=</span><span class="attrv">"<span class="dir"><%#</span> ShouldBeVisible <span class="dir">%></span>"</span> />
</pre>
Hope this helps you out!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com4tag:blogger.com,1999:blog-6541794371751538567.post-33536359050881329232009-04-03T13:58:00.000-07:002009-04-03T15:05:06.016-07:00Centering Non-Text Content (Like Buttons) In A DivRecently I was working on a web form and had the need to center a button below a text box on the form. I was using a table-less layout and I have to admit at first I was stumped.
Basically what I had was a div that contained the text box and the button. To center the button I put another div around it and tried setting text-align: center on that div. Of course that didn't work since a button is not text.
So I did some searching and learned that the correct way to do this is to set the width on the div containing the element to be centered and then to set margin: 0px auto;.
The real key to making this work however (and since I didn't see anyone point this out on the sites that helped me figure this out I'm writing this post now) is to set the width of the div containing the button to be just big enough to contain the button. I originally tried setting the width of the button container div to be the width of the parent container div and my button wasn't centered at all. It was only when I set the width of my button container div to be roughly the width of the button that I started to see it moving towards the center. And when you think about this it makes sense; setting the left and right margins to auto allows the browser to center the div when the page is rendered. But if the div is wider than the element being centered then it is the div being centered rather than the element within it. By matching up the sides of the containing div with the edges of the button we force the centering of the div to take the button with it.
Sample code:
<pre class="javascript"><html>
<head>
<style type=<span class="string" >"text/css"</span>>
<span class="preprocessor" > #PageContainer</span>
{
width: 800px;
background-color: Wheat;
}
<span class="preprocessor" > #CenteredButtonContainer</span>
{
width: 85px;
margin: 0px auto;
}
</style>
</head>
<body>
<div id=<span class="string" >"PageContainer"</span>>
<p>
This is an example paragraph. Isn't it great?
</p>
<div id=<span class="string" >"CenteredButtonContainer"</span>>
<input type=<span class="string" >"submit"</span> />
</div>
</div>
</body>
</html>
</pre>steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-58230107070977623862008-06-04T11:50:00.001-07:002008-07-21T13:42:52.309-07:00Getting Started With Linq To Sql and Sql Server Compact Edition (CE)UPDATE 7.21.2008: Well, I'm now in the middle of my third project using Sql Server CE and I've got to say that it's really kind of a piece of shit and I can't in good conscience recommend using it with LINQ or without. It's a shame because having a decent offering in this space would really be a strength for Microsoft but SqlCE just kind of sucks. The tools suck, the IDE support sucks and the database format itself is so chock-full of limitations and oddities that using it is way more trouble than it's worth. My current advice is that if you've got a project you're thinking of using SqlCE on you should either ditch LINQ and use some other embedded database or else just bite the bullet and use the full version of Sql Server. Bummer huh? Old post preserved below:
Today I had a little project to get done that caused me to take a look at using Microsoft's relatively new product, <a href="http://www.microsoft.com/sql/editions/compact/default.mspx">Sql Server 2005 Compact Edition</a>. As near as I can tell CE (as we'll call Compact Edition from here on out) is sort of like a blend between normal Sql Server and MS Access. In terms of its SQL support and syntax it's like Sql Server. But the big difference is that there is no server. Instead CE utilizes a single database file which has an .sdf extension. This file works similarly to the old Access .mdf files in that you can move it around, copy it, etc. and every instance of the file is its own separate database. Accessing this file from code is very like accessing a normal Sql Server instance: you create a SqlCeConnection object with a connection string and then you can use objects that are immediately familiar like SqlCeDataReader, SqlCeCommand, etc. These objects are found in the System.Data.SqlServerCe namespace and operate almost exactly like their normal System.Data.SqlClient coutnerparts.
But the object of this post is twofold: firstly to explain how to create and work with CE .sdf files and secondly to then show how to use Linq To Sql rather than the SqlServerCe data client classes to access that file in an application. Neither of these tasks is particularly difficult but neither is either obvious or (in my opinion) particularly well documented. So hopefully you've searched for something like 'getting started with Linq to Sql and Sql Server CE' or 'using Linq with Sql Server Compact Edition', you've ended up here and hopefully the following will be of service to you.
To get started you'll need to create your CE database (.sdf) file. You can do this either with Visual Studio 2008 or with Sql Server Management Studio Express SP2. If you're using Management Studio you need to be certain you download the SP2 version for CE support. At the time of this writing it can be found <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=6053c6f8-82c8-479c-b25b-9aca13141c9e&displaylang=en">here</a>. Note that for CE files Management Studio provides absolutely no benefits that I can see over just using the built in VS2008 Server Explorer tools so for this post I'm going to be using VS2008 to create and manage the db.
So to create the db in VS2008 either create a new executable project (WinForms or Console) or open an existing one. Then in the Solution Explorer pane right-click on the project and choose 'Add . . .' -> 'New Item . . .' from the context menu. This will bring up the Add New Item dialog. You might want to filter the list by click on 'Data' in the top left pane of this dialog. The template you are looking for is called 'Local Database'. Select this and then type a logical name for the database. This name will be the physical file name as well as the default name for your Linq DataContext when we create it. For this example we'll call it 'MyAppDB.sdf'. Click OK and the .sdf file will be added to your project. VS will immediately then prompt you to create a DataSource and DataSet for your new database. Since we're going to be using Linq we don't need either of those so just cancel out of the Data Source Configuration Window.
At this point you should see MyAppDB.sdf listed as a file within your project in the Solution Explorer. If you open the Server Explorer you should also see it listed under Data Connections. Expand the listing under Data Connections in the Server Explorer and you should see sub-folders for Tables and Replication. If you do then you're db is created and ready to go. Now all you need to do is add some tables. You can either use SQL to do this by right-clicking the db entry under Data Connections and selecting 'New Query' or you can use the GUI to do it by right-clicking on the db's Tables sub-folder and choosing 'Create Table'. The GUI's probably a bit easier but it's also pretty limited. It will allow you to create a primary key and an identity field. It will allow you to create indexes and to set default values on fields. And of course it allows you to add, delete and modify the columns in the table. What it won't do however is let you create foreign keys. I have absolutely no idea why this is since CE supports them just fine but that's the way it is.
If you want to create a foreign key you've got to do it with SQL. Just in case you can't remember the syntax it should be something like this:
<pre class="csharpcode">
alter table myChildTable
add constraint FK_ParentID
foreign key (ParentID)
references myParentTable(ParentID) on delete cascade </pre>
Once you've got your db schema all set up how you want it you're ready to generate your Linq DBML file so you can start coding against your database. Once again however for whatever reason Microsoft has failed to implement the most obvious way of getting this done. The DBML designer in VS doesn't have full support for CE. You can't just create a new Linq to Sql classes object in your project and then drag tables from the Server Explorer to the .dbml files designer canvas. In fact, there is no way (as far as I can tell) within VS to create a DBML file for a CE database. To do this you have to use the command line tool SqlMetal.exe.
The easiest way to get at SqlMetal in my opinion is to go into your start menu and from there choose 'Microsoft Visual Studio 2008' -> 'Visual Studio Tools' -> 'Visual Studio 2008 Command Prompt'. This will open a console window whose path and everything are set up correctly to run SqlMetal. In this console window you then need to execute a command like this:
<pre>
sqlmetal /dbml:MyAppDB.dbml MyAppDB.sdf /pluralize
</pre>
SqlMetal is the code generator that VS uses to create a DBML file and its code-behinds when you use the designer. While the designer doesn't fully support CE, SqlMetal does. So what we're doing here is an end-run around the designer. Let's take a look at the arguments we're passing to the SqlMetal executable above.
When we specify <pre>'/dbml:MyAppDB.dbml'</pre> we are telling SqlMetal two things. First we're telling it that we want it to generate a DBML file. Second we're telling it that the filepath of the newly generated file should be <pre><thecurrentpath>\MyAppDB.dbml</pre>.
The second argument, <pre>'MyAppDB.sdf'</pre> is the filepath to the CE database file that has contains the table schema we want SqlMetal to run against.
The final argument, <pre>'/pluralize'</pre> tells SqlMetal to automatically handle pluralization of the names of entities. For instance the entity generated to represent rows in a table named Employees will automatically be called Employee. This works pretty well and I recommend using it as it makes your code more readable. In American English at least.
Now one thing you'll note is that I didn't specify paths to either the .dbml file or the .sdf file in the above call. You certainly could, but I find it easier to just cd to your project directory first, before you execute SqlMetal at all. That way everything just refers to the current directory (your project directory) by default.
So, so far your steps were to:
<ol><li>Create or open a project in Visual Studio 2008</li><li>Add a Local Database file to that project</li><li>Added one or more tables to the database created in step 2</li><li>Opened a Visual Studio Command Prompt window and changed directory to your project directory</li><li>Ran the SqlMetal command line tool with something like this: <pre>'sqlmetal /dbml:MyAppDB.dbml MyAppDB.sdf /pluralize'</pre></li></ol>At this point if you look in your project directory you should see that there is a new file there called MyAppDB.dbml just as you specified. Now go back into your project in VS and in the Solution Explorer right-click your project and from the context menu select 'Add' -> 'Existing Item . . .' and in the resulting dialog find MyAppDB.dbml and add it. Now you should see the .dbml file listed in the Solution Explorer under your project. Double-click it and voila! The designer opens up and you should see all of the tables in your database represented graphically, including their relationships if you defined any.
That's really all there is to it. To program against your model simply create an instance of the related DataContext object and use Linq to query against it. For instance:
<pre class="csharpcode">
using (MyAppDB db = new MyAppDB("MyAppDB.sdf"))
{
//add a row to the database
MyTestEntity myEntity = new MyTestEntity();
myEntity.TestProperty = "Test";
db.MyTestEntities.InsertOnSubmit(myEntity);
db.SubmitChanges();
//load rows from the database into objects
var query = from mte in db.MyTestEntities
select mte;
//should output one instance of the word 'Test'
foreach (MyTestEntity e in query)
{
Console.WriteLine(e.TestProperty);
}
}
</pre>
Hopefully someone out there finds this helpful! If so leave a comment and let me know.steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com4tag:blogger.com,1999:blog-6541794371751538567.post-81367495522051484672008-04-08T09:47:00.000-07:002008-06-09T12:29:26.670-07:00Bulk Deletes in Linq to Sql<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css"><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: Consolas, "Courier New", Courier, Monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br /><br />.csharpcode pre { margin: 0em; }<br /><br />.csharpcode .rem { color: #008000; }<br /><br />.csharpcode .kwrd { color: #0000ff; }<br /><br />.csharpcode .str { color: #006080; }<br /><br />.csharpcode .op { color: #0000c0; }<br /><br />.csharpcode .preproc { color: #cc6633; }<br /><br />.csharpcode .asp { background-color: #ffff00; }<br /><br />.csharpcode .html { color: #800000; }<br /><br />.csharpcode .attr { color: #ff0000; }<br /><br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br /><br />.csharpcode .lnum { color: #606060; }<br /></style>
UPDATE: Terry Aney independently came up with a similar solution to this problem as I did. Terry however has a better implementation in that his supports compound primary keys. He's also got similar support for batch updates in there. I recommend that you read about his approach <a href="http://www.aneyfamily.com/terryandann/post/2008/04/Batch-Updates-and-Deletes-with-LINQ-to-SQL.aspx">here</a> but then read his follow-up and download a later version of his code <a href="http://www.aneyfamily.com/terryandann/post/2008/04/LINQ-to-SQL-Batch-UpdatesDeletes-Fix-for-Could-not-translate-expression.aspx">here</a>. Thanks Terry!
A couple of days ago I was working on an application that uses Linq for its data access layer and I was trying to figure out the Linq way of doing a bulk delete. To illustrate the type of operation I was attempting let's imagine a simple HR database. This database has two tables: Employees and EmployeeTypes. A relationship between the tables exists such that each Employee has one and only one type while each EmployeeType has 0, 1 or many Employees. This is accomplished with the following schema:
Employees
========
EmployeeID int(primary key, identity)
EmployeeName varchar
EmployeeTypeID int (foreign key to EmployeeTypes)
EmployeeTypes
============
EmployeeTypeID int (referenced by Employees)
TypeDesc varchar
Let's say that in my imaginary organization we had been experimenting with using offshore developers to decrease our development costs. Now let's imagine that (just like in most attempts at off shoring development) the higher ups have realized that the code coming from across the pond is total crap and that you really do get what you pay for. Therefore the powers that be have decided to can the whole offshore team. Now we need to update the database to get rid of all the offshore employees in the database. (In reality we probably wouldn't really want to delete these rows but this is just an example.)
In Sql this would be a simple thing to accomplish. We could simply execute the following query:
<pre class="csharpcode">
<span class="kwrd">DELETE</span> <span class="kwrd">FROM</span> Employees
<span class="kwrd">WHERE</span> EmployeeTypeID <span class="kwrd">IN</span>
(<span class="kwrd">SELECT</span> EmployeeTypeID
<span class="kwrd">FROM</span> EmployeeTypes
<span class="kwrd">WHERE</span> TypeDesc = <span class="str">'Offshore'</span>)</pre>
Much to my surprise there is no clear analog to this query in Linq. I thought that this would work:
<pre class="csharpcode">
var toDelete = from e <span class="kwrd">in</span> db.Employees
<span class="kwrd">where</span> e.EmployeeType.TypeDesc == <span class="str">"Offshore"</span>
select e;
db.Employees.DeleteAllOnSubmit(toDelete);
db.SubmitChanges();</pre>In fact it does work but when I ran the Sql Profiler I noticed something quite troubling. When Linq executed this code it was first running the select defined by my toDelete var and retrieving the results. It was then going through each Employee returned by the query and issuing a SEPARATE delete call for each.
To me this seemed needlessly inefficient so I started Googling for a solution. I came up with two hits that attempted to solve this problem. The first was from Chris Rock (no not that Chris Rock.) He wrote a good post about a solution to the problem that a guy named <a target="_blank" href="http://www.blogger.com/profile/04340842860289810596">Benjamin Eidelman</a> wrote. You can see this solution at Chris's blog, <a href="http://www.rocksthoughts.com/blog/archive/2008/03/03/delete-multiple-rows-with-linq-to-sql.aspx">RocksThoughts</a>. But there were some problems with this solution. Firstly the code he posted did not build as posted. I traced down the problem to the fact that the capture groups in the regex he was using to parse the query were not named properly. I'm guessing that since .NET regexes put their capture group names between less than/greater than symbols his blogging app is stripping out the group names thinking that they are HTML. Fair enough. I've contacted him and hopefully he'll get the fix I supplied posted.
A much bigger problem for me though was that when I tried to use his code to accomplish the above defined task I got an error that my query type wasn't supported. I tried to use his code like so:
<pre class="csharpcode">
db.DeleteAll(db.Employees.Where(e => e.EmployeeType.TypeDesc == <span class="str">"Offshore"</span>));</pre>This is definitely what I wanted but in examining the code from Chris's blog I quickly determined that it would only work if the Where clause was very simple and limited to checking values on the object being deleted. Joins are not supported by this code at all as far as I can see. And in fact while looking at the underlying code I realized that it relies heavily on string manipulation to achieve its results. And that certainly doesn't seem very Linq-y to me. Linq gives us the facility to pass around a query as a logical construct so it seemed to me that we should be operating on that construct rather than its interpreted string representation.
So from Chris's blog I then followed a link to a Lambda expression-based solution to the same problem. The solution was from <a href="http://weblogs.asp.net/jeffreyzhao/archive/2008/03/06/linq-to-sql-extension-batch-deletion-by-lambda-expression.aspx">Jeffrey Zhao's blog</a>. Here I thought I would find what I was looking for. A method that examined an IQueryable construct and parsed it into an efficient Sql DELETE statement. And yet upon further examination I found that this wasn't really what I was looking for either. First of all, rather than taking an IQueryable as an argument it takes a predicate defined as Expression<func><tentity,>>. While I think this cuts down on the readability of the consuming code I figured no big deal and so I tried my delete query with this library like so:
</tentity,></func><pre class="csharpcode">
db.Employees.Delete(e => e.EmployeeType.TypeDesc == <span class="str">"Offshore"</span>);</pre>This built but when I ran it it the generated query did not join in the EmployeeType table. Instead the query was looking for a field called TypeDesc on the Employees table. So of course it failed. I came to the conclusion that while this method was probably somewhat better than Mr. Eidelman's string manipulations it still didn't do what I wanted.
If you look on the message boards, etc. you will see people asking this question and the answer is usually either to use one of the two above methods or else to write a custom stored procedure to accomplish this type of delete. But having to write a stored proc for every different type of bulk delete I might need to perform just rubbed me the wrong way. I wondered if it would be possible for me to come up with a solution more to my liking.
After some thought I came up with something that so far I'm liking quite a bit. It takes its inspiration from the solutions explored above, combining the legibility of defining the records to delete in a simple Linq query with the relative type safety of using the expression tree to generate the where clause.
Now it doesn't work in all situations.The main one I see right now is that it doesn't support tables with compound primary keys. But other than that I think it's pretty nice. The usage is pretty straightforward:
<pre class="csharpcode">
var toDelete = from e <span class="kwrd">in</span> db.Employees
<span class="kwrd">where</span> e.EmployeeType.TypeDesc == <span class="str">"Offshore"</span>
select e.EmployeeID;
db.Employees.BulkDelete(toDelete);
</pre>Note that the query is selecting a list of the ids of the records to be deleted. This is key because basically what the code does is wrap the select query defined in Linq inside a delete query based on the primary key of the table being deleted from. So the query that is actually executed on the server from the above is:
<pre class="csharpcode">
<span class="kwrd">delete</span> <span class="kwrd">from</span> Employees
<span class="kwrd">where</span> EmployeeID <span class="kwrd">in</span>
(<span class="kwrd">select</span> EmployeeID
<span class="kwrd">from</span> Employees t0
<span class="kwrd">inner</span> <span class="kwrd">join</span> EmployeeTypes t1 <span class="kwrd">on</span> t0.EmployeeTypeID = t1.EmployeeTypeID
<span class="kwrd">where</span> t1.TypeDesc = <span class="str">'Offshore'</span>)
</pre>Unfortunately I haven't yet been able to come up with a way to make the compiler ensure that the enumerable type returned by the user-supplied query matches the type of the primary key. That's just up to the developer right now. I'd love to hear if anyone's got any ideas beyond just making it only work with int. It should I think at the very least also support string and guid keys.
But I think that the Sql Optimizer should be able to execute this sort of query quickly and efficiently. If anyone sees any problems please let me know!
Here is the code for the extension method <tentity>that actually does the work:
</tentity><pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> System.Data.Linq;
<span class="kwrd">using</span> System.Data.Linq.Mapping;
<span class="kwrd">using</span> System.Data;
<span class="kwrd">namespace</span> LinqExtensions
{
<span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> TableExtensions
{
<span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">int</span> BulkDelete<TEntity>(<span class="kwrd">this</span> Table<TEntity> table, IQueryable idsToDelete)
<span class="kwrd">where</span> TEntity : <span class="kwrd">class</span>
{
MetaTable metaTable = table.Context.Mapping.GetTable(<span class="kwrd">typeof</span>(TEntity));
<span class="kwrd">string</span> tableName = metaTable.TableName;
<span class="kwrd">if</span> (metaTable.RowType.IdentityMembers.Count != 1)
{
var keyFields = from im <span class="kwrd">in</span> metaTable.RowType.IdentityMembers
select im.MappedName;
<span class="kwrd">if</span> (keyFields == <span class="kwrd">null</span> || keyFields.Count() < 1 )
{
keyFields = <span class="kwrd">new</span> List<<span class="kwrd">string</span>>();
}
<span class="kwrd">throw</span> <span class="kwrd">new</span> ApplicationException(<span class="kwrd">string</span>.Format(<span class="str">"Error in TableExtensions.BulkDelete<TEntity>: Table {0} has a compound key. BulkDelete can only operate on tables with a single key field. (key fields found: {1}"</span>, tableName, <span class="kwrd">string</span>.Join(<span class="str">","</span>, keyFields.ToArray())));
}
<span class="kwrd">string</span> primaryKey = metaTable.RowType.IdentityMembers[0].MappedName;
System.Data.Common.DbCommand origCmd = table.Context.GetCommand(idsToDelete);
<span class="kwrd">string</span> toDelete = origCmd.CommandText;
<span class="kwrd">string</span> sql = <span class="kwrd">string</span>.Format(<span class="str">"delete from {0} where {1} in ({2})"</span>, tableName, primaryKey, toDelete);
origCmd.CommandText = sql;
<span class="kwrd">bool</span> openedConn = <span class="kwrd">false</span>;
<span class="kwrd">if</span> (origCmd.Connection.State != ConnectionState.Open)
{
openedConn = <span class="kwrd">true</span>;
origCmd.Connection.Open();
}
<span class="kwrd">try</span>
{
<span class="kwrd">return</span> origCmd.ExecuteNonQuery();
}
<span class="kwrd">finally</span>
{
<span class="kwrd">if</span> (openedConn)
{
origCmd.Connection.Close();
}
}
}
}
}
Post in the comments and let me know how it works for you!
</pre>steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com6tag:blogger.com,1999:blog-6541794371751538567.post-18838236112778200752008-04-03T12:06:00.000-07:002008-05-07T12:05:06.104-07:00The REAL Current Directory in .NETI've got this little open source C# app I wrote called <a href="http://code.google.com/p/powerflag/">PowerFlag</a>. PowerFlag is a response to the unending plethora of spammers on Craigslist. Now, I love Craigslist but the spammers really get on my nerves. So I wrote this app to automatically flag as spam posts which contain keywords that the user defines.
The app worked pretty great except for the fact that when I tried to run it through the Windows
Scheduler it seemed to run fine judging by the logs but it never seemed to find any posts to flag. For instance if I ran the app by double-clicking it and clicked the Flag! button in the app it would find maybe 10 or so spam posts and flag them. This would be evident via a text box in the app and the log file that is created on every run. If I right-clicked the scheduled task and chose 'Run' though the log file would be created but it would say that no posts were found to flag.
I lived with it this way for a couple of months before today I finally got around to figuring out what the root of the problem was and how to fix it. My problem was that the app uses two settings files and the definitions for the filepaths for those two files were stored in string constants which concatenated together System.Environment.CurrentDirectory with the file names of the files. By adding more logging I was able to see that when the app was run via the task scheduler the value of System.Environment.CurrentDirectory was not the directory the app executable was in but instead was for some reason c:\windows\system32. Upon further investigation I found different settings files for the app in this location as well. These settings files contained the initial settings that the program uses on first startup and did not contain all of the keywords I had defined using the app itself.
The fix for this problem of System.Environment.CurrentDirectory not always being the same as the current directory of the executing app is to instead use this:
System.IO.Path.GetDirectoryName(System.Reflection.Assembly._
GetExecutingAssembly().Location)
This is pretty much guaranteed to always give you the path to the folder containing the current executable.
Once I made this change my log files overflowed with flagged posts from the scheduled app. Voila!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-63894918685861383182008-03-11T09:10:00.000-07:002008-03-11T09:24:50.091-07:00How To Copy A SqlServer 2005 DatabaseSo today I started a new project here at the office. I'm going to update our homebuilt CMS to include some new functionality that our sites require. The changes I plan to make will require changes to the database structure.
Now the current version of our app's database is running on SqlServer 2005. Since other developers will continue work on the old app while I work on the new one it was necessary for me to create a copy of the database and do my development against this copy.
The problem was that every time I need to make a copy of a database I have a hard time remembering the right way to go about it. Searching on Google was no help. I got links to a bunch of 3rd party crapware and out of date MSDN articles. None of which helped.
Eventually I figured out a good way to do it and figured I'd post it here for anyone else who might need to do this. What I'm describing here is a way to create a duplicate of an existing database on the same server as the existing db. This method uses Sql Server Management Studio Express. I'm sure the full version would work as well. I imagine a similar procedure would work across servers too.
The first step I did was to create a new database on the server which would be the duplicate db. For the purposes of this article let's assume that my original db is called 'Prod' and my new db is called 'Dev'. So I right clicked on the 'Databases' folder in SSMSE (Sql Server Management Studio Express), chose 'New Database . . .' and created a new db called 'Dev'.
Next up I right-clicked on the 'Prod' db (the one I want to duplicate) and chose 'Tasks -> BackUp . . .' I created a full backup of the db just to make sure I had the latest data.
Once the backup had completed I then right-clicked on the 'Dev' database and chose 'Tasks -> Restore -> Database . . .' On the 'General' page I specified 'Prod' as my 'From database' and 'Dev' as my 'To database'. Note that when you select the 'From database' the 'To database' value gets changed to that value. You need to make sure that the 'To database' value is actually the name of the duplicate database.
Next I clicked over to the Options page in the Restore dialog. Here I had to change the 'Restore As' names of the files in the file listing. By default they are the same as the 'Original File Name' values. But that would cause the backup to overwrite the original database. What we want is to change the 'Restore As' names to be the names of the duplicate database's files. In this example I changed these two entries:
C:\Program Files\Microsoft SQL Server\MSSQL\data\Prod.mdf
C:\Program Files\Microsoft SQL Server\MSSQL\data\Prod_log.ldf
To:
C:\Program Files\Microsoft SQL Server\MSSQL\data\Dev.mdf
C:\Program Files\Microsoft SQL Server\MSSQL\data\Dev_log.ldf
Then, still on the Options page of the 'Restore Database' dialog, I checked the 'Overwrite the existing database' checkbox. Next just hit the 'OK' button and let it do its thing.
The end result should be a duplicate of your original database! Hope this helps some of you out there.
So first I created a new, empty db called 'Dev'.steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com4tag:blogger.com,1999:blog-6541794371751538567.post-2387077898475471412008-02-08T09:53:00.000-08:002008-02-08T10:11:10.679-08:00Linq, LinqDataSource and Connection StringsToday I figured something out that might be obvious to some but caused me confusion for a good amount of time. What I figured out is where the default constructor for a DataContext class gets the connection string it uses to connect to a database.
You're probably already aware that when you create a new LINQ To SQL item in a Visual Studio 2008 project a custom DataContext derived class is generated by the SqlMetal tool (this happens invisibly in VS.) This DataContext has numerous constructors. One for example takes a connection string as an argument. A DataContext object created in this way will use the connection string supplied when it needs to access the database.
In fact since I started using LINQ I have been relying heavily on this very constructor. I was using it despite the fact that I knew that there was a default constructor on the generated DataContext as well. The reason for this was that I wasn't really clear on where exactly the default constructor was getting its connection string value from. I did see that if I used the default constructor the data context would just automatically connect to the database I used to create the Linq To Sql file but that was all I knew. And since I do this for a living and most of the applications I develop are developed against a devel db and then deployed against a producton db I just sort of figured that the proper way to handle things would be to always use the previously mentioned DataContext constructor that takes a connection string argument.
I then created a little utility method called GetDataContext that would read a connection string out of the app or web.config file and use that as the argument to the DataContext's constructor, passing back the resulting object.
This method served me pretty well for a couple of months. But this morning it went pear-shaped on me. I was deploying a new LINQ-based web application to our production server. Once up there I tried to load the first page in my browser. To my surprise instead of the page I expected I got an error message that the SQL Server couldn't be reached. I checked and double checked my connection string value in my web.config to make sure it was pointing at the right place. It was. I verified that the same code ran fine in our test environment. It did. "What," I wondered, "could possibly be the problem?"
My next step was to add some logging around that GetDB method. I told it to log the connection string it was using. I compiled and posted the new binaries and tried to access my page again. Again I got the error so I looked in the logs. Oddly enough there was no entry in the log at all for this last request. But I knew the page used the database to display a list. How was it that my GetDB method wasn't getting called?
Then it dawned on me. For this particular list I'd decided to try out the new LinqDataSource object. I hadn't yet really worked with the LinqDataSource and I'd figured this would be a good opportunity.
Now that I remembered using the LinqDataSource it occurred to me that I really didn't know where it was getting its connection string from. I knew that under the hood it must be creating an instance of my DataContext object. And I suspected it was using the default constructor. But how could I get it to use the proper connection string?
First I looked at the properties on the LinqDataSource. There didn't appear to be anything there that would let me control the connection string. I then decided to reexamine my LINQ project and try to figure out where the default constructor's connection string was coming from. This led me to realize that the connection string was defined as a member of the Properties.Settings class and therefore the value for it was in the ConnectionStrings section of an app.config file in my LINQ project.
So in order to get my application to work all I had to do was copy the connection string 'add' directive from the app.config in my LINQ project to the web.config of my web project and then update the connectionString value to the proper one for my production envirionment.
Loaded up my page again and it worked like a charm. And now that I know this I'm going back and fixing all my LINQ applications to use the default contsructor. No more GetDB methods for me!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-72754401232310225022007-10-05T11:43:00.000-07:002007-10-05T12:56:43.207-07:00First Experiences With VS2K8 Part 6 (Disabling Optimistic Concurrency Checks)It's been a while since I posted and that's because I've fallen a bit behind schedule on my first production ASP.NET LINQ project. In my previous posts I explained how easy it is to bind controls to data with LINQ to SQL. My next step was to be the creation of the data entry portion of the application. And that's when the problems started. I started getting errors from LINQ on updates. Stuff like 'Row not found or changed' and 'An entity can only be attached or modified without original state if it declares a version member or does not have an update check policy.'
'What the hell do those mean?' I thought. So I turned to Google. Turns out that these error messages are the result of a feature of LINQ that is failing. This feature is called optimistic concurrency checking and it works like this: in an ideal situation when an object that has been retrieved from the database by LINQ is updated, LINQ generates an update statement that takes into account both the original and new states of the object by only updating the row in the database to the new state of the object if it still matches the original state of the object. Perhaps an example is in order.
Let's say for simplicity's sake that I have a customer table with three columns: CustID (int), Name(varchar) and ModifiedDate(datetime with default of getdate()). Now let's say I ask LINQ for the Customer object with CustID = 1. Let's say I get back the following:
{ CustID = 1,
Name = 'skain',
ModifiedDate = '10/2/2007 8:35'
}
Now let's say I change the Name property to 'skainsez' and then call the SubmitChanges() method on my data context. If I have optimistic concurrency checking enabled on all of my fields then I get an update statement similar to the following:
UPDATE customers
SET Name='skainsez'
WHERE CustID=1
AND Name='skain'
AND ModifiedDate='10/2/2007 8:35'
Notice the extra constraints in the where clause. In order to get the update done it would have been sufficient to just have said 'WHERE CustID=1' but LINQ added the extra two constraints. In doing so it's effectively telling the database to only update the record if it hasn't changed since we last requested it.
This is the heart of optimistic concurrency checking. It helps to deal with concurrency issues that arise when many users are all writing to the same database at the same time. LINQ detects whether the row was successfully updated and can let you know if it wasn't. If it wasn't then you probably need to alert the user and ask them whether they want to overwrite the new data or not.
And this is all great. It's built in. The designer turns it all on by default and it just works. It just works that is unless you happen to be working in ASP.NET or any other essentially stateless environment where DataContexts are only instantiated for the length of a request. And since DataContexts must be disposed, if you're in an ASP.NET environment I can't think of any way to handle them other than to instantiate them at the beginning of the request and dispose them at the end.
So why is this a problem? Well the problem arises when in one request we get an object for LINQ and populate a data entry form and then on the next we rebuild the object from the form data and then try to update it with a new data context. For instance our data binding method might look like:
private void bindData()
{
using (CustomerDataContext db = new CustomerDataContext())
{
Customer c = Customer.GetByID(Convert.ToInt32(Request["CustID"]));
bindCustomerToForm(c);
}
}
Our update method (exectued when the user clicks the update button) might look like this:
private void updateCustomerFromForm()
{
Customer c = getCustomerObjectFromFormElements();
using (CustomerDataContext db = new CustomerDataContext())
{
c.Update(db);
}
}
I believe that many current ASP.NET apps follow this basic pattern. This assumes that the primary key field (CustID) was written out as a read-only form value and is the same on postback as it was when it was written. Now you'll notice that I'm calling an instance method on my customer object called 'Update'. Let's look at how this method might be implemented:
public void Update(CustomerDataContext db)
{
db.SubmitChanges();
}
Now if we were in a stateful application where the datacontext and our objects were not disappearing into the ether this would work fine. But we're in a stateless environment and if we call this version of the Update method no error will be thrown but the database won't be updated either. That's because we created a brand new data context for the purpose of executing the update. This data context doesn't even know about the customer object we created from the form values.
Luckily LINQ has a way for us to alert a data context of the existence of a LINQ object: the Attach method. The attach method allows us to pass an object to a data context so that the data context knows to update it when SubmitChanges is called. So it should be pretty easy to modify our Update method to call it:
public void Update(CustomerDataContext db)
{
db.Customers.Attach(this);
db.SubmitChanges();
}
This compiles and runs just fine. But again no update in the database occurs. What gives? Well I did some digging and I came to the conclusion that no updates were occurring because no changes were made to the object after it was attached so maybe I needed to attach and then set my properties. This meant some kind of ugly code but I figured if it worked I could try and pretty it up later. So I tried:
public static void Update(CustomerDataContext db, Customer newCustData)
{
Customer newCust = new Customer();
db.Customers.Attach(newCust);
newCust.CustID = newCustData.CustID;
newCust.Name = newCustData.Name;
newCust.ModifiedDate = newCustData.ModifiedDate;
db.SubmitChanges();
}
But when I tried this I got an InvalidOperationException:
'Value of member 'CustomerID' of an object of type 'Customer' changed. A member defining the identity of the object cannot be changed. Consider adding a new object with new identity and deleting the existing one instead.'
Adding a new one and deleting the existing one? That sounds a little crazy so I figured this must not be the right way. Did some more digging and noticed that the Attach method has an overload that accepts a bool called 'asModified'. Looking this up on MSDN (which is really scanty for LINQ currently) provided the following definition and nothing more:
'<span class="keyword">True</span> if the entity is to be attached as modified.'
Hmm. 'Well,' I reasoned, 'I do want the entity to be attached as it's been modified.' So I went back to my simpler version of Update and added a 'true' like so:
public void Update(CustomerDataContext db)
{
db.Customers.Attach(this, true);
db.SubmitChanges();
}
I have to admit I thought this would work. But it didn't. I got a different InvalidOperationException:
'An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.'
Hmm. 'if it declares a version member or does not have an update check policy.' What could that mean? Well, first I tried playing around with timestamps thinking that maybe that's what 'version member' means. But I won't waste your time with that because I just couldn't get them to work at all.
Next I started looking into that whole 'update check policy' thing. I did some more digging and found that this is most likely referring to optimistic concurrency checking as I described at the beginning of this article. But how could I make it so that my entity wouldn't have an update check policy and would that even solve my problem?
Turns out that every field of a LINQ to SQL created entity has an attribute on it called UpdateCheck and by default it is set to a value of Always. For every field on every table. What I found out is that if you click on every field (including your primary key field) and in its properties window set the Update Check property value to Never then the last version of the Update method works just fine. No errors and the update happens in the database like you'd expect it to.
But there's also no concurrency checking going on so the last person to hit update is going to be the one whose changes actually show up in the database. Not a horrible situation but wouldn't it be nice if there was a solution here that allowed for optimistic concurrency checking in stateless environment?
I can't say for sure right now whether it's a problem in the beta version, whether I'm just doing it wrong or whether optimistic concurrency checking just doesn't make sense in a stateless environment. What I do know is that by turning it off I was able to get my project back on schedule. Which at this point is pretty important seeing as I went out on a bit of limb here using LINQ for a project with a real deadline.
But that's me. I love to live a life of danger!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com5tag:blogger.com,1999:blog-6541794371751538567.post-59168438002562328622007-09-19T07:48:00.001-07:002007-09-19T08:12:15.071-07:00iPhone UnlockingOK. So I don't have an iPhone and I don't really have a desire for one. But my boss is crazy over it. But he hates AT&T. Which I understand is a fairly common sentiment. So he's been reading all this stuff on the web about unlocking the iPhone and Monday he comes into my office to ask me if I think I can unlock his phone.
I started looking on the web and found a bunch of confusing and often contradictory instructions. Nonetheless I decided to take a shot at it. The effort did not go particularly smoothly and all in all it took me about 5 hours until I got it working with T-Mobile. But I did get it working and my boss was psyched.
So this morning he brings in his girlfriends iPhone and wants me to unlock it too. Now, I certainly wasn't looking forward to spending another 5 hours on this nonsense so I was really hoping my previous experiences would allow me to improve that time. And they did. This time it took about 20 minutes and nearly all of that time was just waiting for various operations to complete.
Since I feel like I've got it pretty well figured out at this point I figured I'd share my method here. The most helpful source of info I found was this <a href="http://www.weputit.com/node/24">page</a>. But it's poorly written and repeats itself often so here's my quick guide to getting it done. This procedure was performed on a phone that had been activated with AT&T but then had the AT&T account cancelled so when I started the phone was activated but had no service. The first phone I unlocked was brand new, never activated and I'm pretty sure the steps would be similar. These instructions also assume that you've got your phone's firmware upgraded to 1.0.2. You can use iTunes to upgrade the firmware if you need to.
First you need to download a Windows app called iBrickr. You can get it <a href="http://cre.ations.net/creation/ibrickr">here</a>. Once you've got it plug your iPhone into your computer and run it. The program should detect your iPhone and tell you that the first step is to 'free your iPhone'. A lot of sites refer to this step as 'jailbreaking'. Just follow the instructions given by iBrickr. NOTE: iBrickr will prompt you during this process to restart the iPhone in 'restore mode'. To do this you need to first turn off the iPhone by holding down the power button until a red slider appears on the screen to shut it down. Slide the slider and once the phone has powered down hit the power button again to restart it. When the gray apple logo appears press and hold both the power and home buttons until a yellow triangle with and exclamation point appears. It takes a while for the triangle to appear and while you're holding the buttons and waiting the phone will appear to shut down and restart twice. Just keep holding those buttons! You've got to wait until the triangle appears. It will, but it takes longer than you'd probably expect.
The next step is to patch and activate the phone. This is done with a batch file (that comes with a bunch of supporting files) that can be found <a href="http://www.gofilego.com/download/78de548700b84c6d8dfeb2b83be77757087e216a">here</a>. Just download this package, extract it from the archive and then find the file named '(CLICK HERE) activate.bat'. Double click this file and follow the instructions it gives you on the screen.
Once that's done you need to go back into iBrickr and install the applications that actually do the unlocking. With your phone connected to your PC open iBrickr and click on 'Applications'. This will cause iBrickr to notify you that you first need to install PXL. PXL is apparently some sort of installer application on the iPhone that allows you to install third party software.
Once you've followed all the instructions for installing PXL iBrickr should present you with a button that says 'Browse Applications'. Click this button and then wait for a list of applications to appear. Scroll through the list and select an item called anySim by clicking on it's graphic. iBrickr will let you know that it's downloading the app from the internet and then uploading it to your phone. When this is done repeat the steps but this time choose BinKit from the list and install that.
Now we're almost there. First power down your phone and put an activated T-Mobile sim card in the phone. This should be a card that works in your T-Mobile phone currently. Then power up the phone. You may or may not get an 'unauthorized sim card' error. Just ignore it if you do.
Next make sure your phone isn't going to go to sleep during the unlocking process by going into Settings - General -AutoLock and set it to never. Now go back to your home screen on the iPhone and you should see an icon for anySim. Tap the icon and then slide the slider displayed to unlock. anySim will then display a rather long disclaimer which you need to scroll down to the bottom of. At the bottom of the disclaimer is a red button. Tap this button and wait. This process takes probably close to 10 minutes. Just let it churn away. When it's finally finished it's supposed to tell you that the unlock was successful. But in my two times doing this it hasn't. Instead at the end it says something to the effect of 'The flash was successful but the unlock failed.' Don't panic! In both of my tries I got this message. But when I powered down the phone and then restarted it up in the upper left corner was that sweet, sweet 'T-Mobile' with full signal strength bars. Once you see that the phone is unlocked and working on T-Mobile.
Congratulations!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-54708027463388725342007-08-27T09:46:00.000-07:002007-08-27T09:59:09.075-07:00First Experiences With VS2K8 Part 5 (Data Shaping)This is the fifthin a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. You might also be interested in the other posts which are linked at the right.
This is another quick hit post to talk about something I've been doing a lot of with LINQ that I think is pretty damn powerful. MS calls is data shaping and the basic idea is that you can use LINQ queries to 'reshape' your data in order to make it easier to work with in a given context.
That's probably not too helpful a description without an example so here goes.
Let's say in my order application I need to create a quick reporting page that lists all of our customers' order totals. The page needs three columns: cust id, cust name and total for all orders. For the purpose of this illustration let's assume that I've already got a method that returns a Dictionary<customer,> where the keys are my customers and the values are the sum total of each customer's orders.
Without LINQ I could simply bind a GridView to this Dictionary and then specify a ColumnTemplate for cust id, cust name and orders total. With LINQ however I don't even need to specify those ColumnTemplates. Instead I can use LINQ to shape the data before I bind like so:
var query = from cust in custOrdersDict.Keys
select new
{
ID = cust.Id,
Name = string.Concat(cust.FirstName, " ", cust.LastName),
OrdersTotal = custOrdersDict[key].ToString("C")
};
I then bind my plain vanilla GridView to 'query' and I get exactly what I want. My column headers are 'ID', 'Name' and 'OrdersTotal' and the values in the columns are formatted just how I specified. Name is a concatenation of first and last name and the OrdersTotal value is formatted as currency.
This seems quite useful to me because now my GridView is not so intimately tied to the data it is being bound to. For instance if I needed to add City/State info to the report I would not have to touch my GridView at all. I would simply modify my LINQ query as follows:
var query = from cust in custOrdersDict.Keys
select new
{
ID = cust.Id,
Name = string.Concat(cust.FirstName, " ", cust.LastName),
Location = string.Concat(cust.City, ", ", cust.State),
OrdersTotal = custOrdersDict[key].ToString("C")
};
And now when I bind I get a new Location column. And I didn't have to mess with my GridView at all.
Pretty neat!steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-59106217554739754262007-08-20T09:22:00.000-07:002007-08-20T09:31:01.205-07:00First Experiences With VS2K8 Part 4This is the fourth in a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. You might also be interested in the other posts which are linked at the right.
This is just a quick hit entry because I'm deep in the middle of my project now but I just figured something out that I want to share.
One of the screens in my order taking application needs to allow the user to choose one or more orders from a list and then display those orders with all of their header information. The SQL equivalent would be something like:
select * from OrderHeaders
where OrderID in (<span style="font-style: italic;">list of user-selected ints</span>)
The trouble I ran into is that LINQ doesn't have an 'in' keyword. After a good deal of searching I finally figured out how to do it. LINQ doesn't have an 'in' keyword but it does have a 'Contains' method. Here is how you would use it:
int[] orderIDs = getSelectedOrderIDsFromForm();
OrdersDataContext db = new OrdersDataContext();
var query = from oh in db.OrderHeaders
where orderIDs.Contains(oh.OrderID)
select oh;
It's a little ass-backwards from the SQL way in that rather than in SQL we say we want OrderHeader records that contain the selected IDs. Here it sounds more like we're asking the list of IDs for matches instead.
But using the Sql Profiler I see that the generated SQL is a normal query with an IN clause just like I would have written if I was writing the SQL myself.
A little obtuse but pretty cool nonetheless.steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-90976374492768918922007-08-15T11:19:00.000-07:002007-08-15T11:56:19.722-07:00First Experiences With VS2K8 Part 3This is the third in a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. You might also be interested in the <a href="http://skainsez.blogspot.com/2007/08/my-first-post-first-experiences-with.html">first</a> and <a href="http://skainsez.blogspot.com/2007/08/first-experiences-with-vs2k8-part-2.html">second</a> posts.
At the end of the last post I had successfully created my basic data model and used it on a web page to display a list all of my customers. The problem was that the list really displayed ALL of my customers and the page was way too long. Nobody would want to sit there and sift through all of the rows much less wait for them to download so I figured the next step would be to add sorting and paging.
Luckily for me this couldn't be easier. I simply set the AllowPaging and AllowSorting properties of the GridView to true and everything else was taken care of. I also used the AutoFormat functionality on the GridView to select a simple display style. I chose Classic.
I now had a GridView that was displaying all of my Customer data in a fairly accessible way and it didn't look half bad. Since our phone operators would be taking calls from customers placing orders the first step in the system should probably be to find a customer and now I had a way to do that.
But it still wasn't ideal. A user would have to page through customers to find the customer they wanted. Of course a search box would solve the problem. And I decided a little associated AJAX magic would make for a really nice UI.
The search box would work sort of like an autocomplete box but instead of a drop down appearing with likely candidates when the user started typing I decided to populate the whole GridView with the likely candidates. I also decided not to start the autocomplete operation until after at least 3 characters had been typed.
First of all let me admit that I couldn't figure out how to programmatically configure the LinqDataSource's where clause the way I needed to so I decided to ditch it. I deleted that control and the GridView's reference to it. Then I was ready to proceed with getting the autocomplete working.
To accomplish this I first added an HTML TextBox Input element to the page. I didn't use the ASP:TextBox control becuase it's easier to set the 'onKeyUp' event on the HTML version. I did however set the input element's 'runat=server' property so that it would be easy to access the value of the box in my code behind. For the 'onKeyUp' property I set it to call a javascript function called 'onKeyUp()'. I'll get to that function in a bit.
Next I wrapped my existing GridView with an UpdatePanel and its associated ContentTemplate. I set the onload property of the UpdatePanel to call the server side method "updatePanel1_Load". This is to facilitate the auto-complete functionality. Here's an explanation of how that works:
The search textbox has a function called 'onKeyUp()' assigned to its 'onkeyup' event. This function is defined as:
function onKeyUp()
{
var tb = $get('<%= searchTB.ClientID %>', null);
if (tb.value.length > 2)
{
__doPostBack('<%= updatePanel1.ClientID %>', '');
}
}
This javascript function executes on the client. It gets a reference to the search textbox. It checks the length of the value there and if it's longer than two characters it triggers an AJAX postback that appears to come from the UpdatePanel.
Notice that the ids of the search textbox and the UpdatePanel are being generated at runtime by the ASP engine. This is because ASP.NET prepends the IDs of containing controls onto the ID of their children controls. (So if the textbox's ID is 'searchTB' but it's in an UpdatePanel called updatePanel1 its rendered ID at runtime will be updatePanel1_searchTB, not just searchTB. Every control though has a ClientID property which returns the rendered value of the ID rather than that assigned to it programmatically.)
The postback operation triggered by the above call will then cause the UpdatePanel's OnLoad event to fire and since we assigned the server side method 'updatePanel1_Load' to the onload property this method will now fire. It's contents are:
protected void updatePanel1_Load(object sender, EventArgs e)
{
string searchTerm = searchTB.Value;
if (searchTerm.Length < 2)
{
return;
}
OrdersDataContext db = new OrdersDataContext();
var query = from cust in db.Customers
where cust.lastName.StartsWith(searchTerm)
select cust;
gv1.DataKeyNames = new string[] { "CustID" };
gv1.DataSource = query;
gv1.DataBind();
}
Put this all together and when the page loads it first presents the user with just a textbox. Once the user types three characters the client side script fires an AJAX postback and when that returns a GridView appears with all of the customers whose last names start with the letters in the text box. Each additional letter the user types further refines the results.
In my next post we'll make this page a little more user friendly and then we'll look into giving the user a way to actually select the proper customer when they see the record on the screen.steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-49976489581605165382007-08-15T09:46:00.000-07:002007-08-15T10:09:43.696-07:00First Experiences With VS2K8 Part 2This is the second in a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. The first post can be found <a href="http://skainsez.blogspot.com/2007/08/my-first-post-first-experiences-with.html">here.</a>
In this post I will discuss how I set up my LINQ to SQL data model and how I started using it to build my order taking application. This is not intended to be a tutorial on the specifics of LINQ to SQL. For that I recommend ScottGu's excellent series of posts on the subject which can be found <a href="http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx">here.</a>
For now I've got two projects making up this solution. One is a Web Project (called OrderEntryApp) and is the site with that users will be accessing. The other is a class libarary and is called OrderDBLINQ. It contains the LINQ to SQL mapping and generated classes.
For the OrderDBLINQ project I simply chose to add a new item and selected the Linq To Sql classes option. This created a designer view for me. I then opened the Server Explorer and dragged the tables I was interested in accessing from there onto the new designer view.
This is a legacy database that was created by one of our vendors and for various reasons it would be quite difficult to change it. This is kind of lame since the db in question is not very well designed. Naming conventions are nonexistent and there isn't a single foreign key defined anywhere. But I figured this would make it a good test for LINQ to SQL.
Since there aren't any foreign keys in my crappy db schema the LINQ to SQL designer was unable to infer the relationships of my new objects. This is only a minor stumbling block however since the designer offers tools for defining relationships between objects. The interface is quite similar to the one used by SqlServer to create foreign keys.
Each order in my application is placed by a single customer so I created a relationship between the OrderHeaders table and the Customers table. An order header will have multiple detail items associated with it so I created a relationship between OrderHeaders and OrderDetails. Each OrderDetails entry references a single Product so I created a relationship between OrderDetails and Products. Finally there is a many-to-many relationship between Products and Categories. This many-to-many is defined in the ProductCategories table so I created relationships between Products and ProductCategories and also between Categories and ProductCategories.
This all took me about 20 minutes and a good portion of that time was spent just figuring things out for the first time. All in all it was quite easy to do. But would the code generated from this diagram actually be able to service all of my applications needs?
Well first I figured I'd start with a simple test page. In my OrderEntryApp web project I added a new AJAX Web Form item called Test.aspx. My current goal here was simply to bind a read-only GridView to my Customers table.
The first step was to add a LinqDataSource object to my page and configure it. I was able to choose the DataContext object that was created by the code generator in my OrderDBLINQ project. Next I selected the Customers table from the drop down provided and left everything else at the defaults.
Next I added a GridView to my page. The only property other than 'ID' and 'runat' that I had to set was DataSourceID. I set this to the ID of the LinqDataSource I'd just created and ran the page.
Sure enough there on the page was a simple GridView with all of my customers' information listed. Pretty neat!
Now this was a fairly simple test I suppose but it was also quite simple to implement. Certainly easier than doing this the old way.
Of course now I had a gigantic web page listing every single customer in my database. My next step would be to add some tools to refine the list and add support for paging and sorting. And that will be the subject of my next post.steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0tag:blogger.com,1999:blog-6541794371751538567.post-68706364901407320362007-08-14T21:12:00.000-07:002007-08-14T21:30:23.466-07:00My First Post (First Experiences with Visual Studio 2008)This is not only my first post but also the first post in a series of articles I intend to write about my experiences transitioning from Visual Studio 2005 to Visual Studio 2008 (and .NET 3.5). I'm doing this in the hopes that my stumblings will save others from having to do so in the future.
My first and only experience with VS2K8 is with the recently released <a href="http://go.microsoft.com/?linkid=7171916">Visual Studio 2008 Beta 2 Release.</a> I installed both it and the MSDN documentation from the same page. I installed the stuff a day or so after it came out but didn't really start playing around with it until a week ago when I got handed a new project that I figured would make a good test for 2K8.
The two things I was looking forward to most were the improvements to the HTML editing side of things and LINQ (specifically LINQ to SQL.) I'd read so much about LINQ but hadn't yet bothered to download any of the earlier releases. And since I usually handle backend development in my job I was very interested to learn if and how LINQ to SQL might make my life easier.
While I can't discuss the specifics of the project I was handed it is pretty similar to an order-taking application such as might be used by a phone representative to enter orders into a fulfillment system. It needed to be web based, support multiple users and have a reasonably slick UI. And no one cared what I wrote it in. So I decided why not dive into 2K8 and see what it had to offer?
The application I'll be describing for my examples will be a pretty straightforward order entry system. It will be web-based and will utilize AJAX when called for (I hope.) There will be at a minimum a Customers table, an OrderHeaders table, an OrderDetails table, a Products table, a Categories table and finally a ProductCategories table that defines a many-to-many relationship between Categories and Products (a Category can have many Products and a Product can have many Categories.)
In my next post I'll describe how I created a LINQ to SQL representation of my data model and how I started using that representation to access my database.steve kainhttp://www.blogger.com/profile/14055463404555808688noreply@blogger.com0