Brothers In Code

...a serious misallocation of .net resources

Animate A WPF Element Based On Its Visibility Property

I had a combo box was being displayed based on the the value of another control.  To call attention to it's appearance, I wanted it to "fly in" by animating its size.  Below is my WPF style.  There are a couple of things that I couldn't figure out.  First, having it "fly away" in the same fashion is out since it disappears before any animation takes place (although I have seen people do it in code behind).  Second, I'm not quite sure why I need the first setter value.  I seems like the one in the trigger should work but by itself, it wasn't enough.


    <Style TargetType="{x:Type ComboBox}" >
      <Setter Property="RenderTransform">
        <Setter.Value>
          <ScaleTransform ScaleX="0" ScaleY="0"/>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="Visibility" Value="Visible">
          <Trigger.EnterActions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" To="1" Duration="00:00:2">
                  <DoubleAnimation.EasingFunction>
                    <PowerEase Power="3" EasingMode="EaseInOut"/>
                  </DoubleAnimation.EasingFunction>
                </DoubleAnimation>
                <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" To="1" Duration="00:00:2">
                  <DoubleAnimation.EasingFunction>
                    <PowerEase Power="3" EasingMode="EaseInOut"/>
                  </DoubleAnimation.EasingFunction>
                </DoubleAnimation>
              </Storyboard>
            </BeginStoryboard>
          </Trigger.EnterActions>
        </Trigger>
        <Trigger Property="Visibility" Value="Hidden">
          <Setter Property="RenderTransform">
            <Setter.Value>
              <ScaleTransform ScaleX="0" ScaleY="0"/>
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>

Error: <Object> is not a valid value for property 'Target' in WPF

I'm still pretty new to WPF so you can chalk this one up to inexperience.  I was trying to make a textbox scale in/scale out based on a trigger monitoring visibility.  My thought is I needed to set Storyboard.Target="RenderTransform" and Storyboard.TargetProperty="ScaleX" but doing so got me the above error.  The problem is that Target expects a dependency object and RenderTransform is an attached property.  The simple fix was just to set Storyboard.TargetProperty="RenderTransform.ScaleX.

"show errors" For Oracle Scripts

Everything seems to be turned of by default in Oracle.  That includes returning the details of an error in a sql script. 

After finishing a piece of code like a stored proc or trigger, most developers immediately run the script to make sure there are no errors.  Unfortunately this is all you'll see with Oracle:

Warning: Trigger created with compilation errors. 

There is, fortunately, a way to show the detail of those errors.  If you add a "/" to terminate the script and then "show errors;" to the end of you script.  You'll see the following instead:

Warning: Trigger created with compilation errors.

Errors for TRIGGER PROJECT_BIU:

LINE/COL ERROR
-------- -----------------------------------------------------------------
3/5  PL/SQL: SQL Statement ignored
3/12  PL/SQL: ORA-02289: sequence does not exist

Here's an example:


create or replace trigger PROJECT_BIU
before insert
on PROJECT
referencing old as old new as new
for each row

begin
    --create the id
    select ProjectNuber_seq.nextval
    into :new.Project_Number
    from dual;
end;
/
show errors;

Oracle's Developer Tools - Not So Good (Data Modeler Review)

EDIT: 7/28/2014

I should mention that I'm still using Data Modeler, despite all the shortcomings below.  It's ability to reconcile changes against previous versions of the model, script, or live db, and generate a script is really great.  This could be a great tool,  but there's stilll a lot to be desired.

My current employer primarily uses Oracle as their RDBMS which I have no problem with.  While I can't say I agree that it's a "better" database than Sql Server, I definately see it's strengths and it is certainly a powerful piece of software.  But that unfortunately stops with the database engine.  Oracle's developer tools are on the abysmal side.  They are chalked full of bugs and usability problems. 

