I'm moving some duplicate code from a codebehind file to a class file so I can reuse it. I'm tired and it's a bit late for my best programming so I'm trying to remember is the c# return type also null now. Or maybe no return type needs to be specified if a variable isn't returned (is that vb?). If null works in SQL and it works an an object value to compare against in c#, why can't null be a valid return type instead of void. What does void really say under the covers that is so different from null. Isn't void a carryover from c and c++. Perhaps it's older than that. Why do I need to remember yet another keyword that has no value. No, that's not a pun. I have too many numbers and words in my head that have to be pulled out at a moment's notice. Why add one more? Get rid of void. Just rip it right out.
Wednesday, April 23, 2008
Tuesday, April 22, 2008
Exception Handling for NetTiers
I'm still cleaning up code. One of the things that has never worked in NetTiers is the Microsoft Enterprise Library Exception Handling. I would get an exception thrown in the NetTiers code and the line was an exception with no data. I would get the error on a SQL insert via the NetTiers layers but no information about what was wrong with the data. Since I'm using the Insert where the param is the base type such as
userService.Insert(newUser);
I knew that datatyping from a C#/.Net perspective wasn't the problem. I knew it was at a SQL level but I didn't know what it could be. So I finally had to fix the .NetTiers ExceptionHandling. I'm not sure why doesn't work right off the bat like the rest of NetTiers but I also didn't care enough to investigate. Once I get the ExceptionHandling working, I don't ever touch so I haven't had to REALLY understand it.
But now it has to work, it's hard to move forward without inserting data on this particular table. I knew I didn't have anything in web.config for this so that's where I started. I opened the ExceptionHandling Basic Quickstart and looked at the app.config. There were two different sections I added and then the true SQL error was displayed. While this isn't the entire work I'll have to do with Exception Handling on my pet project, at least I can keep going.
The SQL problem turned out to be that a datetime value was not being passed for a column where NULL was not allowed. Once the error handling showed the problem of datetime out of range, and I looked at the column definitions, I realized the problem.
Here are the sections I added to web.config:
1) in the <configSections>
section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" /><
2) the last entry before the end <Configuration> tag:
exceptionHandling><
<
exceptionPolicies><
add name="Global Policy"><
exceptionTypes><
add name="Exception" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="None"><
exceptionHandlers><!--
<add name="Wrap Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" exceptionMessage="Global Message." wrapExceptionType="ExceptionHandlingQuickStart.BusinessLayer.BusinessLayerException, ExceptionHandlingQuickStart.BusinessLayer" />--><
add name="Custom Handler" type="ExceptionHandlingQuickStart.AppMessageExceptionHandler, ExceptionHandlingBasicQuickStart"/></
exceptionHandlers></
add></
exceptionTypes></
add><
add name="Handle and Resume Policy"><
exceptionTypes><
add name="Exception" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="None"><
exceptionHandlers /></
add></
exceptionTypes></
add><
add name="Propagate Policy"><
exceptionTypes><
add name="Exception" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="NotifyRethrow"><
exceptionHandlers /></
add></
exceptionTypes></
add><
add name="Replace Policy"><
exceptionTypes><
add name="SecurityException" type="System.Security.SecurityException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="ThrowNewException"><
exceptionHandlers><
add name="Replace Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" exceptionMessage="Replaced Exception: User is not authorized to peform the requested action." replaceExceptionType="System.ApplicationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /></
exceptionHandlers></
add></
exceptionTypes></
add><
add name="Wrap Policy"><
exceptionTypes><
add name="DBConcurrencyException" type="System.Data.DBConcurrencyException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="ThrowNewException"><
exceptionHandlers><
add name="Wrap Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" exceptionMessage="Wrapped Exception: A recoverable error occurred while attempting to access the database." wrapExceptionType="ExceptionHandlingQuickStart.BusinessLayer.BusinessLayerException, ExceptionHandlingQuickStart.BusinessLayer" /></
exceptionHandlers></
add></
exceptionTypes></
add></
exceptionPolicies></
exceptionHandling>
Sunday, April 20, 2008
Get the mop out: code cleanup
Now that I have 90% of the v1 features for my pet project web site decided, and have a bullet-proof process for new tables, custom procs and other stuff NetTiers handles, I'm cleaning up the code. I'm ripping out any code I wrote to go against the SQL server that NetTiers handles for me. I'm cleaning up all gridview and codebehind to be consistent with naming and usage. I'm removing files that were tests to see how I wanted something to work. I'm removing links and cleaning up the masters.
Wayne suggested I change a feature from random choice to numeric order choice so that a progression was predectible. I've been fighting it but he finally gave me a business logic reason. Ok, so now I'm changing SQL table column data types. That dominos into all sorts of changes.
Once all this gets back to working, I'll start on the visual design. I haven't written themes and skins so I'm going to try to grab a working set that is close to my overall design. I'll implement it then dink with it till I get the look I can live with short term. Them I'll hand off the theme/skin to a graphic web designer. I don't mind paying for this because I definitely see the value.
Do you have a graphic designer you like?
Saturday, April 19, 2008
New Code to AJAX
My pet project is a new website. I'm working on the search page which searches through several NetTiers Services in the Component layer. It took me a few hours to figure out exactly what I wanted to do here. Do I user an Object Data Source? User StringBuilder to return an HTML table? What should the gridview link to once the search has been performed?
Then I thought, I know I'm just going to AJAX this code. Am I just wasting my time? I just want to get v1 out the door. AJAX can wait to v2 but then I think none of this code would be used via AJAX except the NetTiers stuff.
Do you design new websites via AJAX right off the bat? I haven't done enough AJAX to be able to think AJAX as a first pass on the design.
So You Want Your Own Business Website (SYWYOW): Comparing apples to apples
Wayne and I are remodeling our kitchen. In the process, we are shopping for cabinets. We want quality cabinets in the look that goes with our craftsman home. The high-end price is twice as much as the next quote down in dollar value. While we aren't comparing apples to apples, we are trying to. I keep comparing our kitchen remodel and new-construction backporch to a web site. I know nothing about cabinets and most web site clients know nothing about quality web sites. One of the cabinet salesman said the cabinets were guaranteed for life. No software manufacturer would ever say that. Some pointed questions reveled that only certain things were guaranteed.
What should a non-technical client expect from your final product? They won't ask all the questions you know they should. So lets work on some quality questions:
1) What error detection, communication, and recovery is provided? No one expects it to break so they won't ask this unless they have been burned by this.
2) What, if any, part of the code do they own and can literally walk away with? Are you letting them use your code base you already developed for a fee with a few new custumizations or is this new work written 100% by the web company? Same question for any graphics, images, code libraries, web services, etc?
3) What availability and time frame will you provide for fixes? Is this a scaled situation where only mission-critical bugs are given 24/7 coverage? Or are you tossing the web site over the fence and walking away?
4) What testing will be provided? Who does it and how do they determine the web site is of a certain quality bar? How is the quality managed internally? How is it communicated externally back to the client?
We all want to think we have a certain standard for code quality but how do we communcate that? How do we stand behind it? How do we demonstrate our code meets our own standards? You can song and dance your way around this so the client buys any thing you say but at the end of the day you still know if you meet your own standards. But how?
Friday, April 18, 2008
Codaholic by night
If you have a full-time job programming, you might do something else in your spare time. However, I don't have that situation. I get my fill from 7pm to 10pm at night due to my current situation. I left a non-programming meeting last night and rushed home to start up VS and keep going on my project. I was thrilled to still have a couple of hours left before I had to stop. Then hubby starts watching HULU.com. Arghh! We sit next to each other in our office so this was not as conducive to programming as say...a jackhammer might be. Tonight I'm in the other room while he hulu's on.
Monday, April 14, 2008
NetTiers Find method
NetTiers will build methods for normal CRUD as well as anything they can determine. For example, if you have a table with 1PK and 2FKs, the template with build a method for select where the IN params are the two FKs. Great! I probably need that. But things get a little weird when I need to search. For example, I have a user's table and I want to be able to return results for all users that are like a searchterm. How about all user's like Dina. In T-SQL, I usually would have a where clause " UserName like '%@UserName%'. Works for me.
NetTiers has a Find method for each table. When creating a find proc, it's hard to determine which columns you would want to search through. NetTiers discovery does a bit of assuming that the first column (after the PK/FK columns) is THE main data column (such as a name). But that takes some forethought in terms of table creation. The NetTiers find method comes in two varieties: one with sql params in the NetTiers format and one with a SQL where clause. The NetTiers template should let you turn off this second way but it doesn't. Inside the stored procs that NetTiers creates, they put the where clause together. This makes me a bit nervous. I don't want a user entering something in a search textbox and have it passed to build a SQL query on the fly.
I'm going to write my own custom search proc sing the NetTiers naming conventions so I get a custom Find Method from NetTiers. I know what columns I want to search through on the Table but I also want to get my results in two line of c# code via NetTiers layer.
Sunday, April 13, 2008
So You Want Your Own Business Website (SYWYOW): The first custom process
So now you have a business website and you control the content. It provides enough functionality that 70% of your needs are covered. However, you have a custom process that needs to be available on your website. This can be a common function that your Site Builder provider did not provide with your packaged website or it can be something specific to your business. How do you make this available to your customer base from your website?
A few assumptions:
- You purchased your domain name, such as MyBooksForSale.com.
- You can describe in detail your custom process. Common web functionality such as a shopping cart does not need to be designed. Good examples of a custom processes are: pricing models, data exchange (possibly in files such as blueprints or accounting spreadsheets).
- You have the time and money to maintain the available of this process to your customers on your website. Once the process is made available, you can fulfill the end result if there is one.
So what do you do now?
Local Rules. Bellingham is a small town (50K-70K population) and the techie group here is small. Word of bad work or work left undone gets around. Find a local web site that you like the looks and feel of, then approach the owner and quiz them.
Define a design budget for this process. Is it worth $10,000 or $100,000 to your business? Don't let a Web Design firm set the budget. If they want $100,000 for the process when you want to spend $10,000, one of several things could be happening:
- your expectations are not realistic
- their design is bloated
- your are not communicating clearly
Let's take Amazon as an example for a design: making book purchases available on the web. A small design would be to choose a book, and communicate the interest to purchase the book. The budget design allows for a list of books with the ability to select and send contact info. Your business could call the customer back for the actual purchase and fulfillment. That puts a lot of work on you as the business owner but it is within your budget. Then there is amazon who has every possible feature associated with book purchasing including one-click. Their site cost alot more than $100,00.
Define a maintenance budget for this process. Just like your existing web site, this new feature will have to live somewhere which will not be free. My current thought on joining an existing site from one of the Site Builder's to a new process is to use your domain name. For example, your existing web site was probably www.MyBooksForSale.com but your new process will live on a new location such as repairs.MyBooksForSale.com. There is some technical stuff to make this work but it does work. You can still have and manage your main web site inexpensively and then add a custom process. Any web design firm should be able to help you make this work.
Define a time frame for this process. The web design firm should be able to give you dates for you review of the work as it progresses. Each meeting should be an opportunity to refine the design and fix anything that isn't working for you. However, most design firms will have a standard process to stop "feature creep" which means you want more than you originally communicated and you think this new feature should be covered in the original bid for the work.
Courting and the Marriage. You will probably be working with your web design firm on a regular basis for a while, make sure you like them. That means they understand what you want and you understand what they can do. You communicate well together and progress toward your goals is made in a way that you enjoy. The flip side of the coin is that you pay your bill to them on time which means as soon as you get it.
The WOW factor. Every web design firm will have features, techniques, technologies, or business partners that they think you can't live without. They love them and want you to love them too. They want to WOW you with them and then you'll want them too. You will feel over your head in an area you are unfamiliar with and you will want to say yes just to get to your goal. Keep focused on the goal of your business process on the web and make the WOW thing something that you'll consider after your initial process it up. Of course, they'll say it can't wait, it won't work without it. Get a second opinion from a professional.
Test.Test.Test. Your process is ready to go and you just want it available to your customers. Stop and test your process. Ask you family, friends, employees to test it for you. Do not let your customers see this process unless you are sure it is ready. Don't expect the web design company to test it to your satisfaction because you know your business, they know how to build web sites. This is the last check for any communicate errors.
Evaluate the benefit of the process. After the process is live on your website and your customers are using it, find out a few things:
- How do your customers like it?
- How would they improve it?
- How much more/different/better business is it bringing you or time/expense is it saving you?
- Would you have do it again? This process? This design firm? This way?
Saturday, April 12, 2008
.Net SiteMap using Sql Provider - change to handle role trimming
My site has two roles: Admin, SecureUser, and three types of content:Admin, SecureUser, Public. I found that the code in the last post from Jeff Prosise wasn't managing roles in the SiteMap. For a visitor to the site that has not logged on (anon), the site map improperly showed Admin and Public pages. I checked the web.config and the SQL statement so it had to be either the database caching or the sql provider for sitemaps.
I google'd and found several posts in 2006 about this but none since so I knew it had to have been resolved. I found this blog post by Ishai Hachlili that had the code to deal with roles correctly. I added the code and the problem went away.
I'm not sure the code is robust enough to handle a large site but it fixed the problem for now.
As an aside, several of the 2006 posts said to just fix the web.config by making sure the <location> tags were correct. This meant any user could see links for the site that were restricted but would be denied access once they actually clicked on the page. The location tags are only one solution to the problem. You need to have the sitemap, roles and the <location> tag to solve the problem. I go one step further and have the master page (1 for each area of the site) check the user and deny access.
Tuesday, April 8, 2008
.Net SiteMap using Sql Provider
I stared working on my sitemap tonight. Never done the SiteMap stuff before. Opened my Pro ASP.NET 2.0 in C# 2005 that I use as a jump off point for intro topics I haven't touched yet. The chapter is great but I like my data in the database which gets backed up every night. I understood enough and even got a test page up using nested site map files. Hurray!
Now on to the SQL provider. I knew there had to be one so I googled and found an article with code by Jeff Prosise. The article covers the .cs file that is the SQL provider, as well as the web.config changes, the T-SQL, and the aspnet_regsql commands.
After you get the provider up and working, don't expect the SQL provider to show up in the Data Source Configuration Wizard:
You'll have the change the SiteMapDataSource to specify the Provider that you added to the web.config for SQL:
asp:SiteMapDataSource ID="TotalSiteMap" runat="server" SiteMapProvider="AspNetSqlSiteMapProvider" /><
But now my data is in the database and I changed the proc to be NetTiers-compliant so I can use their Admin pages for my data management.
Monday, April 7, 2008
OOP: Properties and UserProperties
The Problem
I want to show a page with all the user-settable properties. Any properties that the user hasn't set should be shown but the value should be empty or null or some nice text indicating such. However, what is the BEST way to design this?
The Tables
I have a [Properties] table. I have a [UserProperties] table that has a FK back to [Properties]. There is only a row in the [UserProperties] table is the user has set one. If not set, no row. So back to what I want to show: in the user's account area, a page of all properties where any properties that have been set show that value, otherwise some text indicating the property has not been set. I'm going to go through every option to go from DB design to .Net code for the page that Wayne and I discussed on this fine rainy, sunny day.
All SQL
Most obvious from a database perspective: T-SQL join (1 db call) that includes all properties so depending on how it is written, right or left join. The problems with this are that the NetTiers layer is table-based. So I'm returning the properties for the user, but if the [UserProperties] table doesn't have the record, then NetTiers can't make an Entity object for that record because it requires the [UserProperties] PK for creation.
All .Net
Most obvious from a .aspx perspective: N+1 db calls. First page is a list of all properties (1 DB call), with each row using the userproperties entity for that property (1 call for each property). Any property that doesn't have a valid object in the userproperties shows text saying the property is not set. I hate this approach. This means both SQL and .Net have to chug through unnecessary work. Yuk. Yuk. Yuk. However, I can write it quickly. Hmm, write it quickly but spend eternity hating what I've done. Or scratch my head some more.
Other Answers
Less obvious: Work with NetTiers to have a custom proc and custom c# code to get this working. This should be easy. I know the proc code and I know where to tie into NetTiers. However, I still have an entity without a PK. Hmm, I'll have to think some more.
Less obvious: I'm using NetTiers as a stand-alone Library so how about sub/super class (depending on your perspective). Add a custom User class to my library that does what I need to do with a Properties object and a UserProperties object. I don't see how this reduces the db calls.
So far, I don't like any of these but I have a low-traffic site so I'm going to opt for the All .Net answer until I come up with a much better answer. Perhaps the table design is the problem.
Any thoughts? Suggestions?
Friday, April 4, 2008
Web.Config is big, bloated, ugly and unorganized
The days when I don't have to deal with web.config are great. The days I have to go in there are bad, bad, bad. Why can't web.config be easy? Why can't it tell me more, and complain less. Why can't it be easily organized in some sort of logical pattern instead of loosey goosey? The registry looks great compared to web.config.
Is this fixed in the latest VS or .Net?
Code Cleanup unveals bugs
I love it when I'm just cleaning up some code after some major changes and I discover code that has been sitting there a while that would never have worked. I don't even have to run/test it. I can read it and know it's broken. So is that a win for finding a bug and fixing it before it showed up or is that WTF? for having such an obvious bug in the first place.
Wednesday, April 2, 2008
Fuse Extension Method
Previously, a few of us held a discussion about the best way to concatinate a number of string array elements into a single string. You can find details here. Last week, I needed something a bit more general purpose. Came up with an extension method on IEnumerable that will accomplish this for any type.
public static string Fuse<TSource>(this IEnumerable<TSource> source, string separator)
{
if (source is string[])
{
return string.Join(separator, source as string[]);
}
return source.Aggregate(new StringBuilder(),
(sb, n) => sb.Length == 0 ? sb.Append(n) : sb.Append(separator).Append(n),
sb => sb.ToString());
}
The method is optimized to use the string.Join method when the underlying type is a string. Join uses unsafe code and is very quick. Here are a few example uses:
string[] names = new string[] { "Andy", "Wayne", "Dina", };
string allNames = names.Fuse(", "); // "Andy, Wayne, Dina"
int[] numbers = new int[] { 1, 2, 3, 4, 5, };
string allNumbers = numbers.Fuse(":"); // "1:2:3:4:5"
object[] objects = new object[] { 1, "ten", 5.5, true, false, };
string allObjects = objects.Fuse("/"); // "1/ten/5.5/True/False"
Tuesday, April 1, 2008
T-SQL Drop Stored Procs - CodeSmith version
And finally Wayne's Codesmith version for all procs prefaced with 'aspnet_':
<%@ CodeTemplate Language="C#" TargetLanguage="C#" %>
<%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Assembly Name="System.Data" %>
<%@ Assembly Name="System.Design" %>
<%@ Import Namespace="SchemaExplorer" %>
<%@ Import Namespace="System.Data" %>
USE [<%=SourceDatabase.Name%>]GO
<%
foreach(CommandSchema command in SourceDatabase.Commands)
{
%>
<%
if ((command.Name.Length>7) && (command.Name.Substring(0,7) == "aspnet_"))
{
%>
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[<%=command.Name%>]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[<%=command.Name%>]GO
<%
}
}
%>