<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="http://www.timewarptechnologies.com"  xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>Timewarp Technologies, LLC - Hints and Tips</title>
 <link>http://www.timewarptechnologies.com/taxonomy/term/1</link>
 <description>Frequently asked questions and other advice that doesn&#039;t fall within the HowTo step by step style.
</description>
 <language>en</language>
<item>
 <title>Bring rogue programmers in from the cold.</title>
 <link>http://www.timewarptechnologies.com/node/64</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;One interesting resource for programmers is &quot;The Daily WTF&quot;. It is a great place to read about things that should *not* be happening in your organization: antipatterns for code and business processes (mostly code). &lt;/p&gt;
&lt;p&gt;A recent &quot;&lt;a href=&quot;http://thedailywtf.com/forums/thread/72894.aspx&quot;&gt;The Daily WTF&lt;/a&gt;&quot; discusses the plight of a company where a “rogue” programmer built an inefficient spreadsheet for a supervisor, only to find it was too popular and those inefficiencies brought down the database by overloading it with connections. Operating outside of IT, the understanding of enterprise requirements was not there. On the other hand, many have argued that rogue programmers exist because of unresponsive IT departments, and they provide a valuable service.&lt;/p&gt;
&lt;p&gt;The irony is that “rogue” programmers can easily be replaced in most companies by a small number of junior programmers that report to IT itself. Once a skunk works application is conceived and built, IT can use it as a prototype for a real solution to the problem.&lt;/p&gt;
&lt;p&gt;In the article, the rogue programmers “application” is a nightmare for IT: because they didn&#039;t review it and bring it up to standards. On the other hand, any IT department that can&#039;t take a working (even if for a single user) application written by an outsider and convert it into a real, audited application in short order needs to be sacked. The specification is a functional prototype made using tools that limit expressiveness and probably with a skill set that is simplistic at best. If IT can’t trump that quickly and simply, it is time to reconsider your IT department.&lt;/p&gt;
&lt;p&gt;Our solution to skunk works projects: we encourage them, but we insist that once they are functional as prototypes that IT rework them to fit into the infrastructure. We maintain several real time replicas of the databases; skunk works projects operate off those to avoid disruption of production.&lt;/p&gt;
&lt;p&gt;It turns out your users probably know their requirements better that the IT gurus... and can implement a solution or partial solution. On the other hand, only IT can assure integration problems won&#039;t ensure. Use each for their strengths and your library of applications will grow with the combination of user knowledge *and* IT robustness. It is ironic that Agile Methods are all the rage; yet realizing that the initial prototype phase can be very effective in the hands of the central stakeholder seems verboten.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Tue, 16 May 2006 15:58:57 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">64 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>Excel Spreadsheet Transformations</title>
 <link>http://www.timewarptechnologies.com/node/63</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Today I had an request to transform data that came from a printed report into usable Excel data. The data had been printed to PDF, so a PDF to Excel converter tool did the bulk of the work. However, even after being placed in Excel, there were two remaining requirements to achieve this goal. The first was to remove the recurring headers and footers of the report. The second was to reassemble each reported item&#039;s three lines of data back into to a single coherent row of data.&lt;/p&gt;
&lt;p&gt;To do the initial transformation (removing the headers) I used the following VB for Applications code:&lt;/p&gt;
&lt;pre&gt;
Sub RemoveHeaders()
Dim CellRow As Integer, CellCol As Integer

CellRow = 1
CellCol = 1
Dim eof As Integer
eof = 0
Do Until eof = 1
    Select Case Cells(CellRow, CellCol).Value
    Case &quot;EOF&quot; &#039;We have found the manually inserted EOF. Stop.
        Exit Do
    Case &quot;MIGBCL01&quot; &#039;We have found the page header tag. 
        &#039;The next two lines are not actually used in the final run, 
        &#039;but were mearly a visual confirmation that the targeted lines were accurate.
        &#039;Set myRange = range(Cells(CellRow, CellCol), Cells(CellRow + 13, CellCol))
        &#039;myRange.Select

        &#039;Remove the row and the 13 rows after it.
        For i = CellRow To CellRow + 13
            rows(CellRow).Delete
        Next
    Case Else &#039;We have an ordinary row, continue
        CellRow = CellRow + 1
    End Select
Loop
Cells(CellRow, CellCol).Select &#039;Select EOF, we are done.

End Sub
&lt;/pre&gt;&lt;p&gt;This is a very simple routine, but hopefully others will find the ideas in it helpful when confronted with data that is oddly formatted. There were a few manually performed steps: first was the insertion of the EOF to stop the routine and the second was manually handling the totals at the bottom of the report. However, those changes only took seconds to perform: the bulk of the work was removing the hundreds of page breaks. The code completed in under a minute (and would have been faster if Application.ScreenUpdating had been set to false for the actual run).&lt;/p&gt;
&lt;p&gt;After getting the headers out of the way, it was time to transform the data itself. First, I created extra columns to move the data into:&lt;/p&gt;
&lt;pre&gt;
Sub createNewColumns()
&#039;This routine creates two extra columns for each existing
&#039;column. This will allow dumping the data from the rows
&#039;below a main row into the column.
Dim i As Integer
For i = 11 To 2 Step -1 &#039;We have 11 columns,
    &#039;we wish to insert two more for each to &quot;flatten&quot; the data.
    Columns(i).Insert
    Columns(i).Insert
Next
End Sub
&lt;/pre&gt;&lt;p&gt;Then we needed to move the data itself. Here we loop over the range, migrating the data from the old rows into the new columns.&lt;/p&gt;
&lt;pre&gt;
Sub moveData()
&#039;Move through the data grid, tranferring data from columns
&#039;into a single row.

