- 1/1/2009 – 1/10/2009
- 1/1/2009 to 1/10/2009
- 1/1/2009
- January 1st, 2009 to February 15th, 2009
- January 2009 to February 2009
- January to February
- Jan 2008
- Jan 2008 to Sep. 2009
- January 15th to February 26th
- January
- Yesterday
- Today
- Last X days
- Last X weeks
- Last X months
- Last X years
- This week
- Last week
Tuesday, January 27, 2009
Shipment Selector Control - Date Ranges
Here are the supported ways to enter a date range (currently any date range entered will search shipments by ship date):
Monday, January 26, 2009
New Development Environment
I know that we all know and love our trusty IndyPro2, and I'm sure it will be around for some time (it does hold our source code still), however, for a number of reasons we are moving our main development applications off of this server and onto new shiny virtual servers. This includes the website (both legacy and new), service scheduler and all other batch/windows service applications, as well as all web services.
This new environment will mirror the staging and production line of servers as closely as possible.
So without further ado I introduce to you:
DevWeb1V DevProtrackV DevBatchV
DevWeb1V: This is where our web applications live. ProTransWeb & ProLink and the main two applications on this server. Go and try it out http://DevWeb1V
DevProTrackV: This is where our "Application Server" systems live. ServiceDispatcher (The web service, primarily used by ProTrack) is the main application on this server. ***IMPORTANT NOTE*** Since the ServiceDispatcher application will be moving from IndyPro2 to DevProtrackV, anyone connecting to the development environment for protrack must change their ini file (usually located in c:\Program Files\Protrack\Settings.ini) to point to this new location (http://DevProtrackV/ProWebServices/ServiceDispatcher/Controller.asmx)
DevBatchV: This is where our Batch Applications live. ServiceScheduler (The windows service formally known as ServiceDispatcher) is the main application that lives on this server.
ALL applications should have a development version, and as requests for apps to be deployed, the inclusiveness of this server environment as it is compared to Production will increase. Meaning, that if you change an app, and it isn't in development yet, it should and will be.
I will grant all of the developers access to these new servers, so that once your initial development is at a point that you would like to either share it with the world, or just want to see it functioning with the rest of the code, you can deploy it.
ProTransPanel
In a continued effort to make the website easy to develop...introducing the ProTransPanel web control. Add it to your page and enjoy these savings:
Here's an example of this control in action:
Add this line to the top of your page to register the control:
Then for each section of your page that requires user input, wrap your controls in a ProTransPanel control like so:
On the code-behind of your page, you can wire in the postback event of the submit button like so:
Here are the important properties you can set on a ProTransPanel:
- No need to modify CSS to match the layout of your page
- No need to create a submit button
- Simplified layout — overall just less code to write.
Here's an example of this control in action:
Add this line to the top of your page to register the control:
<%@ Register Assembly="ProTrans.Enterprise.Controllers" Namespace="ProTrans.Enterprise.UIs" TagPrefix="protrans" %>
Then for each section of your page that requires user input, wrap your controls in a ProTransPanel control like so:
<protrans:ProTransPanel ID="panSearch" runat="server" Title="Enter a zip code (or address) to find:"
LabelWidth="80" SubmitButtonWidth="100" SubmitButtonText="Search">
<asp:Label ID="Label5" AssociatedControlID="txtSearch" runat="server" Text="Zip Code:"></asp:Label>
<asp:TextBox ID="txtSearch" runat="server"></asp:TextBox>
<br />
<asp:Label ID="Label6" AssociatedControlID="ddCountry" runat="server" Text="Country:"></asp:Label>
<asp:DropDownList ID="ddCountry" runat="server">
<asp:ListItem Text="US" Value="US"></asp:ListItem>
<asp:ListItem Text="Canada" Value="CA"></asp:ListItem>
<asp:ListItem Text="Mexico" Value="MX"></asp:ListItem>
</asp:DropDownList>
</protrans:ProTransPanel>
On the code-behind of your page, you can wire in the postback event of the submit button like so:
Protected Sub panAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles panAdd.SubmitClick
Try
'' TODO:
Catch ex As Exception
HandleException(ex)
End Try
End Sub
Here are the important properties you can set on a ProTransPanel:
- Title – The title of the section on the page (or leave it blank)
- SubmitButtonText – The text to display on the submit button (or leave it blank)
- LabelWidth – The width of labels on your page in pixels (should correspond to the label with the longest text)
- SubmitButtonWidth – The width of the submit button in pixels (should correspond to the amount of text displayed on the button)
- EnableSubmitButton – Set this to false if you do not need a submit button.
Wednesday, January 21, 2009
The Easy Way to Save Entities
Through the use of .NET's reflection namespace, we are able to remove a lot of manual work when it comes to saving an entity's data to the database. In most cases, we no longer need to list out every property, figure out how it is represented in the database, and handle other tedious tasks just to save data.
Another advancement in this area is the ability now to identify what properties of entities represent the primary key (or in other words, the properties that uniquely identify a record) of an entity. You've seen the PropertyTranslation attribute before. It has been extended to include an optional value of "IsInPrimaryKey". Set this to true on any property in your entities that identify the important values for that data.
AdvancedShippingNotification Example:
The controller work to save an AdvancedShippingNotification is now very simple (and I don't have to worry about parsing the response to retrieve the created AdvancedShippingNotificationID if one exists):
Here's what is automatically taken care of for you:
Another advancement in this area is the ability now to identify what properties of entities represent the primary key (or in other words, the properties that uniquely identify a record) of an entity. You've seen the PropertyTranslation attribute before. It has been extended to include an optional value of "IsInPrimaryKey". Set this to true on any property in your entities that identify the important values for that data.
AdvancedShippingNotification Example:
////// Gets or sets the unique identifier of an ASN. /// [PropertyTranslation("int_ASNId", IsInPrimaryKey=true)] public int? AdvancedShippingNotificationID { get; set; } ////// Gets or sets the shipment that this ASN is for. /// [PropertyValidation(1, IsRequired = true)] [PropertyTranslation("int_Track#")] public int? ShipmentID { get; set; }
The controller work to save an AdvancedShippingNotification is now very simple (and I don't have to worry about parsing the response to retrieve the created AdvancedShippingNotificationID if one exists):
public void AdvancedShippingNotificationSave(AdvancedShippingNotification advancedShippingNotification)
{
// Create the request to use when saving a brand new entity.
// This is necessary for fields that exist in the database but aren't properties of the entity.
Interface requestAsStartingPointForNewEntity = Context.NameValuePairs(
"int_ASNId", 0,
"sdt_CreationDateTime", DateTime.Now,
"sdt_DateEntered", DateTime.Now,
"sdt_ShipDateTime", DateTime.Now,
"vc_ShipmentWghtUnit", MeasurementUnit.Pounds,
"vc_SCACCode", "PNII",
"ti_HowReceived", 0,
"ti_AsnStatus", 6,
"int_Verified", 0);
// Perform the generic save of this entity.
Interface response = Save("ASNHeaderNewGetEntityByASNId",
"AddASNHeader",
"ASNHeaderNewUpdateEntityByPrimaryKey",
requestAsStartingPointForNewEntity,
advancedShippingNotification);
}
Here's what is automatically taken care of for you:
- Figuring out if this record already exists
- If the record does exist, prevention of losing any values in the database that have not been imported into the entity
- Determining if the save should be an 'Insert' or an 'Update' in the database
- Performing property-level validation to ensure no bad data is allowed
- Parsing the response of the save to get it into a unified format
- Assigning any identity fields back to the entity (in case a new record was inserted, the ASN object will have ASNID assigned appropriately)
Monday, January 12, 2009
Creating new entities
Creating the entities (domain object model) is a vital process of the overall architecture.
Since these entities will be used to represent the business concepts, they should
remain as Simple as possible. Entities should
come as close as possible to real world objects, and should contain only data about
the thing (tangible or intangible real world concept) it is trying to represent.
There are a few guidelines to follow when creating new entities within the system,
and there will be a series of small posts that focus in on the different aspects
of creating entities.
Creating Entities | Legacy System Properties (Access Modifiers)
Do not think of entities as a one to one relationship
to the database tables. Even though most entities save to one table,
this is not always the case.
During this DB-design transition process and nearly anytime we interface with a
third-party system, the data of the interfaces we use to retrieve the data will
not match up with our model. This is why our model needs to stay as "pure" as possible.
Keeping this level of purity means only allowing certain properties to be exposed.
The exposed properties should be what is used to make up that entity.
For instance, most ProTrans developers are familiar with the "Address" table. We
all know that even though the tables says address, it holds much more data than
just geographic location information. So when creating the Address entity, we were
forced to make some modifications to what we had previously defined as an address.
An Address is made up of a City, State, Country, County, Lat. Long coordinates,
Zip code, etc, but all of these deal directly with a physical address...no name,
no dock information, nothing that wouldn't be widely accepted as an address.
In the image above, you will notice that there are two main regions "Required" and
"Optional". In each of these are two regions "Exposed" and "DB Only". Required and
Optional are pretty self-explanatory, however the Exposed and DB Only are distinguished
from each other.
Exposed: public properties that are "officially"
a part of the entity. These properties have gone through some level of thought and
scrutiny and it has been determined for one reason or another that this is an essential
piece of the entity we are creating.
DB Only: private/protected/or internal
(if you are
uncomfortable with access modifiers, I can post a blog about this, just let me know) properties
that are only a part of the entity to hold some legacy data for saves/updates or other legacy business
processes. By there very nature they are temporary and should and will be refactored during the life cycle
of this framework. They have a more restrictive access modifier, because we do not want "improper"
properties cluttering up the entities.
Presentation
^
|
Entities
^
|
Controllers
^
|
Services (Batch processes / Systems Integration)
^
|
Data
^
|
Entities
^
|
Controllers
^
|
Services (Batch processes / Systems Integration)
^
|
Data
Thursday, January 8, 2009
URL Authentication
ProTransWeb has a security mechanism that requires a user to log in to view certain pages within the application. While this is obviously an important and needed feature, sometimes it is a barrier that prevents efficient cooperation between other applications and the website. For instance, we have a new page, "Shipment Charges", that we would like to open when a certain button in ProTrack is clicked.
Enter URL Authentication. The Authenticate.aspx page in ProTransWeb accepts information from the URL after the "?" character (the part of the URL referred to as the querystring) and tries to authenticate the user based on that information. The power to create this authenticated URLs has been bundled in a service named CreateAuthenticatedQuerystring (which I will describe how to use in just a second).
By forming a URL carefully, we can accomplish the following things:
You must pass the parameters "URL" and "Username" to this service to create a proper authenticated URL.
Here is some sample code for creating a URL:
Enter URL Authentication. The Authenticate.aspx page in ProTransWeb accepts information from the URL after the "?" character (the part of the URL referred to as the querystring) and tries to authenticate the user based on that information. The power to create this authenticated URLs has been bundled in a service named CreateAuthenticatedQuerystring (which I will describe how to use in just a second).
By forming a URL carefully, we can accomplish the following things:
- Create a URL that cannot be spoofed by anyone who doesn't know the 'secret key'.
- Log a user in without requiring them to go through the login form.
- Provides a way to perform impersonation of a user without knowing their password.
- Take the user to a particular, protected page.
- Ensure that this powerful URL only works for a limited time (defaulting to 15 minutes).
You must pass the parameters "URL" and "Username" to this service to create a proper authenticated URL.
- URL — should be the page that you want to take the user to (this can be a full URL that includes http://, or a relative URL from the "Enterprise" directory of ProTransWeb).
- Username — should be the user that will be logged in. This must match a user in the system, otherwise the user will be directed to the login page.
Here is some sample code for creating a URL:
'' How to generate a valid querystring:
nvpRequest.Add("Url", "Brokerage/BrokerageVerification.aspx")
nvpRequest.Add("Username", "someusername")
nvpResponse = appContext.Service("CreateAuthenticatedQuerystring").SendReceive(nvpRequest)
Response.Write("http://localhost/ProTransWeb/Public/Authenticate.aspx" + nvpResponse("Querystring"))
Wednesday, January 7, 2009
Shipment Selector Control
There's a new way to add 'select-a-shipment' functionality to your pages. Simply add the ShipmentSelector.ascx control to your page and users will be presented with a consistent, and powerful way to find shipments.
Sort of like Google, users can enter any search criteria they want and the ShipmentSelector control will do its best to find what they were looking for. Currently you can enter either: a track number, a bill-of-lading number, an invoice number, or an ASN ID. If there were multiple matches, the control presents the user with a chance to refine their search.
The control defaults to requiring the user to pick only one shipment (if multiple are found, the user is presented with a grid to select the one they were really looking for). However, you can override this functionality to accept a list of shipments by setting RequireSingleShipment = false on the control's properties. Have your page listen to the ShipmentsSearched event to be able to react to the user's search.
Also similar to a Google search, there are advanced keywords you can place in your query to restrict the search. For instance, starting your search with "BOL:" signals that you only want to search for bill-of-lading numbers.
Sort of like Google, users can enter any search criteria they want and the ShipmentSelector control will do its best to find what they were looking for. Currently you can enter either: a track number, a bill-of-lading number, an invoice number, or an ASN ID. If there were multiple matches, the control presents the user with a chance to refine their search.
The control defaults to requiring the user to pick only one shipment (if multiple are found, the user is presented with a grid to select the one they were really looking for). However, you can override this functionality to accept a list of shipments by setting RequireSingleShipment = false on the control's properties. Have your page listen to the ShipmentsSearched event to be able to react to the user's search.
Also similar to a Google search, there are advanced keywords you can place in your query to restrict the search. For instance, starting your search with "BOL:" signals that you only want to search for bill-of-lading numbers.
Subscribe to:
Posts (Atom)

