|
|
Browse by Tags
All Tags » Geek talk (RSS)
-
Executive summary: ValidationSummary controls look at the ErrorMessage field to figure out what to display, so always use ErrorMessage in a verbose enough way that it will be helpful from a ValidationSummary control. If you need a shorter message to display inline (i.e., where the validation control is on the form, as opposed to the ValidationSummary) use the body of the control to define it. In the past, I've used RequiredFieldValidator controls on my web forms to remind users that certain fields are required. I would set the ErrorMessage to something vanilla like, "This field is required", or even something simpler like "*" (an asterisk) if I didn't have much room on the form to display more prose for an error. A friend was recently testing a new feature that I'd built for our sales team and she had a hard time seeing the little red asterisks that were showing up next to required fields. It felt to her as though she was pushing the submit button on the form but nothing was happening. It was clear that a ValidationSummary control would be helpful, especially if placed close to the submit button for the form. I've been a bit lazy in the past about using ValidationSummary controls, partially because most of my forms are simple enough that they feel a bit redundant. But on a more complicated form, they can be very helpful to guide users back to the places on the form where there's problems. So I threw one of those puppies on the form and immediately saw that there was a problem - my error message was set to "*", which meant that my validation summary was pretty useless - it just displayed a bunch of red asterisks! And in places where I'd used the prose, "This field is required", well that was pretty useless as an error message in the summary. After a bit of research and experimentation, I discovered that the ValidationSummary control looks at the ErrorMessage property on each validation control in order to figure out what to display in the summary. So it's important to use ErrorMessage with a summary in mind! Don't use text like "*" or "This field is required". Be more specific so the user can find her way up to the problem field, as in, "PostalCode is required". But if you make ErrorMessage verbose so that it's helpful in a summary, it may make your form really ugly when displayed inline next to the control being validated. The trick is to use the body of Read More...
|
-
I just spent about 15 minutes debugging a problem where a document was getting unexpected nulls where empty strings should have been. Indeed controls like the TextBox have code in them that allows you to set the Text property to null and the TextBox will convert that into an empty string. So it's a bit counterintuitive that the declarative data source works the opposite way by default . When you use a declarative data source to perform a parameterized update that contains string parameters, consider setting ConvertEmptyStringToNull='false' on your <asp:Parameter> elements, because it's true by default ! In other words, if a text field contains an empty string, it'll be sent to your declarative data source not as string.Empty, but as null. Now I don't know about you, but I don't like dealing with nulls if I can avoid it. Especially strings. Unless there's a clear need to have a null state, I avoid them like the plague not only in my database designs but also in my XML schema designs. Hopefully this helps somebody out! Read More...
|
-
It's surprising that XmlDocument isn't marked [Serializable], because it's very natural to serialize one into a stream. I wanted to put an object into ASP.NET ViewState the other day, and quickly ran into this roadblock, because part of the object included an XmlDocument, which is not serializable. A quick search revealed that most people deal with this problem by storing a string instead. Indeed, that was where I started, but I quickly realized that there are multiple places in my code where I want to do this sort of thing, and I don't want to have to mess with it in each data structure that contains an XmlDocument. So I put together a simple class that holds an XmlDocument and implements ISerializable and called it SerializableXmlDocument. I'm sharing the source code here in the hopes that a) somebody will find it useful, and b) somebody smarter than I am will point out how I screwed it up and help me make it better. SerializableXmlDocument includes implicit conversion operators to make it easy to convert to/from an XmlDocument. It holds the actual document in a property called Value. This "isomorph" pattern is one that I picked up from Craig . While writing this code, I also wrote a helpful extension method for getting a byte array out of a MemoryStream that is exactly the length of the data written to the stream so far (CopyUpToSeekPointer). So don't go looking in the docs for MemoryStream for this method :) This is obviously not the most efficient way to consume bytes written to a MemoryStream since it copies the data into a new byte array, but it's very convenient in many scenarios. Here is SerializableXmlDocument.cs: using System; using System.Runtime.Serialization; using System.Xml; using System.IO; namespace Pluralsight.Samples { [Serializable] public class SerializableXmlDocument : ISerializable { public SerializableXmlDocument() { } public SerializableXmlDocument(XmlDocument value ) { this .Value = value ; } public XmlDocument Value { get; set; } #region ISerializable implementation public SerializableXmlDocument(SerializationInfo info, StreamingContext context) { byte [] serializedData = ( byte [])info.GetValue( "doc" , typeof ( byte [])); if ( null != serializedData) this .Value = Deserialize(serializedData); } public void GetObjectData(SerializationInfo info, StreamingContext context) { byte [] serializedData = null ; if ( null != Value) serializedData = Serialize(Value); info.AddValue( "doc" Read More...
|
-
Two way data binding in ASP.NET is easy, just use the Bind expression and data will flow between your web controls and your data source flawlessly. Until that is, you try to use a format string: Bind("AmountCharged", "{0:C}") While this displays just as you'd expect (e.g., $200), it doesn't do so well when you submit an edit that includes the same value ($200): Input string was not in a correct format. I searched around and didn't find much in the way of a clean solution, but I did solve the problem with just a few lines of code. The trick is to handle the data-bound control's Updating event. Since I was working with a GridView, my solution looked a bit like this: < asp:GridView DataSourceID ='myDataSource' OnRowUpdating ='FixFormatting' AutoGenerateColumns ='false' CellPadding ="3" ...> Notice the OnRowUpdating handler that I've installed in my grid view. That code looks like this: protected void FixFormatting( object sender, GridViewUpdateEventArgs args) { decimal amountPaid = ParseDecimal(( string )args.NewValues[ "AmountPaid" ]); args.NewValues[ "AmountPaid" ] = amountPaid; } When you handle this event, you're given a dictionary of old and new values, which appear to come directly from the controls (in my case, a TextBox was used to gather the updated data AmountPaid, so the type of object that I found in NewValues["AmountPaid"] was a string. I wrote a little helper method called ParseDecimal that parses a string into a decimal value, allowing currency characters, decimal points, and thousands separators. I also allowed a blank value to indicate zero: public static decimal ParseDecimal( string value ) { if ( string .IsNullOrEmpty( value )) return 0; return Decimal.Parse( value , NumberStyles.AllowThousands | NumberStyles.AllowDecimalPoint | NumberStyles.AllowCurrencySymbol, CultureInfo.InstalledUICulture); } This solved the problem quite nicely. Now two-way binding works with formatted data. Read More...
|
-
We recently updated our website and some links have broken as a result. Here's the place you should go to get the latest version of Password Minder: http://mercury.pluralsight.com/tools.aspx Sorry for any inconvenience! Read More...
|
-
This is the third post in a series. The first post described the problem: ASP.NET wasn't reporting inner exception stack traces. The second post described my solution. This post shows the code I used to solve the problem: a custom email provider for the Health Monitoring system in ASP.NET. Enjoy! Here's the provider. Note that I opted *not* to build a buffering provider to keep things simple: public class MyMailWebEventProvider : WebEventProvider { string to; string from; string subjectPrefix; public override void Initialize( string name, NameValueCollection config) { base .Initialize(name, config); to = GetAndRemoveStringAttribute(config, "to" , true ); from = GetAndRemoveStringAttribute(config, "from" , true ); subjectPrefix = GetAndRemoveStringAttribute(config, "subjectPrefix" , false ); } public override void ProcessEvent(WebBaseEvent raisedEvent) { SendMail(raisedEvent); } private void SendMail(WebBaseEvent raisedEvent) { string subject = ComputeEmailSubject(raisedEvent); string body = ComputeEmailBody(raisedEvent); MailMessage msg = new MailMessage(from, to, subject, body); new SmtpClient().Send(msg); } private string ComputeEmailBody(WebBaseEvent raisedEvent) { WebRequestErrorEvent errorEvent = raisedEvent as WebRequestErrorEvent; if ( null != errorEvent) return ErrorEventFormattingHelper.FormatRequestErrorEvent(errorEvent); else return raisedEvent.ToString(); } private string ComputeEmailSubject(WebBaseEvent raisedEvent) { StringBuilder subjectBuilder = new StringBuilder(); // surface some details in subject about error events WebBaseErrorEvent errorEvent = raisedEvent as WebBaseErrorEvent; if ( null != errorEvent) { Exception unhandledException = errorEvent.ErrorException; // drill through reflection exceptions to show the root cause TargetInvocationException invocationException = unhandledException as TargetInvocationException; if ( null != invocationException) { Exception innerException = DrillIntoTargetInvocationException(invocationException); subjectBuilder.AppendFormat( "{0}" , (innerException ?? invocationException).GetType().Name); if ( null != innerException) subjectBuilder.Append( " (via reflection)" ); } else subjectBuilder.Append(unhandledException.GetType().Name); } // if we've not got anything better // just show the event type in the subject if (0 == subjectBuilder.Length) subjectBuilder.AppendFormat( "Event type: {0}" , raisedEvent.GetType().Name); if (! string .IsNullOrEmpty(subjectPrefix)) Read More...
|
-
We recently switched our blog engine out, and I'm still getting the hang of the new system. Looks like due to a misconfiguration, several comments have been waiting for moderation for days or weeks. If yours was one of them, please accept my apology - I didn't have email notifications turned on, so I wasn't being notified that comments were coming in. I've since fixed the problem, so your comments should show up sooner. Sorry for any confusion! Read More...
|
-
In my last post , I commented on how ASP.NET health monitoring doesn't output stack traces for inner exceptions, which can be problematic due to its heavy reliance on reflection. I spent the morning doing some further spelunking with reflector , and my first solution was to implement a custom WebEvent that overrides ToString() to format itself with all of the data I care about. I then overrode the Error event via global.asax and raised my custom event, instead of letting ASP.NET raise its default event. This worked reasonably well with the SimpleMailWebEventProvider, but didn't seem to change anything at all with the event log provider. What I found is that the two providers were using entirely different means to format the events! The email provider calls ToString(bool, bool) on the event to ask it to format itself. But the EventLogWebEventProvider does its own formatting of individual fields of the event. Indeed, its ProcessEvent method has a big list of checks: if (eventRaised is WebBaseErrorEvent) AddErrorStuff(); if (eventRaised is WebAuthenticationSuccessAuditEvent) AddLogonStuff(); So it seemed like a better approach would be to write my own provider. I left the event log provider alone, and I wrote a custom email provider to display errors in a more useful way. This also allowed me to drop some fields from the event report that aren't useful for us. And I was able to construct a much more concise and useful subject line (the subject line that SimpleMailWebEventProvider uses is rather clunky since it assumes it might be spitting out a whole bunch of buffered events in one go). Not only does my provider include the stack traces for all of the exceptions in the chain, but in the subject line, I display the type of error that is at the root of the problem. So if I am formatting a TargetInvocationException, I drill into its InnerException chain until I find a different exception type, and display that exception type instead. Oh, one other benefit of building the custom provider instead of using a custom WebEvent was that I was then able to remove the Error handler from global.asax. All I had to do was replace the SimpleMailWebEventProvider with my own provider, and I got the behavior I wanted. Now my email notifications include detailed stack traces. I'll post the code for this provider once it's run for a little while in production and I'm satisfied that it works reasonably well. Read More...
|
-
This can be a problem, especially when an ObjectDataSource starts throwing exceptions. The stack trace looks the same because of the way the methods are invoked (via reflection) - you end up with a stack trace for a TargetInvocationException, which basically says, "I used reflection to invoke some method, and it threw an exception. See the inner exception for details." ASP.NET's health monitoring system does list the inner exceptions (apparently up to a maximum depth of two, from spelunking the code with reflector ), but it does not emit the stack traces for these exceptions, which would be really helpful . I've spent some time this morning trying to figure out how I'd customize things to emit this, and it looks like what I'd have to do is catch the exception and generate a custom WebEvent that overrides ToString(bool, bool) and does everything that WebRequestErrorEvent does, but also generate the inner stack trace. That seems a bit ugly. A search for "ASP.NET web event inner exception stack trace" yielded no interesting results, so if you've dealt with this and have a cleaner solution, let me know. I'll post my solution once I get it worked out. Read More...
|
-
I use email as a notification mechanism a lot, and often in class I'll demo sending email via a technique that I use frequently when developing code. It allows you to simulate sending an email message. The trick to doing this is not to hardcode things like host, port, etc. for your SMTP server when you use System.Net.Mail to send mail. Instead, use the default ctor for SmtpClient as I've done in the code below. static void Main( string [] args) { // note the use of the MailAddress class // this allows me to specify display names as well as email addresses MailAddress from = new MailAddress( "admin@fabrikam.com" , "Fabrikam Website" ); MailAddress to = new MailAddress( "mari@fabrikam.com" , "Mari Joyce" ); MailMessage msg = new MailMessage(from, to); msg.Subject = "Testing 123" ; msg.Body = "This is only a test!" ; // note use of default ctor // this looks in config to figure out how to send mail new SmtpClient().Send(msg); } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } What you're telling .NET by using the default ctor for SmtpClient is, "please use my config file to figure out how to send mail". Now you can use the system.net/mailSettings/smtp section in config to specify the details of your mail server, and all of the code in your app that is written to use the default SmtpClient ctor will inherit these settings. Here's an example of what the config on a production server might look like (if you put passwords in your config files, be sure to encrypt those sections ): < configuration > < system.net > < mailSettings > < smtp deliveryMethod ="Network" > < network host ="mail.fabrikam.com" port ="25" userName ="WebsiteMailAccount" password ="whatever" /> </ smtp > </ mailSettings > </ system.net > Read More...
|
-
I've been building some internal pages for our sales team here at Pluralsight , and many of those pages make use of the ASP.NET GridView control to display rectangular data. It's generally a really easy to use control, but I've always struggled with getting column widths to look right. My goal is to fix the width of each column at design time, and any field that contains text that may be longer than my fixed width should wrap around, taking up more vertical space in the table. If you are trying to accomplish this goal, you might find these tips helpful. 1) Set up a CssClass for the GridView itself and include the table-layout:fixed style. This tells the browser that you're going to specify the width of each cell. You may also want to include the overall width of the grid here as I mention in (3). 2) The first row of the table sets the width for each cell, and that's usually the HEADER row, not the item row, so use either HeaderStyle-CssClass or HeaderStyle-Width to set the width of the cell. I wasted a lot of time trying to set the width using the ItemStyle. 3) Make certain the table itself is wide enough to hold all of the cells. I added up all of my cell widths and used that to set the width via the CssClass attribute on the GridView. Using these guidelines, I'm having much better luck controlling the layout of my GridView controls. I hope this simple advise helps someone else! Read More...
|
-
For a couple of years now, I've been giving talks about "claims-based identity", and "claims-aware applications". The most concrete example of a claims-based identity architecture that I've been able to show so far is Active Directory Federation Services v1 (ADFS) and Windows CardSpace. And the claims programming model I've been using is the one that shipped with WCF in the System.IdentityModel assembly. But today I'm happy to announce that there's a new path forward in the claims world. Zermatt is the "identity framework" that I've been itching to talk about, but until today, hasn't been announced publicly. Well, Vittorio just made the announcement just a moment ago, and now you can get your hands on this new framework. With it, you can build web applications and services that rely on claims to discover identity details about users. And you can easily build a security token service (STS) that supplies those claims. Zermatt makes this possible by supplying all of the plumbing that implements WS-Trust (for web services) and WS-Federation (for browser-based web applications). All you have to do is figure out what claims you want to issue based on what you know about the user and what you know about the application (aka relying party). I was fortunate to be asked by the team to write the white paper introducing Zermatt to developers. You can download it here. The paper introduces the ideas behind claims-based identity, and talks about how you can use Zermatt to centralize authentication (and to some degree, authorization) in an STS, thus making it easy to achieve single sign on in your applications, and even be ready to federate with other organizations or platforms should that need arise. Here are some highlights of what you'll find in Zermatt: Zermatt includes a new claims programming model, with IClaimsPrincipal and IClaimsIdentity, two new interfaces that extend the existing IPrincipal and IIdentity that you already know and love from the .NET Framework. IClaimsIdentity adds a collection of claims. Zermatt's claims programming model is in many ways simpler than that in WCF - the Claim class exposes the value of claims as strings (always) and calls the value of a claim "Value", instead of "Resource" as WCF did. But the model is also more sophisticated - multi-hop delegation is supported, so one user can "Act As" another user, and the relying party will see the entire Read More...
|
-
One of the main reasons that Fritz , Aaron , and I wanted to create this company was to provide a home for people who love to teach. We didn't want to build an empire, and we weren't out to get rich. We just wanted a place where we could comfortably practice what we love to do: giving software developers a boost - watching that light go on over their heads when a new concept becomes clear. We love to teach, and it's always exciting to find talented individuals who share that passion. David Starr is one of those people. I've seen him in action, and not only does he have a lot of very practical experience from the trenches, but he also has a clear ability to convey his knowledge and experience to his students. It's clearly important to David to connect with his students, and he does so in a way that lets them know that it's all about them - he's not there just to show off his mad skillz (which he has in abundance!) So I'm very excited to welcome David on board as our newest Pluralsight instructor. I apologize for not writing this sooner - we've all been really busy getting the new website out the door. Here's what David had to say . Welcome! Read More...
|
-
Finally there's a home on the Internet for information cards . I've been waiting for this for a long time - a place to point consumers, executives, and developers to learn more about information cards. And it's not just a Microsoft thing. Founding members include Google, PayPal, Novell, and the Liberty Alliance. While the adoption of information cards has been happening at a snail's pace, this collaboration might just change that. And that would be very good for consumers. Read More...
|
|
|
|