Zune bug revealed

by zack.moore January 05, 2009 12:31

A little update on the Zune issue I mentioned in my last post. Aeroxperience gives a good explanation of the bug and shows some source code here. The problem ended up being a bad way to calculate years and the date combined with a while() loop with a case that can loop forever. Check it out.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Zune

Zune 30 GB crash came and went

by zack.moore January 01, 2009 16:16

This may have came and went without you even noticing.

On 31 Dec 2008 all 30 GB Zunes locked up and stopped working. If you think that MS doesn't make a 30 GB Zune, your confusion is understandable. the 30 GB models were the original Zunes which had the circle style navigator instead of the touch based squarish navigators all the new ones have.

(quick side note: I like the new Zunes. I got my Mom a 4 GB Zune for Christmas and I hadn't realized until I played with it that the little square is touch sensitive. I thought it was just a square version of the circle joystick that mine had but its actually very nice upgrade. It was new to me even if this feature has been out a while. Now back to my main point...)

I use my Zune quite a bit. In fact I had just used it on Tuesday when I went running. But I hadn't used it yet on Wednesday when I saw an article on Slashdot about Zunes mass crashing. I said to myself, "That's weird." and turned mine on to see that it too was locked up.

Turns out there was a bug in the original Zune in how it dealt with leap years. 2008 was a leap year and the Zune got confused because it was one day longer than it expected.

I was wondering how long it would take for a fix to come out but it was pretty quick as it turns out. In fact I didn't have to do anything except let the battery run down. See here for the details from MS, but all you had to do was let the battery die then wait until after the new year started in GMT and re-synch your Zune and the leap year problem went away.

So I guess it was mostly a much ado about nothing unless you were using your Zune for your New Years party music.

(another side note: Over my Christmas break I've been reading about XNA Game Studio 3.0. Thats MS tools to write games for Windows, XBox 360, and your Zune. I mention this here because I remember reading that if you want your game to work correctly on the old 30 GB Zunes like mine that you had to be careful how you used the joystick/navigator because it only has up, down, left, right, and click while the new ones have a much more precise control which can detect a greater degree of movement in every direction. Kind'of interesting, to me at least.

Here is the XNA book I'm reading now. It doesn't get into super complicated aspects but it covers all of the basics so I would recommend it as a good getting started book.

Here are some other XNA 3.0 books that are coming out in 2009 that I'm interested in. When I bought the above book there were only a few other 3.0 books slated to come out soon. Now it looks like there are quite a few so I'm only listing a few of the most interesting looking ones:

)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Zune | XNA

Getting SQL Express Studio upgraded when upgrading SQL Server Express 2008

by zack.moore January 01, 2009 15:47

I've been running SQL Server Express 2005 and the Express Management Studio for all my DB development. Ever since SQL Server 2008 came out I've been thinking about upgrading and today I decided to. However, after I upgraded I noticed that my SQL Management Studio didn't upgrade. After a few minutes I couldn't figure it out so I did a web search and found that Luis Rocha had already figured out the solution to the problem.

You can read his solution here. Some of the screen shots weren't working when I read it and I think he mis-named one of the items to click on Step #4 but much thanks to him for figuring it out.

Essentially, the solution is to close the installation program after upgrading SQL Server. Then, re-run the installation program and select Installation->New SQL Server stand-alone installation or add features to an existing installation. When you are prompted for a new install or add features then select your current instance. Finally, on the feature tree you can select the features to add including the Management Studio.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

sql server

IE Modal Dialog - Debug Y/N Rant

by zack.moore November 20, 2008 14:24

I first wrote this up as a post on the ASP.NET Client Side Web Development Forum when I realized that my blog is badly neglected and I ought to be writing stuff there. You can find my forum post here to see if anybody responded over there.

Yesterday I was reading the Visual Web Developer Blog. They have a really good article on JavaScript Intellisence in Visual Studio. http://blogs.msdn.com/webdevtools/archive/2008/11/18/jscript-intellisense-faq.aspx

In the article and comments the author was really encouraging users to post their issues with the technology because they really did use it to improve the products.

That request got me to thinking about the different little issues that I run into as a developer and I was reminded of something that I deal with countless times every day. I started writing it up as a comment in that blog post but I stopped because it was a little off topic and I didn't want to pollute the blog comments with off topic stuff.

So I decided to write up my little rant here .

I don't know how many other people this affects but the following issue affects me every day. In order to be able to debug JavaScript in web pages, I leave my browser in "debug" mode all the time because it is a pain to switch it on and off. This allows me to quickly jump into some JavaScript if I find a problem. However, the consequence is that now every page I browse to with IE pops up that little dialog asking me if I want to debug a script error on the page. It is amazing to me how many pages have so many script errors (at least in IE). I also think this causes a lot of my IE crashes especially when I get 2 or more dialogs in deadlock with each other on different tabs. Does anyone else get this?

Every time I go to cnn.com I know before I even type in the address that I'm going to get at least 2 Debug popups.

Something that compounds the problem for me is that I'm a "Right Click -> Open in New Tab" browser. As I read, if I encounter a link that I'm interested in I open it in a new tab and keep reading. After I finish the page I'm on, I go to the next tab and start reading. But this causes conflicts and lockups when multiple tabs popup Modal Debug Dialogs and I think that leads to a lot of the crashes that I get.

But it is annoying even if you don't browse that way. This problem is at its worst if you even encounter a page that causes a LOT of JavaScript errors or if you browse to a page that triggers an error when your mouse hovers over an element that you have to cross in order to get your mouse out of the browser window so you can close it. Every time you try to get the mouse out you trigger a modal dialog. After you close it, you try to get out again and re-trigger the modal dialog.

So how do other people deal with this? Is there a trick that I don't know that would make this more bearable?

How about the IE team? Could that change how this works so that it wouldn't cause so many problems and halts when I'm working? I would love to see a way to turn debugging on and off easier. I would also love to see the modal dialog removed and replaced with something else like a little error icon or something. The work on IE 8 has been awesome so far. the new debug and dev tools are pretty neat. How about making it easier for web developers to browse the web normally when not debugging?

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

JavaScript | IE

MS Office 2007 "Document Information Panel" Problems

by zack.moore October 15, 2008 11:09

I've gotten behind on my posts here but this is worth recording. I don't know how many other people were having this problem but when I first searched for a solution I couldn't find one.

The problem I was having was that when I would try to view a document's Properties in Word or Excel 2007 I would get the error "Document information panel was unable to load".

It turns out that the problem is a missing DLL in one of Office's folders.

Thanks to Roman Hnatiuk for finding the solution. You can can read his fix here.

I also want to thank William Boyer for pointing me in the right direction when I couldn't find the answer.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

MS Office

Digital Picture Frames infected with a computer virus

by zack.moore January 23, 2008 11:43

This is great! For Christmas, my wife and I bought digital picture frames for my parents and her parents. We looked at all the different ones and picked one from Insignia because it was a good trade-off between price, features, and picture size.

Well, wouldn't you know it I found out today that a few of the very model that we bought were infected with a computer virus.

You can read Insignia's announcement here.

The article states that the virus was an older virus that is easily detected and deleted by current anti-virus software. At Christmas, I loaded some photos onto each of the frames before we wrapped them up and I didn't detect a virus when the frame was connected our computer, so I feel pretty safe that the ones we bought were ok. But it really sucks that I now have to tell my dad and my wife's parents to watch our for their "nice" new Christmas gifts.

The picture frames were purchased at Best Buy. Its not Best Buy's fault that they were defective, however I do think that Best Buy should have moved quickly to alert their customers. I would have respected that. I have read that there is an announcement from Best Buy but I have yet to see or hear it and so I am disappointed in Best Buy.

I first heard about this when my wife saw an article appear on The Red Tape Chronicles on MSNBC.com. You can read that article here.

Insignia is the one who should really be ashamed. They are the ones who manufactured this product. I am glad to see that they have this article up on the front of their web page, but how many people saw that? I don't make a habit of visiting Insignia's web page. They should have done more to prevent this and done more to alert their customers. Is Insignia going to be liable to any of their customers who lost data or had their lives disrupted by having to deal with a computer disaster like loosing data or worse spreading across a network or hurting someone's reputation by sending out spam emails?

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

New Address and Missing Images

by zack.moore January 22, 2008 12:20

Good News, Zack's Fiasco now has a new URL: http://Blog.ZacksFiasco.com/ !

If you have anything that links to the old address http://ZacksFiasco.blogspot.com/ it should continue to work. However it might be a good idea to go ahead and update your links. If I ever move off of Blogspot then the old URL will stop working.

Now the bad news. Because of the URL change, Blogspot has stopped serving up some of my screenshots that I have in my articles. I am currently researching a solution and will fix it ASAP. In the meantime, you should still be able to read the articles and I apologize for the problem with the screenshots.

Screenshots are working again. I just had to upload them to a new location and repost all my old articles.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Build a complete Stored Procedure based Data Access Layer using Code Generation - Part 2

by zack.moore January 20, 2008 03:08

All code published in this article is published under the Microsoft Public License. See source code download for a copy of the license.

Copyright 2008 Zack Moore

Code can be downloaded from here.

During the course of writing the next part in this series, I made some changes to the scripts. I fixed some bugs in how computed columns were handled, cleaned up some inefficient code, added a lot more comments, and I changed the Insert and Update procedures to make better use of the SQL Server 2005 OUTPUT clause in returning computed and identity columns. This leads into a good discussion of the structure of MyGeneration scripts, how to change them, and how to version them.

Before I begin discussion of the code there are some things you need to know about MyGeneration scripts. Each script is identified by a Unique ID which is a GUID. If you edit a script that you did not create, then it is a good idea to create a new id for that script. Otherwise it could be overwritten or confused with the original script. It is also a good idea to update the title to reflect that it is a modified version of the original.

Basic CRUD properties

This also requires you to consider what to do when updating your own script. What do you do if you make a change or fix a bug in your own script? Should you create a new id or keep the old one? One option would be to create a new id every time you edit a script, but that is tedious and would also clutter the namespace with many versions of the same script.

For my scripts, if the change is small and does not cause a breaking change in the interface then I keep the same id and I add a version to the Title and update the minor version number. If the change is major, then I create a new id and update the Title of the script to include a version number and update the major or minor version number.

In this example, I'm going to modify the SQL that the stored procedures produce, so I'm only going to update the minor version number and keep the same id.

Basic CRUD properties (updated)

As you can see from the script properties, the CRUD script is written in JScript. This script has the following basic format.

var fkProcList = new Array();

for (var i = 0; i < tablenames.Count; i++)
{
    // Loop through all the columns of the table 
    for (var j = 0; j < tableMeta.Columns.Count; j++) 
    {

    }

%>
Stored procedures
<%

    for(var x = 0; x < tableMeta.ForeignKeys.Count; x++)
    {
%>
SelectByFK procedures
<%
    }

    // Save this set of procedures to disk
    output.save(filename, false);
    buffer += output.text;
    output.clear();
}

output.write(buffer);

There is a lot left out of this skeleton, but essentially the script loops through each table and processes each column. The script then creates the Select, Select All, Insert, Update, and Delete procedures. Then the script examines the foreign keys and creates the SelectBy Foreign key procedures. Then all of the TSQL is written to a file and the process starts over with the next table.

The CRUD script v1.0 produces the following TSQL for INSERT and UPDATE.

-- Proc: Table1Insert
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="Table1" commandType="Insert"/>
CREATE PROCEDURE [api].[Table1Insert]
(
    @Table1Id uniqueidentifier,
    @Col1 varchar(50),
    @Col2 int = NULL,
    @rowversion timestamp OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    

    INSERT INTO [dbo].[Table1]
    (
        [Table1Id],
        [Col1],
        [Col2]
    )
    VALUES
    (
        @Table1Id,
        @Col1,
        @Col2
    )

    SELECT         @rowversion = [rowversion]
    FROM [dbo].[Table1]
    WHERE         [Table1Id] = @Table1Id;

    RETURN @@Error
END
GO

-- Proc: Table1Update
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="Table1" commandType="Update"/>
CREATE PROCEDURE [api].[Table1Update]
(
    @Table1Id uniqueidentifier,
    @Col1 varchar(50),
    @Col2 int = NULL,
    @rowversion timestamp OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @t TABLE(x int);    

    
    UPDATE [dbo].[Table1]
    SET
        [Table1Id] = @Table1Id,
        [Col1] = @Col1,
        [Col2] = @Col2
    OUTPUT 1 into @t(x)
    WHERE 
        [Table1Id] = @Table1Id AND
        [rowversion] = @rowversion

    IF(SELECT COUNT(*) FROM @t) = 0
    BEGIN
        RAISERROR('Concurrency Error',16,1)
    END

    SELECT         @rowversion = [rowversion]
    FROM [dbo].[Table1]
    WHERE         [Table1Id] = @Table1Id;


    RETURN @@Error
END
GO

These procedures add and update records to the table and then run a second query to retrieve the rowversion which is returned as a OUTPUT parameter. The purpose of this is to allow an application to continue to use the data they have and if necessary perform an update. If the rowversion wasn't returned, then the application would have to query the entire record again in order to perform an update.

I would like to modify the code generation script so that it produces INSERT procedures that don't need to run a separate query to return the rowversion.

Using a text editor, I edited the above TSQL until it looked how I think I want it. After testing the new procedures, we end up with the following:

-- Proc: Table1Insert
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="Table1" commandType="Insert"/>
CREATE PROCEDURE [api].[Table1Insert]
(
    @Table1Id uniqueidentifier,
    @Col1 varchar(50),
    @Col2 int = NULL,
    @rowversion timestamp = NULL OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    
    DECLARE @t TABLE
    (
        [rowversion] binary(8)
    );

    INSERT INTO [dbo].[Table1]
    (
        [Table1Id],
        [Col1],
        [Col2]
    )
    OUTPUT 
        rowversion
        INTO @t
        (
            [rowversion]
        )
    VALUES
    (
        @Table1Id,
        @Col1,
        @Col2
    )

    SELECT
        @rowversion = [rowversion]
    FROM @t

    RETURN @@Error
END
GO

-- Proc: Table1Update
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="Table1" commandType="Update"/>
CREATE PROCEDURE [api].[Table1Update]
(
    @Table1Id uniqueidentifier,
    @Col1 varchar(50),
    @Col2 int = NULL,
    @rowversion timestamp OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @t TABLE
    (
        [rowversion] binary(8)
    );
    
    UPDATE [dbo].[Table1]
    SET
        [Table1Id] = @Table1Id,
        [Col1] = @Col1,
        [Col2] = @Col2
    OUTPUT
        [rowversion]
        into @t
        (
            [rowversion]
        )
    WHERE 
        [Table1Id] = @Table1Id AND
        [rowversion] = @rowversion

    IF(SELECT COUNT(*) FROM @t) = 0
    BEGIN
        RAISERROR('Concurrency Error',16,1)
    END

    SELECT
        @rowversion = [rowversion]
    FROM @t
    WHERE
        [Table1Id] = @Table1Id;

    RETURN @@Error
END
GO

 

The updated procedures use the new OUTPUT clause to return the rowversion to a table variable. We can then query the table variable for the rowversion and return it in the OUTPUT parameter. The query against the table variable should be much faster than a query against the main table since the table variable has fewer records and is in memory.

In order to produce this new TSQL we need to modify our code generation script.

When the script loops over each column, it builds the pieces it needs to build the the procedures. The variable insertParams contains the string of comma seperated parameters of the Insert procedure. The variable insertFields is the list of columns to be inserted by the insert statement. The variable insertValues maps the insert parameters to the columns.

The section of code that produces the INSERT procedure looks like the following:

-- Proc: <%= insertProcName %>
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="<%=tablename%>" commandType="Insert"/>
CREATE PROCEDURE [<%=mySchema%>].[<%= insertProcName %>]
(
<%= insertParams %>
)
AS
BEGIN
    SET NOCOUNT ON
    <% if(updatedOn == true) { %>
    set @UpdatedOn = getdate();
    <%}%>

    INSERT INTO <%= tablenameFull %>
    (
<%= insertFields %>
    )
    VALUES
    (
<%= insertValues %>
    )
<%= (insertAutoKeyCode == "" ? "" : "\r\n" + insertAutoKeyCode) %><%

if (hasComputedFields) 
{
    insertComputedCode += "\r\n\tFROM " + tablenameFull + "\r\n";
    insertComputedCode += "\tWHERE " + deleteWhere + ";\r\n";
}

%>
<%=insertComputedCode%>
    RETURN @@Error
END
GO

The deleteWhere variable may stand out as being out of place. This variable contains the code for the delete WHERE clause, but some of the other procedures require exactly the same code so it gets reused in several places.

One of the things to notice is the computed fields section. In the current script, this is where the timestamp field value is retrieved since technically a timestamp is computed automatically. So when we change the code that returns the timestamp columns, this also needs to work for other computed columns.

In order to retrieve the computed columns we need to create our table variable, specify our OUTPUT clause, copy the values from the table variable into the OUTPUT parameters. This is accomplished by using these three variables.

var insertComputedTableVar = "";    // generated TSQL to hold computed values
var insertComputedCode = "";    // generated TSQL to retrieve Computed values
var insertComputedReturn = "";    // code to return values into output parameters

You can see the first variable used in the script above, but we are going to modify the values it holds. The second two variables are new. In addition, we are getting rid of insertAutoKeyCode. AutoKeys are what MyGeneration calls like identity columns and in our modification they will be handled by the computed column code.

The code below is executed inside a loop over each table's columns and populates our three variables. It uses a simple  pattern to append a comma and new line if there is more than one computed value.

// generate code to retrieve computed values on insert
if (column.IsComputed || column.IsAutoKey)
{
    hasComputedFields = true;
        
    // build the list of values to go in the
    // OUTPUT clause
    if (insertComputedCode != "") 
    {
        insertComputedCode += ", \r\n";
    }
    
    insertComputedCode += "\t\tINSERTED.[" + column.Name + "]";
    
    // build the list of columns for the table variable
    // to hold the OUTPUT values
    if(insertComputedTableVar != "")
    {
        insertComputedTableVar += ", \r\n";
    }
    
    if(column.DataTypeName == "timestamp")
    {
        insertComputedTableVar += "\t\t[" + column.Name + "] binary(8)";
    }
    else
    {
        insertComputedTableVar += "\t\t[" + column.Name + "] " + column.DataTypeNameComplete;
    }
    
    // build a list of select columns to return 
    // output parameters
    if(insertComputedReturn != "")
    {
        insertComputedReturn += ", \r\n";
    }
    
    insertComputedReturn += "\t\t@" + paramName + " = [" + column.Name + "]";
}

Take note that this code contains a special case for timestamp in the table variable code that it generates. The table variable is used to store values that get spit out by the insert statement when we use an OUTPUT clause. So we declare a column in the table variable for each value we want to output and we create each column in the temp table to be the same data type as they are in the real table. The only problem is if we declare a timestamp column on our temp table, it wants to behave the way timestamp columns always behave and it will forbid you form trying to insert a value into it. The solution is to use a binary(8) column to store the timestamp and all is well.

With this new code we can update our procedure generation script to the following.

-- Proc: <%= insertProcName %>
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="<%=tablename%>" commandType="Insert"/>
CREATE PROCEDURE [<%=mySchema%>].[<%= insertProcName %>]
(
<%= insertParams %>
)
AS
BEGIN
    SET NOCOUNT ON
    <% if(hasComputedFields) { %>
    declare @t table
    (
<%=insertComputedTableVar%>
    )
    <% 
    }
    if(updatedOn == true) { %>
    set @UpdatedOn = getdate();
    <%
    }%>

    INSERT INTO <%= tablenameFull %>
    (
<%= insertFields %>
    )<% 
    if(hasComputedFields) { %>
    OUTPUT
<%=insertComputedCode%>
    INTO @t<%
    }%>
    VALUES
    (
<%= insertValues %>
    )
    <%if(hasComputedFields) { %>
    SELECT
<%=insertComputedReturn%>    
    FROM @t
    <%}%>
    RETURN @@Error
END
GO

The same process is repeated for update and then all we have to do is regenerate our stored procedures.

The final TSQL looks like this.

-- Proc: Table1Insert
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="Table1" commandType="Insert"/>
CREATE PROCEDURE [api].[Table1Insert]
(
    @Table1Id uniqueidentifier,
    @Col1 varchar(50),
    @Col2 int = NULL,
    @rowversion timestamp = NULL OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    
    declare @t table
    (
        [rowversion] binary(8)
    )
    

    INSERT INTO [dbo].[Table1]
    (
        [Table1Id],
        [Col1],
        [Col2]
    )
    OUTPUT
        INSERTED.[rowversion]
    INTO @t
    VALUES
    (
        @Table1Id,
        @Col1,
        @Col2
    )
    
    SELECT
        @rowversion = [rowversion]    
    FROM @t
    
    RETURN @@Error
END
GO

-- Proc: Table1Update
-- This proc was created by script. You can edit it, but if you do
-- then DO NOT regenerate it from the script or you will loose your edits.
-- <MetaData entityName="Table1" commandType="Update"/>
CREATE PROCEDURE [api].[Table1Update]
(
    @Table1Id uniqueidentifier,
    @Col1 varchar(50),
    @Col2 int,
    @rowversion timestamp = NULL OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    
    declare @t table
    (
        [rowversion] binary(8)
    )
    
    UPDATE [dbo].[Table1]
    SET
        [Table1Id] = @Table1Id,
        [Col1] = @Col1,
        [Col2] = @Col2
    OUTPUT
        INSERTED.[rowversion]
    INTO @t
    WHERE 
        [Table1Id] = @Table1Id AND
        [rowversion] = @rowversion

    IF(SELECT COUNT(*) FROM @t) = 0
    BEGIN
        RAISERROR('Concurrency Error',16,1)
    END

    SELECT
        @rowversion = [rowversion]    
    FROM @t
    RETURN @@Error
END
GO

The updated code generation scripts are posted as Release 1.2 on CodePlex.

Give this a try in your own databases.

kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

ADO.NET | sql server | Zack's Fiasco DAL | TSQL | databinding | codegeneration | ORM

Bill Gates' Last Day Video

by zack.moore January 16, 2008 15:44

I just saw this and it was hilarious.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

funny

DAL scripts were updated

by zack.moore December 10, 2007 00:28

svnbridge keeps crashing so I wasn't able to updated the code on Codeplex Source Code section hasn't been updated, but I posted a new release which is the templates in source code form.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

ADO.NET | sql server | TSQL | databinding | codegeneration

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen | Modified by Mooglegiant


I've been doing software development since I was little and my dad first brough home an ATARI 800. I picked up PILOT and BASIC. Now I mostly write C# and ASP.NET, and about a dozen other languages and platforms. I also enjoy a bunch of outdoor sports including running and mountain biking. I am very happily married. I am currently working for a great Software and IT consulting company named SPINEN where I am a Senior Developer.


Copyright Zack Moore

TextBox