mhinze.com

links etc.




    Archive for the 'Tips' Category

    NHibernate composite-element mapping

    Friday, February 15th, 2008

    A tip for mapping value objects that is sort of buried in the NHibernate documentation.

    Please note that a composite element mapping doesn't support null-able properties if you're using a <set>. NHibernate has to use each columns value to identify a record when deleting objects (there is no separate primary key column in the composite element table), which is not possible with null values.

    When you fail to follow this guidance you get orphaned records left in the database when you delete them from the collection.  When I fail to read the documentation (again) I learn a good lesson about how important it is to read the documentation (again).  =)

    After properly implementing Equals() and GetHashCode() with cascade="all" the persistence (and deletion) is transparent.

    Just set 'em all to not-null. 

    Here is an example mapping from CodeCampServer:

    <set name="Sponsors" table="Sponsors" cascade="all">
    <key column="ConferenceId" />
      <composite-element class="Sponsor">
        <property name="Level" not-null="true"/>
        <property name="Name" length="50" not-null="true"/>
        <property name="LogoUrl" length="260" not-null="true"/>
        <property name="Website" length="260" not-null="true"/>
        <nested-composite-element name="Contact" class="Contact">
          <property name="FirstName" length="20" not-null="true"/>
          <property name="LastName" length="20" not-null="true"/>
          <property name="Email" length="60" not-null="true"/>
        </nested-composite-element>
      </composite-element>
    </set>

    Have a PL/SQL Christmas

    Thursday, November 29th, 2007
    
    select decode
           ( sign(floor(maxwidth/2)-rownum)
           , 1, lpad( ' ', floor(maxwidth/2)-(rownum-1))
           || rpad( '*', 2*(rownum-1)+1, ' *')
           , lpad( '* * *', floor(maxwidth/2)+3))
    from all_objects, (select 40 as maxwidth from dual)
    where rownum < floor(maxwidth/2) +5;
    



    HtmlAgilityPack User Agent / HttpWebRequest object

    Tuesday, November 6th, 2007
    using System;
    using HtmlAgilityPack;
    using System.Net;
    
    class LbHtmlWeb : HtmlWeb
    {
        private readonly string USER_AGENT =
            "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
    
        public LbHtmlWeb()
        {
            this.PreRequest += delegate(HttpWebRequest request)
            {
                request.UserAgent = USER_AGENT;
                return true;
            };
        }
    
        //…
    }

     

    Now that was easy!  Pretty spiffy that HtmlWeb exposes that event.

    Please let me at the HttpWebRequest.

    Don't do this (RSS Toolkit, I'm talking to you):

    XmlDocument doc = new XmlDocument();
    doc.Load(url);

     

    =(

    Simple Scraping with SubSonic.Sugar

    Friday, June 29th, 2007

    SubSonic.Sugar has some handy utilities.  Highly recommended.  The source is worth browsing too.

    static void Main()
    {
        foreach (string link in SubSonic.Sugar.Web.ScrapeLinks("http://mhinze.com/", false))
        {
            Console.WriteLine(link);
        }
    }

     

    image 

     

    Technorati tags: , ,

    Where is the Row in RowCommand? DisplayIndex

    Saturday, June 9th, 2007

    So I sat down to do a little "data-driven web development with ASP.NET 2.0" today. 

    GridView, check.  DataSource, DataBind, check. 

    Next I wanted to add a button to each row to perform an action on the item the row represented. 

    So I added my ButtonField and ran into this problem: GridViewCommandEventArgs doesn't expose the Row. (!)

    Fair enough, I'll just add a Template Column and set the CommandArgument like Rick Strahl explains.

    But that didn't work either - actually there are a couple problems.  First, I needed two fields, or the entire row, not just the PK.  And second, I want it to eventually handle paging and it has some concurrency issues because RowIndex no worky-worky since it looks at the data source and not at the grid on the page. 

    Also, I have mutliple grids firing this event handler, so I didn't ever want to reference a specific instance of the GridView.

    Well at first I tried hard coding both fields into the CommandArgument and then splitting them up in the code-behind:

    <asp:gridview id="GridView1" runat="server" onrowcommand="Grid_Command">
        <Columns>
            <asp:TemplateField ShowHeader="False">
                <ItemTemplate>
                   <asp:LinkButton ID="Link1" runat="server"
                        CausesValidation="false"
                        CommandName="Cmd"
                        <!– in the ghetto, below –>
                        CommandArgument='<%#Eval("ID") + ":" + Eval("Name") %>'
                        Text="DoStuff">
                        </asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:gridview>

     

    protected void Grid_Command(object sender, GridViewCommandEventArgs e)
    {
        if (e.CommandName == "Cmd")
        {
            string[] arg = (string[])e.CommandArgument.ToString().Split(':');
            int id = int.Parse(arg[0]);
            string name = arg[1];
            DoStuff(id, name);
        }
    }
     

    The above works great, and I should be commended for my hackery.  But it's WRONG!

    So the solution to this unfortunate hubbub lies with a little known property called DisplayIndex, which gives you the index of the selected row on the page.

    Also, make sure you set the DataKeyNames property of the GridView.

    <asp:GridView ID="GridView1" runat="server" OnRowCommand="Grid_Command" DataKeyNames="ID,Name">
        <Columns>
             <asp:TemplateField ShowHeader="False">
                <ItemTemplate>
                    <asp:LinkButton ID="Link1" runat="server"
                        CausesValidation="false"
                        CommandName="Cmd"
                        CommandArgument='<%# Container.DisplayIndex %>'
                        Text="DoStuff">
                    </asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
         </Columns>
    </asp:GridView>
     
     

    protected void Grid_Command(object sender, GridViewCommandEventArgs e)
    {
        if (e.CommandName == "Cmd")
        {
            GridView gv = (GridView) sender;
            int index = int.Parse(e.CommandArgument.ToString());
            int id = (int) gv.DataKeys[index]["ID"];
            string name = (string) gv.DataKeys[index]["Name"];
            DoStuff(id, name);
        }
    }

     

    Not new, but the hotness!

     

     

    Technorati tags: , ,

    10 Reasons I Unsubscribed From Your Blog

    Monday, June 4th, 2007

    I just unsubscribed from several feeds in my reader.  Was a lot of fun.

    I unsubscribe if I notice unread items and don't care.

    This can happen if you:

    • Update too frequently. I don't want to slog through hundreds of items.
      Solution: make sure you publicize your tag or category feeds
    • Exist only as a node of the echo-net.  I am smart enough to subscribe to the primary sources I want. 
      Solution: check the del.icio.us tag, if your reference is there, you are probably too late.
    • Partial-text feeds are the devil.  If you don't think your feed is worth my attention, I agree. 
      Solution: full text feeds.
    • Post too much personal stuff. Solution: separate personal and professional feeds.
    • Post paid review-type articles and shameless self-promotion.  So tacky. 
      Solution: don't do it.
    • Fail to understand your audience's skill level.  Fair enough.  You are writing for any audience you choose.  I just might not be a part of it. 
      Solution: Focus on your existing readership - your audience will grow organically, don't force growth else you'll dilute that audience.
    • Post about your your conference schedule or your blog itself. 
      Solution: stay on topic.
    • Make announcements before you have something to announce. 
      Solution: save it.
    • Write long, boring articles.  Long interesting articles are okay.
      Solution: Make a screencast instead and save the whitepaper for the book.  Use humor and images.
    •   Participate in blog memes. 
      Solution: Recognize what your readers are there to receive.  Eliminate everything else.

     

    Things that you might think are bad for your readership numbers but aren't:

    • Rare updates or long periods of inactivity. 
      If you simply go away and don't update for awhile you are not in danger.  Only squeaky wheels get the unsubscribe grease.
    • Advertisements at the bottom of your feed items. 
      I have learned to adblock them - technically and with my eyes.
    • Poor writing skills in a technical blog. 
      I deal with enough bad writing to be used to it.  In RSS I prefer content to great writing. Tip: use screenshots liberally.

    Technorati tags: , ,

    .NET Tip: Active Directory's pwdLastSet to DateTime in C#

    Thursday, April 19th, 2007

    to get a DateTime representing the last time a user's password was changed in active directory:

     

    IADsLargeInteger plsVal = (IADsLargeInteger) de.Properties["pwdLastSet"].Value;
    long filetime = plsVal.HighPart * 4294967296 + plsVal.LowPart;
    DateTime PasswordLastSet = DateTime.FromFileTime(filetime);
     

    converted from just one of the (vb.net) tidbits i found today in a truly most excellent wrapper for active directory…  just what i was looking for.  That is ActiveDs.IADsLargeInteger btw.

     

     

    Technorati tags: , ,

    clear or remove query string on postback… nifty but ghetto

    Wednesday, March 28th, 2007

     
     
    if (!Page.IsPostBack)
    {
       // set defaults, hide yer panels, etc here
    
       if (Request.QueryString["q"] != null) // if the query string exists
       {
           // aspnetForm - default masterpage form name, YourPage.aspx - whatever your page name is
           ClientScript.RegisterStartupScript(this.GetType(), "qr", 
                "document.all(\"aspnetForm\").action = \"YourPage.aspx\";", true);

           // do stuff with query string
           RedrawPage(Request.QueryString["q"]);
       }
    }

     

    via msdn

     

    so if you have a page that accepts a query string, you definitely want to set that view

    but this is asp.net, so your page also posts back

    you don't want the query string to persist across postbacks - that's just ugly, breaking bookmarks, etc.  you want to clear the query string but maybe you are trying Clear() or Remove() and you are realizing that Request.QueryString is Read Only..

    one option is to set the form's action in client script (Googlebot's not posting back anyway)

    is there a better way to do this?  it seems so ghetto

    by the way, comments on this blog are NOT nofollowed

     

    .

    pl/sql: updating a table but not replacing it, with sequence/triggers

    Monday, March 26th, 2007

    ever forgotten the predicate of your update query?  you would have to update everything from backup.  back to the future!

    i affected (er, corrupted) all 1600+ rows of semi-important data recently.  and had to restore the rows (while keeping the rows added since last night).. thought about something cool like insert into where not exists or using minus, but we settled on the fastest way:

    alter trigger schema.id_pk_increment disable
    delete from schema.table where id in (select id from other_schema.table_restore)
    insert into schema.table select * from other_schema.table_restore
    alter trigger schema.id_pk_increment enable
    

    consuming web services / crafting webrequests using ssl where the cert is expired or otherwise hosed

    Monday, March 19th, 2007

    with 1.1, we'd do this:

     

    internal class AcceptAllCertificatePolicy : System.Net.ICertificatePolicy
    {
        public bool CheckValidationResult(System.Net.ServicePoint sPoint,
            System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest wRequest,int certProb)
        {
            return true;
        }
    }//acceptallcertificatepolicy

     

    and then in the consumer

    System.Net.ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();

     

    but try this in 2.0 and you get a warning:

    System.Net.ServicePointManager.CertificatePolicy' is obsolete: 'CertificatePolicy is obsoleted for this type, please use ServerCertificateValidationCallback instead.

    we don't have to implement ICertificatePolicy any longer.  and we can use anonymous delegates to craft a better solution:

    System.Net.ServicePointManager.ServerCertificateValidationCallback +=
        delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                                System.Security.Cryptography.X509Certificates.X509Chain chain,
                                System.Net.Security.SslPolicyErrors sslPolicyErrors)
            {
                return true;
            };

    © 2007 mhinze.com