Dim startRow As Integer &#039;The first row of a data group.
Dim columnNumber As Integer &#039;The column to work in
Dim dataRow As Integer &#039;The subdata row to work with
For startRow = 13 To 3249 Step 4 &#039;Hard coded start, end and skip length for run.
    For columnNumber = 1 To 31 Step 3 &#039;Gap is 4, only 3 rows of data, one blank
        For dataRow = 1 To 2 &#039;Simply 1,2 to get at the rows below the main.
            Cells(startRow, columnNumber + dataRow) = _
                Cells(startRow + dataRow, columnNumber) &#039;Move from rows to columns
        Next dataRow
    Next columnNumber
Next startRow
End Sub
&lt;/pre&gt;&lt;p&gt;Finally we remove the extra data rows, which are no longer necessary with the data in the new columns&lt;/p&gt;
&lt;pre&gt;
Sub removeExtraRows()
&#039;Remove the extra rows now that data is in single rows.
Dim CellRow As Integer, i As Integer
CellRow = 13 &#039;Start on first data line
Do
    If Cells(CellRow, 1) = &quot;EOF&quot; Then
        Exit Do &#039;We are done.
    End If
    
    CellRow = CellRow + 1
    For i = 1 To 3 &#039;three extra rows each.
        rows(CellRow).Delete
    Next
Loop
Cells(CellRow, 1).Select &#039;We are done, select EOF
End Sub
&lt;/pre&gt;&lt;p&gt;With these steps performed, I formatted a few columns to improve data display and forwarded the transformed spreadsheet on to the end user. I have not turned this into a generic tool because each situation will be different and require a lot of cusomization, but hopefully these code snippets will help someone looking for a template on how to manipluate Excel spreadsheets.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Wed, 10 May 2006 20:34:39 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">63 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>Domain specific languages, are they worth it?</title>
 <link>http://www.timewarptechnologies.com/node/61</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Yet another interesting discussion of Domain Specific Languages popped up:&lt;/p&gt;
&lt;p&gt;http://www.artima.com/forums/flat.jsp?forum=106&amp;amp;thread=155752&lt;/p&gt;
&lt;p&gt;Domain specific languages (DSLs) are scripting languages (usually) built on top of a traditional environment. For programmers who have written code only in a single language model, moving to a DSL model can be a bit of a shock. The fundamental idea is that when your base programming language is becoming constraining in some way, you can build a DSL on top of it to remove some of the limitations. (Another option is migrate to a more powerful language, but sometimes that is too drastic of a step to take.)&lt;/p&gt;
&lt;p&gt;A project I worked heavily on uses a code generator. Code generators are actually very similar to domain specific languages in the problem set that they solve. In this specific case, the code was being written in VBScript for use in an Active Server Page application. The application created long before .NET 1.0 was released. Over time, it became clear that the same fundamental ideas were being used over and over in some parts of our system. &lt;/p&gt;
&lt;p&gt;Instead of repeating ourselves, we built a data driven code generator. We provided non programmers with a UI to edit the system components without actually writing code. The data in the database created by the non programmers was in effect a DSL that was specified the generated code. Our DSL allowed us to create the structure of the required code, with the generated code being fleshed out on demand to the specifics based on the data provided by our non programming staff. The final product allowed us to reintroduced inheritance into VBScript via our DSL, reducing our actual source code size while increasing productivity.&lt;/p&gt;
&lt;p&gt;This kind of solution works well in some cases, but can also create debugging problems. Your crashes will probably only point to generated code, which you must then map back to the original source of the problem. On the other hand, over time the DSL can become very robust as edge cases are dealt with and debugging routines are implemented.&lt;/p&gt;
&lt;p&gt;We are currently porting the application from VBScript to C#. We are seeing great improvements simply by building interpreters of the original DSL. The old system would generate code which then was saved to ASP pages on disk which were invoked by end users. The C# based interpreters are seeing response times improve because many of the ugly VBScript hacks have given way to a more elegant C# based class structure and a more direct implementation of our ideas.&lt;/p&gt;
&lt;p&gt;All of this brings up the question: are DSLs worth the effort and potential liabilities? One limitation is that DSLs are not usually strongly typed while languages such as C# and Java are. This means you can create more errors that are found late in the process with DSLs. Another limitation is that your core language has robust debugging tools and a large library to call upon. On the other hand, C# and Java can be very cumbersome to use if you need to invoke many variants of nearly identical code. You could subclass for each instance, but in many cases DSL driven invocations of a more flexible object are more efficient than generating reams of nearly identical code. As long as the work to build and maintain the DSL is lower than subclassing, it is a winning choice.&lt;/p&gt;
&lt;p&gt;After many years of using these types of solutions, I am finding that the best way to use DSLs is as a guide for what your core system really needs to be able to do. The dividing line between DSL and core code will evolve as the system grows. Early on, you will probably have all functionality in the core system. As your requirements demand more flexibility, you will find yourself repeating yourself. Where possible, use the core system’s language to eliminate that repetition: you will retain the advantages of strong typing and a robust debugging environment. In many cases you can avoid creating a DSL and the work maintain it by clever analysis of the core problem.&lt;/p&gt;
&lt;p&gt;However, if you find that you are repeating yourself in ways that your core language cannot easily remove, consider moving to a DSL or data driven architecture. Over time, this flexibility will lead to the discovery of features that probably should be submerged back into the core system. You will find these wherever you find yourself repeating yourself in the DSL (which defeats the purpose of the DSL) or creating large blocks of code in the DSL. When these things happen, it indicates that your core system needs to gain more power, potentially as an enhancement to the DSL you created. Remember, the goal is to write as little code as possible in the DSL side: verbose DSL code is an anti-pattern to be avoided and too much dependence on a DSL can create a version of the &lt;a href=&quot;http://en.wikipedia.org/wiki/Second-system_effect&quot;&gt;second system effect&lt;/a&gt; where too much work is being put into the DSL and not enough on solving the original problem.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Fri, 21 Apr 2006 18:13:07 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">61 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>Google Analytics</title>
 <link>http://www.timewarptechnologies.com/node/56</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Google is testing a new service: &quot;Google Analytics&quot;. If you already use Ad Words, then this additional reporting can be useful for determining which words are getting you the best results. More than a hit counter, the product allows you to monitor four items to help you understand your marketing successes and failures.&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