Let's take Oracle's Data Modeler.  In typical Oracle fashion it's full of bells, knobs, and whisles.  In addition to the expected relational modeller it also contains a slew of tools aimed at the database geek.  The problem is they're so busy making this the ultimate do-every-thing tool, that they run out of time when it comes down to Usablility:

  • You can't scroll left, right, up, or down beyond the farthest object in that direction.  If you want to add something to the perimeter of your erd, you need to add something and push it over a little at a time, and even that only works to the right or down.  Moving an object left or up runs it into a brick wall.
  • Items in drop down lists like the pk/uk index selection for a constrain or sorted in created order.  Seriously, a database company failing to recognize a decent place for a simple sort?
  • At first I thought there was no such thing as automatic, right angle connectors.  But I right clicked one day on an empty area of the diagram and selected "auto route".  I didn't find such a feature in the preferences.  Worse, I quickly realized this is a worthless feature since it doesn't allow you to fix them afterwards (and it does a pretty lousy job so they do need to be fixed).
  • Doing things like adding columns requires way too many windows and steps:
    • Right click
    • Click Properties
    • Click Columns
    • Click the '+' button
    • Click the Type drop down and select
    • In visio, I just started typing the column name in the pane on the bottom.
  • The properties box is modal-less, which means you can make changes outside of the properties box but you'll need to close it to refresh.
  • The connectors are pretty dumb.  They stay stuck to the edge they were originally attached to even if that's the opposite edge.
  • Inconsistent context menus.  Some things have them, but try to add a table or domain by right clicking?  Nope.  For tables you need you use the designer tool.  For domains you need to go to Tools > Domains Administration
  • In Domains Administration, The Save Button doesn't enable after changing something.  You need to move to something else and wait for the "Do you want to save..." dialog.  After that, then you click save.
  • An additional unique constraint that includes the PK, it is not shown for the PK.  It only shows "P".  Even worse, if the alternate key is composite, the other column looks like it's unique by itself.
  • Mouse wheel only scrolls up and down.  No zoom or horizontal scroll.
  • I just about had a seizure when I saw this color scheme:
  • Very Confusing button sets - On many dialogs there will be a Save, Apply, and Close button.
  • Custom design rules can only be deleted.  They cannot be deactivated.
  • Design Rules is a modal dialog.  You can't leave it open while fix the errors that it lists.
  • Want to rename a table?
    • Table Properties
    • Rename the table
    • Click OK
    • Back into table properties
    • Click Naming Rules
    • Click OK to rename the related constraints
    • Click OK
    • And, no you can't do the rename and the naming rules in the same step - it doesn't rename the primary key constraint until you close out of the properties window.
    • By the way, despite not having an option listed for columns, the naming rules are also going to rename your columns (which was great when my Parent_Object_Id, Child_Object_Id columns were renamed Object_Id and Object_Id1).
  • No feedback - try deleting a column with a foreign key constraint.  Nothing will happen.  You have to delete the FK, click ok, then go back in a delete the column.
  • Save As always defaults to your My Documents directory.  So if you want to save a copy as a different name but under the same directory, you better not forget to change the path
  • Multiple periods in the file name are ignored.  So if you save an erd as erd1.2.dmd it will be saved as erd1 instead.
  • As far as I can tell there is no way to change your subversion server url.  They give you a cute Edit "server" button that opens a text editor for other subversion parameters, but to change my server from a local repository to an online one, I had to rename repositories.xml under C:\Documents and Settings\brandon\Application Data\Oracle SQL Developer Data Modeler\system3.1.0.691\o.jdeveloper.subversion.11.1.1.4.37.59.48 and start again.
  • Can't associate a DMD file - I've tried opening the program by clicking on a dmd file.  However, associating the dmd file extension to data modeler causes the dmd file to be opened in sql developer instead as an xml file??
  • No indicator that current file has changes.  Asterisk, italics, anything would be nice to let me know that changes have been made but are not saved.
  • CTRL-S doesn't save.  Ever other app on the planet, including Oracle's SQL Developer (so I know they got the memo), uses CTRL-S to save.  I thought I was saving, but of course with the item immediately above this one, I didn't realizing that I was doing CTRL-S just to stretch my index finger and not for any other purpuse.

Error Screenshots From Names You Can Trust