Campaign tracking variables: These are URL elements you can use in web advertisements, e-mails, etc. These codes allow you to track the number of users arriving from different marketing efforts. Most marketing people have figured out how to use different landing pages or similar variables to track marketing responses, but these are particularly useful when combined with the other Analytics features.
&lt;/li&gt;
&lt;li&gt;
Goals: Pages that you want the visitor to reach. For an e-commerce site, this would be the confirmation after a successful purchase. For a blog, it might be the &quot;signup for an account&quot; page.
&lt;/li&gt;
&lt;li&gt;
Funnels: A chain of pages that a user would traverse to &lt;em&gt;reach&lt;/em&gt; a goal. This will allow you to see the &quot;drop off&quot; rate at different parts of the process. How many users balk at using the checkout confirmation screen, the shipping screen, the credit card page, etc. By understanding where you lose people, you can make adjustments to improve that step.
&lt;/li&gt;
&lt;li&gt;
Filters: Allows for the exclusion of various elements from the reports (such as your companies IP range, to avoid inflation from internal user views).
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;This is information that &lt;em&gt;can&lt;/em&gt; be gleaned from traditional tools, but the slick implementation really shines due to the goals and funnels implementation. These two tools give a simpler and yet more detailed explanation of user flow than I have seen before, and it is free.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Tue, 22 Nov 2005 21:16:32 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">56 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>FireDaemon</title>
 <link>http://www.timewarptechnologies.com/node/54</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;From time to time it is nice to be able to run a program as a service. If you have written the software yourself (especially with .NET) creating services isn&#039;t actually all that hard. However, for third party programs or as a quick fix, nothing beats FireDaemon. FireDaemon is a low cost solution that can convert nearly any executable into a daemon. Simply specify the executable to run in the background and set the startup properties, security account, command line, etc and you are off. This is &#039;&#039;particularly&#039;&#039; useful for those programs that seem like they should have been background capable to start with, such as some backup tools that only operate while a user is logged in. Yes, these are usually inexpensive options and do what they were created to do quite well, but the ability to background them makes them even more useful. Iomega&#039;s backup tools or Second Copy 2000 both benefit from the ability to run backgrounded, and after doing so you wonder why they didn&#039;t in the first place.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Tue, 11 Oct 2005 22:55:10 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">54 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>LLBLGen</title>
 <link>http://www.timewarptechnologies.com/LLBLGen</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;If you are coding .NET projects, one of features I miss most compared to Java is a good Object Relational Mapping tool. Java programmers for years have been using tools such as Hibernate to help with persistence code, but for .NET the tools have been fairly lacking. Although there are products such as NHibernate, they are still not as mature as the Java versions. Commercial offerings are usually very expensive. Recently I tried LLBLGen and found it to be a great solution with good support and a good feature set. On top of that, the price makes it a no-brainer option for even an independent developer working on a budget.&lt;/p&gt;
&lt;p&gt;When installing the product, copy your license file into the program’s main directory (by default “C:\Program Files\Solutions Design\LLBLGen Pro”). When you generate code, you will need to reference your generated projects (&lt;name&gt; and &lt;name&gt;DBSpecific) and the SD.LLBLGen.Pro.ORMSupportClasses.NET&lt;dot net=&quot;&quot; version=&quot;&quot;&gt; library. Finally, in the DBSpecific Project, drag the DatabaseSpecific/App.Config to your application’s main project (or add the contents of that file if you already have an app.config). With all of these steps done, you can access all the features of the product. &lt;/dot&gt;&lt;/name&gt;&lt;/name&gt;&lt;/p&gt;
&lt;p&gt;Make sure you read the section on “concepts” as it makes clear the underlying theory behind the product, which is good to understand. I wish all documentation was this clear. I will extend this story as I dig deeper in to this wonderful product.&lt;/p&gt;
&lt;p&gt;A couple of changes to my normal pattern of table naming was required: normally I distinguish tables and view with tblName and viewName style prefixes. This product strips prefixes, so the preferred solution is to give table names singular names, and view plural names. This becomes important because to &quot;firehose&quot; data, you will want to use the TypedView. As an example, I have PolicyStatus and PolicyStatuses as the table and view respectively.&lt;/p&gt;
&lt;p&gt;One thing to watch for: if you save an entity and wish to continue using it, you must reload the entity after the save or the system will complain that your copy is &quot;out of synchronization&quot;.&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.adapter.SaveEntity(m_data);
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.adapter.FetchEntity(m_data); &lt;font color=&quot;#008000&quot;&gt;//Reload after saveA&lt;/font&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
Combo box value loads (and other firehose cursors) are best implemented by using a stored procedure and then loading from the result set (sorting provided by the procedure):&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
DataTable surplusLinesStatuses = RetrievalProcedures.RpSurplusLinesStatuses();
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.PolicySurplusLinesStatus.LoadTableItems(surplusLinesStatuses, &lt;font color=&quot;#c00000&quot;&gt;&quot;Status&quot;&lt;/font&gt;, &lt;font color=&quot;#c00000&quot;&gt;&quot;SurplusLinesStatusID&quot;&lt;/font&gt;);
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
Note that the order in which the combo box properties are set is important (this is found in the LoadTableItems method of my derived combo box object type):&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.BeginUpdate();
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.ValueMember = valueMember;
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.DisplayMember = displayMember; 
&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.DataSource = data; &lt;font color=&quot;#008000&quot;&gt;//Set the members *before* setting the source. 
&lt;/font&gt;&lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.EndUpdate();

&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
Note that if the data source was set *first* this would crash. This appears to be a .NET issue as it makes no sense to worry about order in which these are assigned. It only crashes if one of the items (usually the value member) ends up with a name containing ID at the end. &lt;/p&gt;
&lt;p&gt;Pulling a collection of data in is quite easy:&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
CompanyEntityFactory companyEntityFactory = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; CompanyEntityFactory();
EntityCollection companies = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; EntityCollection(companyEntityFactory); 
ISortExpression sorter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; SortExpression(
  SortClauseFactory.Create(CompanyFieldIndex.CompanyName, SortOperator.Ascending));
adapter.FetchEntityCollection(companies, &lt;font color=&quot;#0000FF&quot;&gt;null&lt;/font&gt;, 0, sorter);
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
The most complex part of this is the sort expression. We use factories to generate the objects that are relevant, and this takes some getting use to. Also, naming conventions are critical, each table is TablenameEntity and has TablenameFieldIndex and TablenameEntityFactor objects created. The use is illustrated above.&lt;/p&gt;
&lt;p&gt;A more complex example, here using another object to collect up the rows based on a relationship and with three sort filters:&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
CompanyEntity companyEntity = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; CompanyEntity(CompanyGUID);
adapter.FetchEntity(companyEntity);

ISortExpression sorter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; SortExpression(
  SortClauseFactory.Create(LocationFieldIndex.State, SortOperator.Ascending));
sorter.Add(
  SortClauseFactory.Create(LocationFieldIndex.City,  SortOperator.Ascending));
sorter.Add(
  SortClauseFactory.Create(LocationFieldIndex.Zip, SortOperator.Ascending));

EntityCollection locations = companyEntity.Location;
adapter.FetchEntityCollection(locations, companyEntity.GetRelationInfoLocation(), 0, sorter);
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
Note the companyEntity.GetRelationInfoLocation() that is passed: this is how we restrict the collection to *related* records. Passing null here pulls everything in the table (which is what you want sometimes, but not if you went to the trouble of pulling in a related record most times.&lt;/p&gt;
&lt;p&gt;Another action you may wish to perform is a direct update of data without loading the entity. Here is an example, updating a field without loading it (this is the equivelent of an &lt;code&gt;UPDATE Login SET PasswordHash = @NewPassword WHERE LoginGuid = @LoginGuid&lt;/code&gt; followed by the parameter sets, connection creation and execution).&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
LoginEntity loginEntity = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; LoginEntity();
loginEntity.LoginGuid = m_loginEntity.LoginGuid; &lt;font color=&quot;#008000&quot;&gt;//Update the current user
&lt;/font&gt;loginEntity.IsNew = &lt;font color=&quot;#0000FF&quot;&gt;false&lt;/font&gt;; &lt;font color=&quot;#008000&quot;&gt;//We exist in the DB already.
&lt;/font&gt;loginEntity.passhwordHash = &lt;font color=&quot;#0000FF&quot;&gt;this&lt;/font&gt;.newPassword.Text;
DataAccessAdapter adapter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; DataAccessAdapter();
adapter.SaveEntity(loginEntity);
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
When using &quot;self servicing&quot; mode files, this next point is moot. When using adapter to control data access more carefully (which is how I would recommend the product be used, despite the convenience of self servicing mode) letting the adapter know what related objects to bring along with is important to the performance of the program. In this example, we are pulling all of the policies that relate to an insured:&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; &quot;&gt;
&lt;pre&gt;
InsuredEntity insuredEntity = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; InsuredEntity(InsuredGUID);
adapter.FetchEntity(insuredEntity);

SortExpression sorter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; SortExpression(
    SortClauseFactory.Create(PolicyProspectFieldIndex.EffectiveDate, SortOperator.Ascending));

EntityCollection policies = insuredEntity.PolicyProspect;
PrefetchPath2 prefetchPath = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; PrefetchPath2((&lt;font color=&quot;#0000FF&quot;&gt;int&lt;/font&gt;)EntityType.PolicyProspectEntity);
prefetchPath.Add(PolicyProspectEntity.PrefetchPathPolicyType);
adapter.FetchEntityCollection(policies, insuredEntity.GetRelationInfoPolicyProspect(), 0, sorter, prefetchPath);

&lt;font color=&quot;#0000FF&quot;&gt;foreach&lt;/font&gt;(PolicyProspectEntity policyProspectEntity &lt;font color=&quot;#0000FF&quot;&gt;in&lt;/font&gt; policies)
{
...
}
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
He we see an entity relationship being used (the insured&#039;s policies can be found by filtering on &lt;code&gt;insuredEntity.GetRelationInfoPolicyProspect()&lt;/code&gt;. We are sorting these entries by a date field, again similar to prior examples. The novel piece of this code is the use of a prefetchPath, here constructed with the PolicyProspectEntity and then given the PolicyProspectEntity.PrefetchPolicyType as an related entity to load. Running the SQL Profiler while this runs shows the following queries executed:&lt;/p&gt;
&lt;pre&gt;
SELECT &lt;fieldlist&gt;
FROM Insured  
WHERE ( Insured.InsuredGUID = @InsuredGuid1)

SELECT &lt;fieldlist&gt;
FROM PolicyProspect  
WHERE ( PolicyProspect.InsuredGUID = @InsuredGuid1) 
ORDER BY PolicyProspect.EffectiveDate ASC

SELECT &lt;fieldlist&gt;
FROM PolicyType  
WHERE ( PolicyType.PolicyTypeID IN (
	SELECT PolicyProspect.PolicyTypeID AS PolicyTypeId 
	FROM PolicyProspect  
	WHERE ( PolicyProspect.InsuredGUID = @InsuredGuid1)))
&lt;/fieldlist&gt;&lt;/fieldlist&gt;&lt;/fieldlist&gt;&lt;/pre&gt;&lt;p&gt;
Note that this is potentially more efficient than the standard join query, where the same policy type information might be repeated over and over again in the result set. Here we see that only those policy types that are relevant are preloaded, and each will only be preloaded only once. Compare that to the equivalent SQL code:&lt;/p&gt;
&lt;pre&gt;
SELECT &lt;fieldlist&gt;
FROM Insured  
WHERE ( Insured.InsuredGUID = @InsuredGuid1)

select *
from PolicyProspect 
inner join PolicyType 
  on PolicyProspect.PolicyTypeID = PolicyType.PolicyTypeID
where PolicyProspect.InsuredGUID = @InsuredGUID
&lt;/fieldlist&gt;&lt;/pre&gt;&lt;p&gt;
The direct SQL only makes two round trips instead of three, but potentially repeats policy type information needlessly.&lt;/p&gt;
&lt;h1&gt;Some typical patterns:&lt;/h1&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; font-size: 12px; &quot;&gt;
&lt;pre&gt;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; System;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; ORM;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; ORM.DatabaseSpecific;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; ORM.EntityClasses;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; ORM.FactoryClasses;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; ORM.HelperClasses;
&lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; SD.LLBLGen.Pro.ORMSupportClasses;

&lt;font color=&quot;#0000FF&quot;&gt;namespace&lt;/font&gt; PolicyTracker.BL
{
    &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;class&lt;/font&gt; AgentManager
    {
        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; AgentEntity CreateAgent(Guid locationGuid)
        {
            AgentEntity entity = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; AgentEntity();
            entity.AgentGuid = Guid.NewGuid();
            entity.LocationGuid = locationGuid;
            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; entity;
        }
        
        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;string&lt;/font&gt; GetCaption(AgentEntity agent)
        {
            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; agent.AgentName;
        }

        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; AgentEntity GetAgent(Guid agentGuid)
        {
            AgentEntity agent = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; AgentEntity(agentGuid);

            &lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; (DataAccessAdapter adapter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; DataAccessAdapter())
                adapter.FetchEntity(agent);

            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; agent;
        }

        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; EntityCollection GetReassignTargets(AgentEntity sourceAgent)
        {
            LocationEntity sourceLocation = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; LocationEntity(sourceAgent.LocationGuid); &lt;font color=&quot;#008000&quot;&gt;//Agent to load
&lt;/font&gt;            PrefetchPath2 path = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; PrefetchPath2((&lt;font color=&quot;#0000FF&quot;&gt;int&lt;/font&gt;) EntityType.LocationEntity); &lt;font color=&quot;#008000&quot;&gt;//Get the location of the agent.
&lt;/font&gt;            path.Add(LocationEntity.PrefetchPathCompany).SubPath.Add(CompanyEntity.PrefetchPathLocation);

            &lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; (DataAccessAdapter adapter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; DataAccessAdapter())
            {
                adapter.FetchEntity(sourceLocation, path);
            }

            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; sourceLocation.Company.Location;
        }

        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;void&lt;/font&gt; Reassign(AgentEntity sourceAgent, LocationEntity targetLocation)
        {
            sourceAgent.LocationGuid = targetLocation.LocationGuid;

            &lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; (DataAccessAdapter adapter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; DataAccessAdapter())
            {
                adapter.SaveEntity(sourceAgent);
            }
        }

        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; EntityCollection GetInsureds(Guid agentGuid)
        {
            AgentEntity agentEntity = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; AgentEntity(agentGuid);
            DataAccessAdapter adapter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; DataAccessAdapter();
            adapter.FetchEntity(agentEntity);

            SortExpression sorter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; SortExpression(
                SortClauseFactory.Create(InsuredFieldIndex.Name, SortOperator.Ascending));

            EntityCollection insureds = agentEntity.Insured;
            adapter.FetchEntityCollection(insureds, agentEntity.GetRelationInfoInsured(), 0, sorter);

            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; insureds;
        }

        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; LocationEntity GetLocation(AgentEntity agent)
        {
            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; LocationManager.GetLocation(agent.LocationGuid);            
        }

        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; InsuredEntity CreateChildInsured(AgentEntity agent)
        {
            InsuredEntity newInsured = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; InsuredEntity();
            newInsured.InsuredGuid = Guid.NewGuid();
            newInsured.AgentGuid = agent.AgentGuid;
            &lt;font color=&quot;#0000FF&quot;&gt;return&lt;/font&gt; newInsured;
        }
    }
}
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;p&gt;
An example of an update performed against the data store (no loading of objects):&lt;/p&gt;
&lt;!-- Start of snippet generated by C#2HTML --&gt;&lt;div style=&quot;background-color: #FFFFFF; font-family: Courier; font-size: 12px; &quot;&gt;
&lt;pre&gt;
        &lt;font color=&quot;#008000&quot;&gt;/// &amp;lt;summary&amp;gt;
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// Moves the child Locations to the new parent Company
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// 
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// update Location 
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// set CompanyGuid = @targetGuid
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// where CompanyGuid = @sourceGuid
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// &amp;lt;/summary&amp;gt;
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// &amp;lt;param name=&quot;sourceGuid&quot;&amp;gt;Company to remove children from.&amp;lt;/param&amp;gt;
&lt;/font&gt;        &lt;font color=&quot;#008000&quot;&gt;/// &amp;lt;param name=&quot;targetGuid&quot;&amp;gt;Company to add children to.&amp;lt;/param&amp;gt;
&lt;/font&gt;        &lt;font color=&quot;#0000FF&quot;&gt;internal&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;static&lt;/font&gt; &lt;font color=&quot;#0000FF&quot;&gt;void&lt;/font&gt; MergeChildEntities(Guid sourceGuid, Guid targetGuid)
        {
            LocationEntity updateLocation = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; LocationEntity();
            updateLocation.CompanyGuid = targetGuid;

            RelationPredicateBucket filter = 
                &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; RelationPredicateBucket(LocationFields.CompanyGuid == sourceGuid);
            
            &lt;font color=&quot;#0000FF&quot;&gt;using&lt;/font&gt; (DataAccessAdapter adapter = &lt;font color=&quot;#0000FF&quot;&gt;new&lt;/font&gt; DataAccessAdapter())
            {
                adapter.UpdateEntitiesDirectly(updateLocation, filter);
            }            
        }

&lt;/pre&gt;&lt;/div&gt;
&lt;!-- End of snippet generated by C#2HTML --&gt;&lt;h1&gt;A note about migration between environments.&lt;/h1&gt;
&lt;p&gt;LLBLGen writes code that includes an explicit reference to the database being referenced. This is because the ORM can talk to multiple databases at once. However, this also introduces the requirement to be able to rewrite the database references on the fly: if your production or test environments have databases with different names, the config files must be updated to rewrite the &quot;source&quot; names to the new names.&lt;/p&gt;
&lt;p&gt;Adding   &lt;/p&gt;
&lt;p&gt;  &amp;lt;configSections&amp;gt;&lt;br /&gt;
    &amp;lt;section name=&quot;sqlServerCatalogNameOverwrites&quot; type=&quot;System.Configuration.NameValueSectionHandler&quot; /&amp;gt;&lt;br /&gt;
  &amp;lt;/configSections&amp;gt;&lt;/p&gt;
&lt;p&gt;  &amp;lt;sqlServerCatalogNameOverwrites&amp;gt;&lt;br /&gt;
    &amp;lt;add key=&quot;original_name&quot; value=&quot;new_name&quot; /&amp;gt;&lt;br /&gt;
  &amp;lt;/sqlServerCatalogNameOverwrites&amp;gt;&lt;/p&gt;
&lt;p&gt;to the .config file for the application will ensure the proper code is executed against the database. Simply changing your connection string will *not* have the desired effect unless the exact same names are used for the databases. There is also a schema rewriting section that works the same, just at the schema level.&lt;/p&gt;
&lt;p&gt;Update: an example of a sub-query. Here we are looking for the current record. If the effective date is in the future or is an older record, it is excluded.&lt;/p&gt;
&lt;pre&gt;
FieldCompareSetPredicate newFieldCompareSetPredicate = new FieldCompareSetPredicate(
	OnlineFeeTaxFields.EffectiveDate, //Defines the &quot;outer&quot; field.
	null, 
	OnlineFeeTaxFields.EffectiveDate.SetObjectAlias(&quot;ft&quot;), //Defines the &quot;inner&quot; query field. SetObjectAlias gives the table an alias, SetFieldAlias does the same for a field.
	null,
	SetOperator.Equal, //We are looking for equality.
	(
		OnlineFeeTaxFields.State == OnlineFeeTaxFields.State.SetObjectAlias(&quot;ft&quot;) &amp;amp; 
		OnlineFeeTaxFields.TransactionTypeId == OnlineFeeTaxFields.TransactionTypeId.SetObjectAlias(&quot;ft&quot;) &amp;amp; 
		OnlineFeeTaxFields.TransactionCompanyCode == OnlineFeeTaxFields.TransactionCompanyCode.SetObjectAlias(&quot;ft&quot;) &amp;amp;
		(
			OnlineFeeTaxFields.SecOrgId.SetObjectAlias(&quot;ft&quot;) == 4 | OnlineFeeTaxFields.SecOrgId.SetObjectAlias(&quot;ft&quot;) == -1)
	), //Above is a fairly complex &quot;predicate&quot; filtering the subquery
	null, //No relationship map
	&quot;&quot;, //No name from the relationship map
	1, //We want the &quot;top&quot; 1 record.
	new SortExpression(OnlineFeeTaxFields.EffectiveDate.SetObjectAlias(&quot;ft&quot;) | SortOperator.Descending)); //Sort descending so the largest is on top
&lt;/pre&gt;&lt;p&gt;
Here we have an example of *filtering* on a related table. Prefetch and filter are not directly related: we can filter on tables we don&#039;t fetch and fetch related tables we didn&#039;t filter on. Prefetched tables are filtered based on the relationships established in the database, so it isn&#039;t frequently a requirement to filter prefetched tables.&lt;/p&gt;
&lt;pre&gt;
        public static EntityCollection&lt;carrierentity&gt; GetCarriers(Guid programId)
        {
            EntityCollection&lt;carrierentity&gt; carriers = new EntityCollection&lt;carrierentity&gt;();
            PrefetchPath2 path = new PrefetchPath2(EntityType.CarrierEntity);
            path.Add(CarrierEntity.PrefetchPathCarrierProgram);
            RelationPredicateBucket bucket = new RelationPredicateBucket(
                CarrierProgramFields.ProgramGuid == programId);
            bucket.Relations.Add(CarrierEntity.Relations.CarrierProgramEntityUsingCarrierGuid);

            SortExpression sorter = new SortExpression(CarrierFields.CarrierName | SortOperator.Ascending);

            using (DataAccessAdapter adapter = new DataAccessAdapter())
                adapter.FetchEntityCollection(carriers, bucket, 0, sorter, path);

            return carriers;

        }
&lt;/carrierentity&gt;&lt;/carrierentity&gt;&lt;/carrierentity&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Fri, 07 Oct 2005 17:35:44 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">51 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>Small Business Backup Solutions</title>
 <link>http://www.timewarptechnologies.com/node/45</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;This is one of those questions that should be asked about &lt;em&gt;more&lt;/em&gt; often, &quot;How do I protect my data&quot;. Many small businesses take unnecessary risks with their livelihood by ignoring the most basic concept of computing: the value of your data. Since most small businesses are based on Windows platforms, the information here is tinged with Windows terminology, but the ideas are generic enough to use in most environments.&lt;/p&gt;
&lt;p&gt;Business owners understand the value of their data, otherwise there would be little need for a computer system. (Even if you only do word processing and a few spreadsheets, think of the difficulty of recreating that information. If you don&#039;t like that idea, then you need to create backups.) What is often not understood is the risk the data is placed in every day simply by using their computer system. Let us look at some common small business data environments to understand the risks and some ways to mitigate those risks. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Small company backups&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In a very small business there may be only a single machine (this is frequently the case in small retail establishments) that does everything. This machine is responsible for point of sale, accounting, word processing and perhaps even web browsing and recreational use. Thus a failure of this computer means a total loss of electronic data, and a lack of backups could be a disaster (reconstructing finances from paper receipts is time consuming and error prone). &lt;/p&gt;
&lt;p&gt;Now consider the fact that most small companies don&#039;t have IT technical assistance and so probably have an insecure environment that can be infected with malware, viruses and simply damaged by accidents such as deleting critical system folders or the data itself. Calls of the nature &quot;I just lost my hard drive: could you get my data for me?&quot; are a sadly all too common. The cost of such data recovery can be thousands of dollars if there are physical problems with the drive as the drive will need to be sent to a clean-room for recovery.&lt;/p&gt;
&lt;p&gt;On the other hand, most companies of this nature have very small total quantities of data (perhaps around 100MB). This means that most modern computers come with a handy backup solution built into them: the CD-R or DVD-R drive. Recordable CDs can hold 650-750 MB of data or more and DVDs hold 4300 MB of data. Both types of media are available in bulk, CD-R costs around 12 cents each and DVD-R around 25 cents, at the time of this writing. Many of the drives come with backup utilities or &quot;packet mode&quot; writers (which allow you to treat the drive as a standard drive), both of which can be used effectively to back up your data. (If a machine doesn&#039;t come with a recordable drive, they are easily added for little cost).&lt;/p&gt;
&lt;p&gt;With backup software the process can be automated so all that is necessary is place a fresh disk in the drive at the end of the day and allow the scheduled backup to copy your data to the disk. If you are using the packet mode writing feature, dragging and dropping your files on the disk will suffice. But which files should be backed up?&lt;/p&gt;
&lt;p&gt;To make your backups as quick and easy as possible, it is best if you can organize your files into a single folder. Folders under this main location can be used to organize data for easy navigation. For a single computer user, this can be as simple as ensuring all documents reside under &quot;My Documents&quot; and backing that up. Some software may by default save files to other locations (such as the program directory itself) in which case it is best to check to see if the defaults can be changed.&lt;/p&gt;
&lt;p&gt;One thing to keep in mind is that the files should not be open at the time the backup is made as most backup software will skip open files, which defeats the purpose of backing them up in the first place. When backing up databases this can be complex, as some database software runs as a &quot;service&quot; and the files are open as long as the service is running. Consult the product documentation for recommended methods of backing up any software that uses databases storage. Often a workable solution is to use the product&#039;s own backup tool to create a backup &lt;em&gt;file&lt;/em&gt; in your main folders, which will in turn be backed up when you execute your normal backup procedures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rewritable and off site&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is important to take your backup media off-site to avoid loss of your information due to loss of the building itself. For small companies this can simply mean taking the recorded backups home with the owner, or better yet to a safe deposit box. Note that any backup media is temperature sensitive. Leaving it in extreme temperatures may destroy the usefulness of the backup, so take care during transport and storage that such extreme temperatures are not a potential problem. Also note that it is a good idea to &lt;em&gt;test&lt;/em&gt; the recovery procedures from time to time. Bring an off-site stored disk back on site and verify that you can actually recover data successfully. Failure to test backup restoration is another all too common source of heartache, and if the first time you test the restoration is when you need it, a failure is no longer just a nuisance.&lt;/p&gt;
&lt;p&gt;An added advantage to using CD-R and DVD-R media is that in the case of an audit you will have frequent, reliable snapshots of the state of the business. Data written to -R products is not alterable: the packet mode drivers that allow overwriting of data simply write new data and refer to it by default, but the old data remains on the disk.&lt;/p&gt;
&lt;p&gt;However, it is wise to also consider the rewritable versions of the media you use: a couple of packages of rewritable disks makes it an option to run a daily backup Monday - Thursday (for example) on rewritable disks and on Friday on a write once media which is stored off site and acts as your archival media. Write once media has a longer shelf life and because of this rewritable media should &lt;em&gt;not&lt;/em&gt; be your only optical backups. Over time the reuse of the rewritables will keep storage and costs down. Make sure to note the number of rewrites the media is rated for and use only a portion of those rewrites before replacing the media with fresh disks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network backups&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A slightly more complex backup situation is the small network. Two or more computers may be in use and backing up &lt;em&gt;all&lt;/em&gt; the data is just as important as it was with a single machine. For very small networks the easiest solution can be to use a single machine as the &quot;server&quot; for the other machines. This means you still store all of your data in a single folder structure, which is now shared among the users. It used to be that such arrangements were hazardous because of the stability problems with Windows 98 and earlier, but from Windows 2000 forward, the professional editions have been excellent choices for a small business to operate a peer to peer network with a central storage machine. In even larger companies (more than five machines) or for those who want the extra assurance of stability, purchasing a low end server machine that acts as a central storage machine is an excellent investment.&lt;/p&gt;
&lt;p&gt;In either of these cases you may still be able to perform backups exactly as outline above, albeit the amount of data will increase. With DVD-R it is quite reasonable for a small company to still be able to backup to a single disk.&lt;/p&gt;
&lt;p&gt;Another option (and one that can be quite effective) is to allow each of the users to store data locally, but to copy it to a central location (peer server or full server) periodically. This can be done as simply as a scheduled batch file running the &quot;xcopy&quot; command, via software designed for this purpose (such as Second Copy 2000) or by backup software that can target a file share for storage. This central location can then be backed up to recordable media, and can act as &quot;first stop&quot; for any data recovery. Specialized programs like Second Copy 2000 can be configured to maintain multiple versions of files, such as the last five revisions of a file in an archive, protecting against accidental changes being saved and overwriting legitimate data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Larger requirements&lt;/strong&gt;&lt;br /&gt;
Of course, eventually your data may exceed the capacity of even DVD-R, in which case a more traditional backup solution may be required. Another article will be posted covering medium weight backup solutions in the near future. Some may be wondering why tape backup has not been mentioned: that is because for the light weight needs of very small business, tape can be more expensive and complex than the simple solution outlined here. The article on medium weight backup solutions will consider tape&#039;s pros and cons more fully.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Wed, 17 Aug 2005 17:56:52 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">45 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>CutePDF</title>
 <link>http://www.timewarptechnologies.com/node/48</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Many people need to generate PDF files occasionally, but it can be an expensive proposition to get the latest version of Adobe Acrobat for every user in your office to create basic PDF files. Acrobat comes with many wonderful features for create sophisticated PDF files. Digital signing, encryption and forms that post to web servers are among a few that Timewarp Technologies uses Acrobat for. However, when all you need is get some pages from some unusual program and provide the results to a user without the ability to read the source or resulting files, PDF is a great solution. For that matter, if the end consumer should not be modifying the document, I highly recommend sending PDF instead of Word documents as they are safer and more robust.&lt;/p&gt;
&lt;p&gt;Enter CutePDF Writer: &lt;/p&gt;
&lt;p&gt;http://www.cutepdf.com/Products/CutePDF/writer.asp&lt;/p&gt;
&lt;p&gt;This handy program will allow you to &quot;print&quot; to the CutePDF printer and save the result as a PDF you can then send along to your end consumer. Simple, powerful and best of all, free.&lt;/p&gt;
&lt;p&gt;Free? Why free? Well, CutePDF uses Ghostscript, an open source (and itself free) postscript manipulation program as the conversion tool. The company provides more powerful tools that build upon CutePDF which are all quite affordable as well, but the print to PDF functionality remains a free product that does everything the average user will ever need. If you do need additional functionality, try the companies affordable CutePDF product: &lt;/p&gt;
&lt;p&gt;http://www.cutepdf.com/Products/CutePDF/plus.asp&lt;/p&gt;
&lt;p&gt;Both are highly recommended. Of course, if you need the most advanced PDF features only Acrobat will do:&lt;/p&gt;
&lt;p&gt;http://www.adobe.com/products/acrobatpro/main.html&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Wed, 27 Jul 2005 18:26:42 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">48 at http://www.timewarptechnologies.com</guid>
</item>
<item>
 <title>Why is NAT good for you?</title>
 <link>http://www.timewarptechnologies.com/node/39</link>
 <description>&lt;div class=&quot;field field-name-body field-type-text-with-summary field-label-hidden&quot;&gt;&lt;div class=&quot;field-items&quot;&gt;&lt;div class=&quot;field-item even&quot;&gt;&lt;p&gt;Recent articles have pointed to a 50% chance that an &quot;unprotected Windows PC&quot; will be infected within 12 minutes of being connected to the Internet. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.globetechnology.com/servlet/story/RTGAM.20050704.gtvirusjul4/BNStory/Technology/&quot; target=&quot;window2&quot;&gt;Globe and Mail article on exploits of Windows Systems&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What this means is that if a machine is installed with Windows and then goes online to obtain the necessary security updates, it is probably exploited before completing the update process, with all the negative consequences thereof. Windows XP with Service Pack 2 is already partially &quot;hardened&quot; compared to prior versions of Windows, which makes a most compelling argument for updating to the newer operating system if it is possible when it becomes necessary to reinstall windows. However, it is often necessary to reinstall Windows due to some calamity that befell the operating system, and such reinstalls usually will be with the existing operating system the machine is licensed for. If you only have 12 minutes before having a 50/50 chance of being exploited, how in the world can you safely update it?&lt;/p&gt;
&lt;p&gt;One way is to use &quot;slipstream&quot; updates or manually installing updates that were obtained by other means, but this is a complex and confusing undertaking for the average computer user. Is there a simpler way?&lt;/p&gt;
&lt;p&gt;It turns out there is: use a NAT capable router. Almost all &quot;broadband&quot; routers come with NAT or Network Address Translation. NAT isn&#039;t a &quot;true&quot; firewall, but it eliminates nearly all attack vectors that Windows is normally exposed to during the update process. In addition, they are incredibility easy to set up, which can&#039;t be said of &quot;true&quot; firewalls. (On the other hand, if you many machines or special security needs, NAT can&#039;t replace the capabilities of the true firewall products, so this advice is mostly for those with home computers.)&lt;/p&gt;
&lt;p&gt;If you have a machine behind a NAT router (or even better, one a true firewall), you can safely use the Windows update service to bring your machine up to date and secure it against the known vulnerabilities. What&#039;s better is that the NAT functionality will protect against the &quot;remote exploit&quot; vulnerabilities that come to light in the future.&lt;/p&gt;
&lt;p&gt;A NAT capable broadband router should cost $50-$100 for a 4 port version, which gives the additional benefit of allowing more than one machine to share a single broadband connection. It is a small investment when you consider the cleanup costs of an exploited machine.&lt;/p&gt;
&lt;p&gt;Note that a NAT capable device or a firewall is not a magical solution... it will &lt;em&gt;not&lt;/em&gt; protect against viruses, Trojans and malware that arrive from websites that you download programs from (intentinally or unintentionally) or e-mail. The best defense is a layered defense, which means a NAT capable device or firewall, a &quot;software&quot; firewall (a limited one is provided in Windows XP Service Pack 2) and an anti-virus program. The more layers of defense, the less likely any one failing will cause your machine to be exploited. Note though that third party software firewalls and anti-virus programs must be kept up to date to remain effective and not become a potential path of exploitation themselves. These products will be discussed in a later article.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://froogle.google.com/froogle?q=broadband+router&amp;amp;hl=en&amp;amp;lr=lang_en&quot; target=&quot;window2&quot;&gt;Froogle Search on &quot;Broadband Router&quot;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above clearfix&quot;&gt;&lt;div class=&quot;field-label&quot;&gt;Category: &lt;/div&gt;&lt;ul class=&quot;links&quot;&gt;&lt;li class=&quot;taxonomy-term-reference-0&quot;&gt;&lt;a href=&quot;/taxonomy/term/1&quot;&gt;Hints and Tips&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</description>
 <pubDate>Tue, 05 Jul 2005 20:23:10 +0000</pubDate>
 <dc:creator>jlopez</dc:creator>
 <guid isPermaLink="false">39 at http://www.timewarptechnologies.com</guid>
</item>
</channel>
</rss>