I collect screenshots of errors (yes I'm a geek).  I find it to be a friendly reminder that no piece of software is ever bug-free, regardless of how big the company is.  And I'm not talking obscure, hard to reproduce, stuck in a feature that nobody uses, bugs.  These are up front and center.  I'll update this post as I get them.

3/16/2011 - Oracle Data Modeler 3

 

Gmail after clicking the button to register a new account:

 

Quick Way to View an InfoPath Data Connection File.

Once you upload a data connection file into central admin, there doesn't seem to be a way to view it's contents.  If you have access to the central admin config database, you can find the xml of the file in the properties column of the objects table...


select cast(properties as xml) from objects o
where name like '%.xml%'

Moving a "Simplified Configuration" WCF Service From IIS Host to a Self Host

WCF configuration files can get pretty big pretty fast, so the new Simplified Configuration in .Net 4.0 definately interested me.   However there are a couple of errors that make perfect sense but might catch you off guard if you're not reading the fine print.

Error: Service has zero application (non-infrastructure) endpoints

The simple configuration is supposed to create one end point for each base address / contract combination.  So what happend here?  This can be explained by one simple line in the above linked document:

Services hosted under Internet Information Services (IIS) or Windows Process Activation Service (WAS) use the virtual directory as their base address.

Services NOT hosted in one of these environments assume nothing which is a good thing.  It wouldn't make a lot of sense if some console.exe program just randomly grabbed a port to use.  So you are required to specify a base address:


    <services>
      <service name="MyApp.MyService">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:51234"/>
          </baseAddresses>
        </host>
      </service>
    </services>

Error: The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address.

This is due to the following line:


  <serviceMetadata httpGetEnabled="true"/>

You're basically saying that you want the metadata to be exposed via http, but you've provided no http endpoint.  One fix is to set it to false.  Another is to add a second base address:


          <baseAddresses>
            <add baseAddress="net.tcp://localhost:51234"/>
            <add baseAddress="http://localhost:50080"/>
          </baseAddresses>


No rocket science here, but I wanted to post this because the answers I found weren't very specific.
 

Selecting A Row With A Minimum Value

I think everybody has had to do this a dozen times: return a row of data having a minimum value.  The first reaction is to do a subquery in the where clause or as a derived table:


select *
from scan s
join (
  select min(scan_date) as scan_date
  from scan
) s_min on s.scan_date=s_min.scan_date
where rownum=1

A more performant solution is to use analytic functions:


select *

from (
  select s.*, row_number() over (order by s.scan_date) rn
  from scan s)
where rn = 1;

However, I thought this was a relatively simple and database agnostic solution:


select s1.*
from scan s1
left join scan s2 on s1.scan_date>s2.scan_date
where s2.scan_date is null
and rownum=1

This will return the row  where no other row in the same table is older - in other words, the oldest row.  This is probably the worst performer in the bunch but might be useful for an already complicated query.

HTTP Requests With Oracle - Part 1

Making a web request from oracle is as easy as:


select utl_http.request('www.google.com')
from dual;

But if you're making a request from Oracle, chances are you need to do a little work and not just google something so here's a little pl/sql code...


declare
  l_url varchar2(4000) := 'www.google.com';
 
  l_httpRequest   utl_http.req;
  l_httpResponse  utl_http.resp;
  l_tempResponse clob;
  l_buffer varchar2(32767);
begin  
  utl_http.set_detailed_excp_support (true);

  --setup the http connection
  l_httpRequest := UTL_HTTP.begin_request(l_url, 'GET','HTTP/1.1');

  l_httpResponse := utl_http.get_response(l_httpRequest);
  
  --write the response (in blocks) to a temporary clob
  dbms_lob.createtemporary(l_tempResponse, FALSE);
  BEGIN
    LOOP
       UTL_HTTP.read_text(l_httpResponse, l_buffer, 32767);
       dbms_lob.writeappend(l_tempResponse, length(l_buffer), l_buffer);
    END LOOP;
  EXCEPTION
    WHEN utl_http.end_of_body THEN NULL;
  END;
  UTL_HTTP.end_response(l_httpResponse);

  DBMS_OUTPUT.PUT_LINE('Response: ' || l_tempResponse);

end;
/

It would be a simple job to add a couple of query string parameters on to the URL.  But it's very likely you'll be sending data to some sort of a web service and then expecting a response.

Here's an example of a POST request.


  ....
  l_httpRequest := UTL_HTTP.begin_request(l_serviceUrl, 'POST','HTTP/1.1');
  utl_http.set_header(l_httpRequest, 'Content-Type', 'application/x-www-form-urlencoded');
 
  --these are likely constant for the application
  l_postData :=
    'x_login='|| 'xxxxxx' 
    || '&x_tran_key='|| 'yyyyyyy'
    || '&x_delim_data='|| 'TRUE'

   utl_http.set_header(l_httpRequest, 'Content-Length', length(l_postData));


  utl_http.write_text(l_httpRequest, l_postData);
  --get a reference to the response
  l_httpResponse := utl_http.get_response(l_httpRequest);
  ....

That's it.  In Part 2 I'll go over some of the finer points of SSL requests and the Oracle Wallet.

Getting Active Directory Information From ASP.net The Easy Way

A couple years ago I wrote a short article on an old blog about connecting to Active Directory from an ASP.net project.  I thought the article was fairly complete until I tried to help somebody else do the same thing.  I had forgotten two major parts:

  • Manually sifting thru the different AD containers to find the right LDAP string. 
  • Ensuring the correct security context.

The security context can be an easy thing.  A console or winforms .net app runs in the context of the currently logged on user.  For simplicity the ASP.Net development server in Visual Studio does the same thing.  So as long as your user account as access to the AD (which is the norm unless your AD admin has locked down querying), then you should be fine while you are developing.

Running under IIS is a different story.  Depending on the version, the user context of the iis process could be all sorts of things - ASPNET, Network Service, Local Service, the new IIS app pool accounts, etc.  During testing the easiest thing to do is enable impersonation:


<configuration>
  <system.web>
    <identity impersonate="true" userName="contoso\Jane" password="********" />
  </system.web>
</configuration>

Optionally, you can remove the userName and password attributes and make sure that NTLM/Integrated security is still checked in the IIS configuration.  This will make IIS operate similar to the ASP.Net Development Server and use the current user as the execution context.

So with the security context out of the way, the next task is to actually query AD for a user object.  In my old example, I would specify the container to connect to whe creating a DirectoryEntry object.  For example:


System.DirectoryServices.DirectoryEntry directoryEntry
        = new System.DirectoryServices.DirectoryEntry(LDAP://CN=Users,DC=contoso,DC=com);

This worked fine for me but was a bit to limiting as an example to go by.  Many domains have an additional "corp" domain context.  "Users" might be named something different like "DomainUsers".  There are all sorts of variables with AD.  To combat this I added an extra query to "RootDSE" which is sort of the current context.  With that I'm able to get the address of an actuall domain controller and from there, can make the query on a higher level.  In the example below, I'm trying to get the AD User object for the currently authenticated user, but there's lots of diagnostic output that would let you find other things or narrow down the container that you're querying (for performance reasons).

 


<%@ Page Language="C#" %>
<%@ Import Namespace="System.DirectoryServices" %>
<%@ Import Namespace="System.Collections.Generic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
      OptLocalUser.Text = User.Identity.Name;
     
      Dictionary<String, String> directoryPropertyDictionary;
       
     
      //get the currently connected AD server info:
      DirectoryEntry de = new DirectoryEntry("LDAP://RootDSE");
     
      directoryPropertyDictionary = new Dictionary<string, string>(de.Properties.Count);
      foreach (string key in de.Properties.PropertyNames)
      {
        String allValues = "";
        foreach (object value in de.Properties[key])
          allValues += value;
        directoryPropertyDictionary.Add(key, allValues);
      }
      GridView1.DataSource = directoryPropertyDictionary;
      GridView1.DataBind();
     
      //connect to the current server
      de = new DirectoryEntry("LDAP://" + de.Properties["dnsHostName"][0].ToString());

      DirectorySearcher ds = new DirectorySearcher(de);
      String[] nameParts = User.Identity.Name.Split('\\');
      ds.Filter = "(&(objectClass=user)(sAMAccountName=" + nameParts[1] + "))";
      SearchResult result = ds.FindOne();


      OptUserPath.Text = result.Path;
      directoryPropertyDictionary.Clear();
      foreach (string key in result.Properties.PropertyNames)
      {
        String allValues = "";
        foreach (object value in de.Properties[key])
          allValues += value;
        directoryPropertyDictionary.Add(key, allValues);
      }
      GridView2.DataSource = directoryPropertyDictionary;
      GridView2.DataBind();

      OptLocalUser.Text = User.Identity.Name;
    }
  </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
      Local User:<asp:Label ID="OptLocalUser" runat="server" /><br/>
     
      RootDSE:
      <asp:GridView ID="GridView1" runat="server">
      </asp:GridView>
     
      AD User Path:<asp:Label ID="OptUserPath" runat="server" /><br/>
      <asp:GridView ID="GridView2" runat="server">
      </asp:GridView>
    </div>
    </form>
</body>
</html>