Load template data
-
EF Code First and Data Scaffolding with the ASP.NET MVC 3 Tools Update
[Programming, RIA (Rich Internet Apps)] (ScottGu's Blog)Earlier this week I blogged about the new ASP.NET MVC 3 Tools Update that we shipped last month. In today’s blog post I’m going to go into more detail about two of the cool new features it brings: Built-in support for EF 4.1 (which includes the new EF “code-first” support) Built-in data scaffolding support within Visual Studio (which enables you to rapidly create data-driven sites) These two features provide a really sweet, and extremely powerful, way to work w ...
Earlier this week I blogged about the new ASP.NET MVC 3 Tools Update that we shipped last month.
In today’s blog post I’m going to go into more detail about two of the cool new features it brings:
- Built-in support for EF 4.1 (which includes the new EF “code-first” support)
- Built-in data scaffolding support within Visual Studio (which enables you to rapidly create data-driven sites)
These two features provide a really sweet, and extremely powerful, way to work with data and build data-driven web applications.
Scenario We’ll Build
To help illustrate how to use the above features, we’ll walkthrough building a simple data-drive site. It will support listing products:
As well as creating/editing new products (and categories):
We can now build this entire application (and create the database that backs it) with ASP.NET MVC 3 in only a minute or two.
Step 1: Create New Project
We’ll begin by creating a new ASP.NET MVC 3 project (using the File->New Project menu command). We’ll use the “Internet Project” template – which will provide us with a default starter template for our application.
When you look at the newly created project within the Solution Explorer, you’ll see that the ASP.NET MVC 3 Tools update adds a new assembly – EntityFramework – to our ASP.NET MVC 3 projects:
The EntityFramework assembly implements Entity Framework 4.1 – which we also released last month. EF 4.1 brings with it a bunch of great data API improvements to .NET – including the new “code first” option. EF Code First provides a really elegant and clean way to work with data, and enables you to do so without requiring a designer or XML mapping file. You can now easily take advantage of this by default with all new ASP.NET MVC 3 projects.
We’ll be using the EF Code First approach to implement our data access for this application.
Step 2: Implement Model Classes
Our first step will be to create two classes – Product and Category – that we’ll use to define the “model” of our application. We’ll implement these as standard “plain old C# objects” within the \Models folder of our project using the code below:
Notice how the above classes are just standard classes with .NET data-types. They do not need to derive from any base class, nor implement any interfaces.
In addition to standard scalar properties, each class has an “association” property. For example, the “Product” class has a “Category” property that enables a developer to get access to the Category it belongs to. Likewise the “Category” class has a “Products” property that enables a developer to retrieve the Products that are within that particular Category. EF Code First can handle automatically managing these associations (using the Primary Key/Foreign Key values), and can lazy load the appropriate data on-demand.
Step 3: Implement a StoreContext Class using EF Code First
Now that we’ve defined our two model classes, our next step will be to implement a “DbContext” class using EF code First that will map the model classes to tables in a database. We’ll implement this using the code below:
The “StoreContext” class above is the DbContext class we are using to map our Product and Category classes to/from a database. It derives from the DbContext base class provided by EF Code First, and exposes two properties that correspond to tables within our database. For this sample we are using the default “convention over configuration” based mapping rules to define how the classes should map to/from the database. This means that the Products property will map to a Products table, and the Categories property will map to a Categories table. In later blog posts I’ll talk about how you can optionally override this and perform custom mappings.
You can add the above class anywhere within your solution. For example, you could add it within the \Models folder of your project – or within a separate class library project if you prefer to keep your data code separate. You’ll want to add a “using” statement to the System.Data.Entity namespace at the top of the class file (in order to reference the DbContext and DbSet<T> classes).
Step 4: Scaffold a Categories Controller
We have everything we need to model our data and save/retrieve it from a database. Now let’s create an ASP.NET MVC Controller class that will implement the support necessary to display our Category data and enable creating/editing/deleting Category objects. Prior to last month’s ASP.NET MVC 3 Tools Update, you would have had to manually write a Controller class to do this (and implement all of the EF data access code yourself). The ASP.NET MVC 3 Tools Update now includes built-in “scaffolding” support that can help do this for you automatically.
To scaffold a new Categories controller class, we’ll right-click on the “/Controllers” folder of our project and choose the Add->Controller context menu:
Selecting Add->Controller will bring up the “Add Controller” dialog. We’ll name the controller class we want to create “CategoriesController”. Starting with last month’s ASP.NET MVC 3 Tools Update, the “Add Controller” dialog also now supports a new “Scaffolding options” section – which enables you to choose to “scaffold” the new controller using a variety of built-in templates:
We’ll choose the “Controller with read/write actions and views, using Entity Framework” template – which will create a Controller class that has all of the EF code necessary to create, update, list and delete model objects.
After selecting this template, we can choose the model class we want our Controller to implement CRUD support for – in this case the “Category” class. We can also select the EF DbContext/ObjectContext class that we want to use to access the database – in this case the “StoreContext” class we defined earlier:
When we click the “Add” button, Visual Studio will automatically create a CategoriesController class for us – with appropriate Create, Edit, Details, Delete and Index action methods inside it – as well associated view templates for each of the actions:
If you open the CategoriesController.cs class you’ll find that the action methods within it implement the EF data access code necessary for CRUD support:
And now we have all of the code we need to implement data listing/editing for Categories within our database
Step 5: Configuring our Database Location
The last step we’ll do before running our application is to point our application at the database we want to use. EF code first allows you to use an existing database you already have defined, or alternatively you can point it at a database that doesn’t yet exist – in which case it will automatically create it for you using the model classes you’ve defined.
No Connection-String
By default, you actually don’t need to configure a connection-string to point at a database. If you don’t specify one, EF Code-First will by default create a new SQL Express database whose name matches your DbContext class name (for example: above it would create a ScaffoldingDemo.Models.StoreContext database within your local .\SQLExpress database instance).
Explicitly Specifying a Connection-String
You can alternatively explicitly specify where you want your database to live by adding a connection-string to your application’s web.config file. You’ll want the name of the connection-string to match the DbContext class name. For example – in our application above our DbContext class was named “StoreContext”, and so by default EF will look for a connection-string with the same name to determine the database location.
Below is the entry we’d add to our web.config file if we wanted to use an embedded SQL CE database named “Store.sdf” within our application’s \App_data folder:
<configuration>
<connectionStrings>
<add name="StoreContext"
connectionString="Data Source=|DataDirectory|\Store.sdf"
providerName="System.Data.SqlServerCe.4.0" />
</connectionStrings></configuration>
You can learn more about SQL CE – and the new VS 2010 SP1 tooling support for it – from my previous blog post about it.
Step 6: Run the Application
Ok – let’s now run the application and navigate to the /Categories URL. This will execute our CategoriesController’s Index() method – which will list all categories in our database. Currently we don’t have any:
Let’s click the “Create New” link to create a new category – this will navigate to the /Categories/Create URL, which provides us with a form to create a new category:
Clicking the “Create” button will post the form to our CategoriesController and add a new Category to our database. We can repeat this a few times to add several different categories:
The cool thing is that we didn’t have to write any Controller or View code to enable this – the scaffolding support automatically generated the code we needed to enable this. This provides a really easy way to get started. We can then go in and tweak it as necessary to customize the functionality further.
Step 7: Looking at the database
We configured our application to use a connection-string that pointed to a SQL CE database that (at the time) didn’t exist. When we ran our application, EF 4.1 detected that the database didn’t exist and automatically created it for us – based on the Product and Category classes we defined earlier.
To see the database, click the “Show All Files” icon at the top of Visual Studio’s “Solution Explorer” and add the “Store.sdf” database that was automatically created by EF to your project:
You can then double-click the database to open it up in the Server Explorer – and expand the tables within it to see the schema that was created:
Notice above how EF Code First automatically created the appropriate database schema based on the Category and Product classes we created earlier. It also automatically setup Primary Key/Foreign-Key relationships between the two tables based on the association properties specified in the classes.
Step 8: Scaffolding a Products Controller
We’ve created a CategoriesController class to manage listing and editing Categories. Let’s now create a ProductsController class to manage listing and editing Products.
Just like before, we’ll right-click on the \Controllers folder in our project and choose the Add->Controller context menu. We’ll name the Controller class we want to create “ProductsController” and indicate that we want to scaffold using the EF template with a Product model class:
Clicking the “Add” button will scaffold a ProductsController class for us, with appropriate action methods for CRUD scenarios, and create the associated view templates that go with them:
We can then run our project again and navigate to the /Products URL within it:
Let’s click the “Create New” link to create a new Product:
One cool thing to notice is that the “Category” is a drop-down of valid Categories (and not just a textbox with a foreign-key integer value):
The scaffolding support within Visual Studio was smart enough to detect that Category was an association, and scaffold EF code that populates the drop-down with valid Categories.
After entering several products our /Products URL will display the data like below:
Notice again how Category is displayed using the friendly name – and not just the foreign-key value.
Learning More
The above walkthrough provided a quick look at the new EF code-first and data scaffolding support built-into the ASP.NET MVC 3 Tools Update, and how you can use them to easily and quickly jumpstart application development. EF Code First enables you to write incredibly clean data access code. The data scaffolding support enables you to rapidly create data UI – and then allows you to go in and easily tweak/customize it further.
In addition to working with EF Code-First, you can also use the data scaffolding support against standard EF ObjectContext objects (built using either the database-first or model-first approach and the EF WYSIWYG designer).
Scaffolding
You can learn more about the new ASP.NET MVC 3 Scaffolding Support from Steve Sanderson’s excellent talk about it at MIX:
- Scaffolding – ASP.NET, NuGet, MVCScaffolding – Steve Sanderon’s MIX talk about it
In his talk Steve also covers how you can use the scaffolding support from the command-line (using NuGet) as well as how you can download and use additional scaffolding templates (for example: ones that can automatically implement a repository pattern, etc). Also make sure you bookmark Steve’s excellent blog series about scaffolding (and its extensibility) here:
- Scaffolding with ASP.NET MVC 3 – Excellent blog series about ASP.NET MVC 3 Scaffolding
EF Code First
I’ll be doing more blog posts about EF Code First in the future. Below are links to some tutorials I’ve written in the past about it:
- Code First Development with Entity Framework 4.x
- EF Code First: Custom Database Schema Mapping
- Using EF Code First with an Existing Database
Note: The above tutorials were written against the CTP4 release of EF Code First (and so some APIs are a little different) – but the concepts and scenarios outlined in them are the same as with the final release.
ASP.NET MVC 3
If you want to learn more about ASP.NET MVC 3, I highly recommend Scott Hanselman’s excellent video presentations at the Dutch DevDays and at MIX:
- ASP.NET MVC 3 101 – From Beginning to Advanced
- Overview of the MS Web Stack of Love – Scott’s talk at MIX
Also check out all of the new content and videos on the http://asp.net/mvc web-site:
Summary
The new ASP.NET MVC 3 Tooling Update is full of goodness. If you haven’t already downloaded and installed it I highly recommend you do so. Stay tuned for even more blog posts about it!
Hope this helps,
Scott
P.S. I am also now using Twitter for quick updates and to share links. Follow me at: twitter.com/scottgu
-
Step-by-Step Using ImplicitDataType in Silverlight 5 Beta
[RIA (Rich Internet Apps)] (SilverlightShow: Silverlight Community)In this blog post, Kunal Chowdhury discusses using ImplicitDataType in Silverlight 5 and gives an example. Source: Kunal's Blog ImplicitDataType is a new feature in Silverlight 5. Using ImplicitDataType, you can declare multiple Data Templates for your control and based on the data type, you can load the proper data template automatically. In this article we will discuss on the same step by step with a good example. Read the complete article to know it in depth.
In this blog post, Kunal Chowdhury discusses using ImplicitDataType in Silverlight 5 and gives an example.
Source: Kunal's Blog
ImplicitDataType is a new feature in Silverlight 5. Using ImplicitDataType, you can declare multiple Data Templates for your control and based on the data type, you can load the proper data template automatically.
In this article we will discuss on the same step by step with a good example. Read the complete article to know it in depth.
-
Blog Post: WinDbg File Association and Explorer Context Menu
[Microsoft] (Site Home)For a long time now I've had a registry file to make context menu entries for WinDbg. The entries allow you to select the x86 or x64 debugger. Internally at Microsoft, I have another version of the registry file that contains two more context menu entries for the private symbol server. You can see all 4 options I add internally in this screenshot. Note, the registry file assumes WinDBG is installed in c:\debuggers_x86 and c:\debuggers (for the x86 and AMD64 debuggers respectively). FYI ...
For a long time now I've had a registry file to make context menu entries for WinDbg. The entries allow you to select the x86 or x64 debugger. Internally at Microsoft, I have another version of the registry file that contains two more context menu entries for the private symbol server. You can see all 4 options I add internally in this screenshot.
Note, the registry file assumes WinDBG is installed in c:\debuggers_x86 and c:\debuggers (for the x86 and AMD64 debuggers respectively). FYI: The reason you need to install the debugger twice is the lack of architecture agnostic support in some extensions - yes, SOS.DLL I'm looking at you. The multiple entries give you a quick way of jumping between the two architectures.
The registry entries pass in the symbol path for the Microsoft Public Symbol Server (http://msdl.microsoft.com/download/symbols) and for me, the Microsoft Private Symbol Server. In both cases, the symbols are cached locally (e.g. c:\symbols) through SRV* chaining. Much like the architecture entries, this technique gives you a quick way to toggle between (in my case) the public and private symbols. The passing of the symbol path, as opposed to using the _NT_SYMBOL_PATH environment variable is the only way to get around the concatination that WinDBG does. If you have the environment variable set, it will be prepended to the symbol path passed. This means that a environment variable path could still be used instead of a passed path. I'll admit that this is only an issue for people needing support of both symbols stores and that for the vast majority of people, the environment variable is a better way to go. If you go this way, just remove the -y section of the command line.
The registry file makes an association for all of the dump variants, including reflected dumps (*.ini). Reflected dumps can be made using ProcDump with the -r switch (e.g. procdump.exe -r -ma notepad.exe). Currently, I piggy back on the Visual Studio 2010 keys so as to get an icon, and still support opening dumps in Visual Studio via the Open entry (I have no idea why you would want to do that though).
The other thing I do is load the debugger extensions that I commonly use. In this (example) registry file, I just include one of my own extensions (myext.dll) via the -a switch (the extension I made for the MSDN Magazine Debugging API series). If you want to load more, you just include multiple -a myext.dll entries in the command line. (Side note, if you are doing the same with cdb, you can't have a space between -a and the dll name). In my personal version of this, I load six extensions.
One thing you may want to consider is adding the -WX switch to ignore the default workspace. I personally set up the default workspace the way I like it (command window docked, WinDbg maximized on my main monitor, font colors set for each text type, etc.) and use this as a template for each new debug session. As such, I don't want the -WX switch (that's a double negative) as I want the workspace to apply. If you however want to use the 'out of box' defaults always, add the -WX switch.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.dmp]
@="VisualStudio.dmp.10.0"[HKEY_CLASSES_ROOT\.hdmp]
@="VisualStudio.dmp.10.0"[HKEY_CLASSES_ROOT\.mdmp]
@="VisualStudio.dmp.10.0"[HKEY_CLASSES_ROOT\.kdmp]
@="VisualStudio.dmp.10.0"[HKEY_CLASSES_ROOT\.ini]
@="VisualStudio.dmp.10.0"[HKEY_CLASSES_ROOT\VisualStudio.dmp.10.0\shell]
@="WinDbg_x64_public"[HKEY_CLASSES_ROOT\VisualStudio.dmp.10.0\shell\WinDbg_x86_public]
@="Open with WinDbg x86 (Public)"[HKEY_CLASSES_ROOT\VisualStudio.dmp.10.0\shell\WinDbg_x86_public\Command]
@="\"C:\Debuggers_x86\windbg.exe\" -z \"%1\" -a myext.dll -y \"SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols\""[HKEY_CLASSES_ROOT\VisualStudio.dmp.10.0\shell\WinDbg_x64_public]
@="Open with WinDbg x64 (Public)"[HKEY_CLASSES_ROOT\VisualStudio.dmp.10.0\shell\WinDbg_x64_public\Command]
@="\"C:\Debuggers\windbg.exe\" -z \"%1\" -a myext.dll -y \"SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols\""WinDbg Support
I've always known that WinDbg had a -I (that's a capital i) switch to set it as the (automatic) postmortem debugger. This follows in the Dr.Watson footsteps that has the same option. Of interest here, you can do this association twice on 64bit versions of Windows (running -I on the x86 and x64 debugger versions of WinDbg). By setting it twice, the x64 debugger is used for x64 failures (via the HKLM AeDebug key) and the x86 debugger is used for x86 failures (via the HKLM WOW64 AeDebug key). (Handy for those architecture agnostic extension issues.)
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
- HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug
Note, there is no built-in way to revert the -I setting to the WER default. Maarten van de Bospoort has posted about this previously. You need to resort to a backup of the keys (i.e. export them prior to the running of WinDbg -I).
So what happened today to prompt me to write this post?
Today, I discovered that WinDbg can do file association too! WinDbg.exe supports a -IA switch to register the file associations. Both the -I and -IA options are both listed in the Debugger.chm file for the WinDbg command line topic; but the description is a little brief for -IA.
So as to find out what -IA actually does, I recorded it's execution with ProcMon. Running WinDbg.exe -IA elevated from c:\debuggers produces the following screenshot and ProcMon log file (lots of filtering has been applied).
8:21:12.7253212 PM windbg.exe 7140 RegOpenKey HKCR\.dmp SUCCESS Desired Access: Read, Maximum Allowed
8:21:12.7254430 PM windbg.exe 7140 RegCreateKey HKCR\.dmp SUCCESS Desired Access: All Access
8:21:12.7255690 PM windbg.exe 7140 RegQueryKey HKCR\.dmp SUCCESS Query: Name
8:21:12.7256082 PM windbg.exe 7140 RegQueryKey HKCR\.dmp SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7257391 PM windbg.exe 7140 RegSetValue HKCR\.dmp\(Default) SUCCESS Type: REG_SZ, Length: 36, Data: WinDbg.DumpFile.1
8:21:12.7265370 PM windbg.exe 7140 RegOpenKey HKCR\.hdmp SUCCESS Desired Access: Read, Maximum Allowed
8:21:12.7266420 PM windbg.exe 7140 RegCreateKey HKCR\.hdmp SUCCESS Desired Access: All Access
8:21:12.7275875 PM windbg.exe 7140 RegQueryKey HKCR\.hdmp SUCCESS Query: Name
8:21:12.7276288 PM windbg.exe 7140 RegQueryKey HKCR\.hdmp SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7277548 PM windbg.exe 7140 RegSetValue HKCR\.hdmp\(Default) SUCCESS Type: REG_SZ, Length: 36, Data: WinDbg.DumpFile.1
8:21:12.7282217 PM windbg.exe 7140 RegOpenKey HKCR\.mdmp SUCCESS Desired Access: Read, Maximum Allowed
8:21:12.7283812 PM windbg.exe 7140 RegCreateKey HKCR\.mdmp SUCCESS Desired Access: All Access
8:21:12.7284834 PM windbg.exe 7140 RegQueryKey HKCR\.mdmp SUCCESS Query: Name
8:21:12.7285233 PM windbg.exe 7140 RegQueryKey HKCR\.mdmp SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7286521 PM windbg.exe 7140 RegSetValue HKCR\.mdmp\(Default) SUCCESS Type: REG_SZ, Length: 36, Data: WinDbg.DumpFile.1
8:21:12.7291973 PM windbg.exe 7140 RegCreateKey HKCR\.kdmp SUCCESS Desired Access: All Access
8:21:12.7307581 PM windbg.exe 7140 RegQueryKey HKCR\.kdmp SUCCESS Query: Name
8:21:12.7307980 PM windbg.exe 7140 RegQueryKey HKCR\.kdmp SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7309261 PM windbg.exe 7140 RegSetValue HKCR\.kdmp\(Default) SUCCESS Type: REG_SZ, Length: 36, Data: WinDbg.DumpFile.1
8:21:12.7332358 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.DumpFile.1 SUCCESS Desired Access: All Access
8:21:12.7349016 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1 SUCCESS Query: Name
8:21:12.7349436 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1 SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7350913 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.DumpFile.1\(Default) SUCCESS Type: REG_SZ, Length: 58, Data: WinDbg Post-Mortem Dump File
8:21:12.7373303 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1 SUCCESS Query: Name
8:21:12.7373793 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1 SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7377440 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.DumpFile.1\DefaultIcon SUCCESS Desired Access: All Access
8:21:12.7388456 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\DefaultIcon SUCCESS Query: Name
8:21:12.7388848 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\DefaultIcon SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7390150 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.DumpFile.1\DefaultIcon\(Default) SUCCESS Type: REG_SZ, Length: 64, Data: "C:\debuggers\windbg.exe",-3002
8:21:12.7467553 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1 SUCCESS Query: Name
8:21:12.7474203 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1 SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7477828 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.DumpFile.1\shell SUCCESS Desired Access: All Access
8:21:12.7511046 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell SUCCESS Query: Name
8:21:12.7511515 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7513244 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.DumpFile.1\shell\(Default) SUCCESS Type: REG_SZ, Length: 10, Data: Open
8:21:12.7540113 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell SUCCESS Query: Name
8:21:12.7540540 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7544075 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.DumpFile.1\shell\Open SUCCESS Desired Access: All Access
8:21:12.7556393 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell\Open SUCCESS Query: Name
8:21:12.7556785 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell\Open SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7558143 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.DumpFile.1\shell\Open\(Default) SUCCESS Type: REG_SZ, Length: 12, Data: &Open
8:21:12.7576306 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell\Open SUCCESS Query: Name
8:21:12.7576775 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell\Open SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7580456 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.DumpFile.1\shell\Open\command SUCCESS Desired Access: All Access
8:21:12.7592768 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell\Open\command SUCCESS Query: Name
8:21:12.7593167 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.DumpFile.1\shell\Open\command SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7594629 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.DumpFile.1\shell\Open\command\(Default) SUCCESS Type: REG_SZ, Length: 68, Data: "C:\debuggers\windbg.exe" -z "%1"
8:21:12.7630360 PM windbg.exe 7140 RegCreateKey HKCR\.wew SUCCESS Desired Access: All Access
8:21:12.7641811 PM windbg.exe 7140 RegQueryKey HKCR\.wew SUCCESS Query: Name
8:21:12.7642168 PM windbg.exe 7140 RegQueryKey HKCR\.wew SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7644407 PM windbg.exe 7140 RegSetValue HKCR\.wew\(Default) SUCCESS Type: REG_SZ, Length: 38, Data: WinDbg.Workspace.1
8:21:12.7677107 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.Workspace.1 SUCCESS Desired Access: All Access
8:21:12.7688110 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1 SUCCESS Query: Name
8:21:12.7688488 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1 SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7690853 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.Workspace.1\(Default) SUCCESS Type: REG_SZ, Length: 44, Data: WinDbg Workspace File
8:21:12.7708855 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1 SUCCESS Query: Name
8:21:12.7709275 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1 SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7712446 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.Workspace.1\DefaultIcon SUCCESS Desired Access: All Access
8:21:12.7722720 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\DefaultIcon SUCCESS Query: Name
8:21:12.7723098 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\DefaultIcon SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7724169 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.Workspace.1\DefaultIcon\(Default) SUCCESS Type: REG_SZ, Length: 64, Data: "C:\debuggers\windbg.exe",-3002
8:21:12.7742535 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1 SUCCESS Query: Name
8:21:12.7742990 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1 SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7746364 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.Workspace.1\shell SUCCESS Desired Access: All Access
8:21:12.7784159 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell SUCCESS Query: Name
8:21:12.7784565 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7785895 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.Workspace.1\shell\(Default) SUCCESS Type: REG_SZ, Length: 10, Data: Open
8:21:12.7806080 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell SUCCESS Query: Name
8:21:12.7806528 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7809825 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.Workspace.1\shell\Open SUCCESS Desired Access: All Access
8:21:12.7821576 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell\Open SUCCESS Query: Name
8:21:12.7821933 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell\Open SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7823025 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.Workspace.1\shell\Open\(Default) SUCCESS Type: REG_SZ, Length: 12, Data: &Open
8:21:12.7840075 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell\Open SUCCESS Query: Name
8:21:12.7840460 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell\Open SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7856271 PM windbg.exe 7140 RegCreateKey HKCR\WinDbg.Workspace.1\shell\Open\command SUCCESS Desired Access: All Access
8:21:12.7869639 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell\Open\command SUCCESS Query: Name
8:21:12.7870052 PM windbg.exe 7140 RegQueryKey HKCR\WinDbg.Workspace.1\shell\Open\command SUCCESS Query: HandleTags, HandleTags: 0x0
8:21:12.7871508 PM windbg.exe 7140 RegSetValue HKCR\WinDbg.Workspace.1\shell\Open\command\(Default) SUCCESS Type: REG_SZ, Length: 70, Data: "C:\debuggers\windbg.exe" -WF "%1"The context menu registered using the same technique as my regsitry file - it uses shell commands in the HKCR hive.
The dump associations aren't as useful as my registry file as only one debugger is supported and it skips *.ini files, but it does however associate the workspace files. I've personally never opened a workspace, but if you have, this might be a good thing to have.
So what will I be doing on my systems from now on?
I'll be running WinDbg.exe -IA against my x64 debugger, I'll then run WinDbg.exe -I against the x64 and x86 debugger, and then I'll run my registry script to give me more flexibility (and file type support) in the way that dumps are loaded.
BTW, if after doing this you find that you aren't getting the context menu, the odds are that you have a 'Open With...' file association in your HKCU hive. Find the .dmp key and delete it to revert to the global HKCR configuration.
Note, if you have no intention of debugging the AeDebug dumps, you are much better off leaving WER as the postmortem debugger (i.e. don't run -I) and sending the issue to Microsoft for analysis. Its the best way of getting the issue fixed.
-
Web Sales / Marketing Manager (SP1289) / Ajel Technologies / Plano, TX
[Jobs (not Steve)] (Business Insider Jobs)Ajel Technologies/Plano, TX Web Sales / Marketing Manager (SP1289) 6-7 Months Contract in Plano, TX 75024 Rate :- $35 - $38/Hr on W2 Requirements :- Minimum 6-24 months of marketing or sales experience – preferably online Previous creative background desired – hobby or professional. Artistry, design, photography, photoshopping, collage, scrapbooking, etc. Data analysis, manipulation, mining, etc.; Business Objects or SQL a plus. Wiki markup language a plus MS-Offic ...
Ajel Technologies/Plano, TX
Web Sales / Marketing Manager (SP1289)
6-7 Months Contract in Plano, TX 75024
Rate :- $35 - $38/Hr on W2
Requirements :-
Minimum 6-24 months of marketing or sales experience – preferably online
Previous creative background desired – hobby or professional. Artistry, design, photography, photoshopping, collage, scrapbooking, etc.
Data analysis, manipulation, mining, etc.; Business Objects or SQL a plus. Wiki markup language a plus
MS-Office product familiarity – Excel, Powerpoint, Word, etc.
Project management skills
Daily tasks for this person will include :-
1. Create, maintain and design webpage related to upgrades.
2. Design and mock up the web page, banner and webtiles to use as supporting documentation in the web ticket system.
3. Create, update and maintain web tickets to update the web page, create new banner/promotions, update the existing tiles, upgrade pricing, etc.
4. Create online promotions
5. Update and load the upgrade promotion, marquee, dashboard and tile
6. Create an upgrade template and roll it out to the website
7. Target the promotions using an internal targeting system
8. Update marquees and tiles according to meet standards for a new, corporate-wide website deployment.
9. Identify and prioritize the promotional tiles. Evaluate/Recreate the upgrade template; Revise the customer targets
10. Other - Support project manager to maintain, track, advise and report on various project stages within the organization
Apply To Job -
WPCandy: Introduction to Hooks: a basic WordPress building block
[WordPress] (WordPress Planet)WordPress hooks are arguably the basis of WordPress development, forming a large part of the core functionality and used by almost every plugin and theme available to date. The concept of hooks can also be somewhat daunting for users who are starting out with developing for WordPress. Today, we’ll jump in and find out a bit more about just what exactly WordPress hooks are and how they can help you on your way to WordPress rock stardom.What exactly *are* WordPress hooks anyways?WordPress ho ...
WordPress hooks are arguably the basis of WordPress development, forming a large part of the core functionality and used by almost every plugin and theme available to date. The concept of hooks can also be somewhat daunting for users who are starting out with developing for WordPress. Today, we’ll jump in and find out a bit more about just what exactly WordPress hooks are and how they can help you on your way to WordPress rock stardom.
What exactly *are* WordPress hooks anyways?
WordPress hooks are, essentially, triggers of sorts that allow users to, with short snippets of code, modify areas a WordPress theme or plugin, or add their own code to various parts of WordPress without modifying the original files. An example of this could be along the lines of either “when WordPress chooses which template file to load, run our custom code” or “when you generate the content for each post, add social bookmarking links to the end of the content”. These examples will be expanded upon once we’re a bit more familiar with what exactly the different types of hooks are.
Hooks can be divided into “Action” and “Filter” hooks, the former allowing for insertion of custom code at various points (not unlike events in JavaScript) and the latter allowing for the manipulation of various bits of content (for example, the content of a page or blog post). Lets take a closer look at each of these, shall we?
Action Hooks
Action hooks are designated points in the WordPress core, theme and plugin code where it is possible for outside resources (outside of the scope of where the hook is… either in the core, theme or plugin) to insert additional code and, there by, customise the code to do additional functions they may desire. An example of this is the commonly used
wp_headaction hook, used by many themes and plugins to inject additional CSS stylesheets, processing code or anything else they require to sit between the<head>and</head>tags of their WordPress theme’s XHTML structure. This is the reason for includingwp_head();in all WordPress themes.To hook on to an action, create a function in your theme’s
functions.phpfile (or in your plugin’s code) and hook it on using theadd_action()function, as follows:<?php add_action( 'wp_head', 'wpcandy_actionhook_example' ); function wpcandy_actionhook_example () { echo 'Hello WPCandy readers!'; } // End wpcandy_actionhook_example() ?>The above code adds the text “Hello WPCandy Readers!” between your theme’s
<head>tags. Placing “wp_head” in the call toadd_action()with “get_header” would display this text above your theme.The way I like to explain action hooks, in a single sentence, is: “When you get to this point, do that.”
Filter Hooks
Filter hooks are used to manipulate output. An example of this would be to add a line or text (or a hyperlink, or a signature sign-off—whatever you’d like) to the end of the content of each of your blog posts. Filter hooks can also be used for truncating text, changing formatting of content, or just about any other programming manipulation requirement (for example, adding to or overriding an array of values).
Custom code is added as a filter using the
add_filter()function. The following code adds a sign-off to the end of each blog post, only when viewing the full blog post screen:<?php add_filter( 'the_content', 'wpcandy_filterhook_signoff' ); function wpcandy_filterhook_signoff ( $content ) { if ( is_single() ) { $content .= '<div class="sign-off">Th-th-th-th-th That
s all, folks!</div> - Action hooks: http://codex.wordpress.org/Plugin_API#Actions
- Filter hooks: http://codex.wordpress.org/Plugin_API#Filters
- And for further reading on the topic, the remainder of the Plugin API page is also available: http://codex.wordpress.org/Plugin_API#Hooks.2C_Actions_and_Filters.
' . "\n";
} // End IF Statement
return $content;
} // End wpcandy_filterhook_signoff()
?>
The above code adds a new div tag to the end of the content of our blog post, only when on a single blog post screen.
A filter hook is like using the str_replace() function in PHP. You give it some data, manipulate, replace or reformat the data and return the new content out at the end.
… and now, for a few commonly asked questions… answered.
Are custom hooks available only to the theme or plugins I’ve got activated?
Custom hooks and filters that are added by a theme or plugin, only apply if that theme or plugin is active. There are many hooks, however, that are global (get_header, wp_head and wp_footer are three examples). If you’d like to switch themes regularly and maintain the functions you’ve hooked onto these, or other, global hooks or filters, I’d recommend writing them into a plugin.
Themes and plugins are able to specify custom filters and actions. We’ll get more into this in part two.
Where can I learn more about action and filter hooks?
My favourite resource is, without a doubt, the WordPress Codex. While there are many tutorials available online regarding filter and action hook applications, the best understanding comes, as they say, when heard from the horse’s mouth. The Codex provides useful examples, as well as up to date and informative explanations, which aid in gathering an overall understanding of the Plugin API (the API that handles action and filter hooks).
Right. Now that we’ve answered a few questions, lets show some practical examples that can be used right off the bat with any WordPress theme.
Add “time ago” time display at the end of each post.
<?php
add_filter( 'the_content', 'wpcandy_time_ago' );
function wpcandy_time_ago ( $content ) {
$content .= "\n" . __( 'Posted ', 'wpcandy' ) . human_time_diff( get_the_time('U'), current_time('timestamp') ) . __( ' ago', 'wpcandy' );
return $content;
} // End wpcandy_time_ago()
?>
Use WordPress conditional tags to detect the user’s web browser and add a class with it’s name to the body tag.
<?php
add_filter('body_class','browser_body_class');
function browser_body_class($classes) {
global $is_lynx, $is_gecko, $is_IE, $is_opera, $is_NS4, $is_safari, $is_chrome, $is_iphone;
if($is_lynx) $classes[] = 'lynx';
elseif($is_gecko) $classes[] = 'gecko';
elseif($is_opera) $classes[] = 'opera';
elseif($is_NS4) $classes[] = 'ns4';
elseif($is_safari) $classes[] = 'safari';
elseif($is_chrome) $classes[] = 'chrome';
elseif($is_IE) $classes[] = 'ie';
else $classes[] = 'unknown';
if($is_iphone) $classes[] = 'iphone';
return $classes;
}
?>
Remove the WordPress 3.1 Admin Bar.
<?php add_filter( 'show_admin_bar', '__return_false' ); ?>
What about Twenty Ten?
With the introduction of Twenty Ten as the WordPress default theme, the theme received a large amount of documentation in the theme files, with several custom hooks or pluggable functions created specifically for it. Lets take a look at how we can use these hooks to enhance Twenty Ten.
The main custom hook in the TwentyTen theme serves a relatively simple purpose: adding content to the credits area in the footer. This is done by hooking on to the new “twentyten_credits” action hook. Here’s an example:
<?php
add_action( 'twentyten_credits', 'wpcandy_credits' );
function wpcandy_credits () {
$html = '';
$html .= 'Proudly brought to you by <a href="http://wpcandy.com/">WPCandy</a>.' . "\n";
echo $html;
} // End wpcandy_credits()
?>
The above function adds a simple line of credit text to the footer area in the Twenty Ten theme. In part two, we’ll discuss pluggable functions, which can be used in the Twenty Ten theme in particular to enhance and customise the output above and below the post content in the theme.
Coming in part two:
Creating your own hooks & filters and a brief introduction to pluggable functions; the hook’s lil’ sister. Please share your experiences with using WordPress action and filter hooks in the comments below.
You just finished reading Introduction to Hooks: a basic WordPress building block on WPCandy. Please consider leaving a comment!
How to Create Presentation Slides with HTML and CSS
[Web Design] (Nettuts+)Advertise here As I sifted through the various pieces of software that are designed for creating presentation slides, it occurred to me: why learn yet another program, when I can instead use the tools that I’m already familiar with? With a bit of fiddling, we can easily create beautiful presentations with HTML and CSS. I’ll show you how today! One Slide from Our Final Product 0 – Directory Structure Before we get started, let’s go ahead and create o ...
As I sifted through the various pieces of software that are designed for creating presentation slides, it occurred to me: why learn yet another program, when I can instead use the tools that I’m already familiar with? With a bit of fiddling, we can easily create beautiful presentations with HTML and CSS. I’ll show you how today!

One Slide from Our Final Product
0 – Directory Structure
Before we get started, let’s go ahead and create our folder structure; it should be fairly simple. We’ll need:
- index.html
- css/style.css
- js/scripts.js
- img/
- slides/
A simple base template. Your slides/ directory can remain blank for the time being. We’ll fill that shortly.
1 – Beginning Markup
Let’s begin by creating the base markup for our presentation page. Paste the following snippet into your index.html file.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" />
<title>My Great Presentation</title>
</head>
<body>
<div class="wrap">
<div id="slides">
<!-- load in slides -->
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
</body>
</html>
Above we have some clean, HTML5 goodness. No more type attributes in our script and link elements, short DOCTYPE, a laughably simple meta charset tag, etc.
2 – Slides
The
load()method is an AJAX method that loads data from your server, and inserts the returned HTML into the selected element.
Now you might be wondering how we’re going to deal with each slide. We have a couple options here. While we could wrap each slide in its own div, and place a massive block of HTML within the #slides container, my instinct is that this will make the process of editing individual slides more time consuming, as we then have to hunt through all that markup for the slide that we need.
Instead, for this tutorial, I’ve opted to place each slide within its own .html file. We can then use jQuery’s load() method to bring in each slide, and append them to our container.
Create a handful of numbered HTML files, and place them within your slides/ directory, like so:
- slides/
- 0.html
- 1.html
- 2.html
- 3.html
- 4.html
Within each of these files, we’ll insert the markup for your desired slide. As an example, together, let’s create an “About Me” slide.
1.html
<div>
<h3> About Me</h3>
<ul>
<li> Nettuts+ Editor</li>
<li> Envato Marketplaces Manager</li>
<li>Wicked WordPress Themes</li>
<li>Theme Tumblr Like a Pro</li>
</ul>
</div>
Feel free to mix and match how you wish.
3 – Load the Slides
Before we can focus on the styling of our presentation, we need to load those slides into our document with jQuery. But rather than creating a bunch of global variables and methods, we’ll store everything in an object, called Slides.
var Slides = {
};
Next, the process of loading those slides into our document will be stored within, say, a loadContent() method. Let’s create that now.
var Slides = {
loadContent : function() {
}
}
To load all of the slides within the slides/ directory, we first need to know how many slides there are; though, JavaScript doesn’t have the ability to access the file system. Instead, we’ll pass in our total number of slides when we initiate our object.
With that in mind, let’s create an init() method that will serve as our controller, of sorts. This method will also receive an argument that specifies the total number of slides. This will then be assigned to a property of our Slides object.
var Slides = {
totalSlides : '',
init : function( totalSlides ) {
// If nothing was passed to this function, we can't continue.
if ( !totalSlides ) throw new Error('Please pass the total number of slides to the init method');
Slides.totalSlides = totalSlides;
// Load the slides
Slides.loadContent();
},
loadContent : function() {
}
}
That’s better. But, of course, none of this code will run until we call the init() method.
var Slides = {
totalSlides : '',
init : function( totalSlides ) {
// If nothing was passed to this function, we can't continue.
if ( !totalSlides ) throw new Error("Please pass the total number of slides to the init method");
Slides.totalSlides = totalSlides;
// Load the slides
Slides.loadContent();
},
loadContent : function() {
}
}
// All right; let's do this. We'll assume that we've created 6 slides, total.
Slides.init( 6 );
4 – Three Ways to Load the Slides
Let’s take our first crack at loading these slides — and then we’ll slowly improve upon our code, as we continue.
loadContent : function() { for ( var i = 0; i < Slides.totalSlides; i++ ) { $('<div id="#slide-' + i + '"></div>') .load('slides/' + i + '.html') .appendTo( $('#slides') ); } }
Above, we’re creating a new div element for the total number of slides that we’ve specified. Each div will have an id of #slide-n. After we’ve created each element, we then load the contents of the desired slide, which we stored within the slides/ directory. Once this block of HTML has been retrieved from the server, we append the newly created div to the #slides container.
We Can Do Better
This code will indeed work, but we can do better. There are a couple issues with the code above:
-
Traversal: For every slide, we are traversing the DOM for the
#slideselement. This is wasteful and unnecessary. Also, as we’ll certainly be working with this#slidescontainer element throughout our project, it makes sense to store it as a property of ourSlidesobject. We’ll do this shortly. -
Reflows: This code will create any number of page reflows, which can increase the load time of our page. Rather than calling the
appendTo()method dozens of times (or in our case: six), let’s limit our reflows to one.
Traversal
Let’s first fix the traversal issue.
var Slides = {
totalSlides : '',
container : $( "#slides" ),
init() { ... },
loadContent() { ... }
}
This way, we search our document for the #slides element exactly once, rather than over and over.
If you’re still confused about the advantages to this, think of it as jumping into a pool, and searching for a coin. Every time you call
$('#slides'), the JavaScript engine jumps in the pool and looks for that coin again. Over and over. But, if we instead store the location of$('#slides')in a variable, it never has to jump back into that pool. It remembers where that coin is.
Reflows
Next, we’ll take care of that pesky reflow issue. There are two ways to limit our reflows. We’ll examine both methods.
Document Fragments
JavaScript document fragments allow us to store chunks of HTML. Then, rather than updating the DOM multiple times, as we did before, with this method, we only call appendTo() once.
Refer here for more information on document fragments.
loadContent : function() { var frag = document.createDocumentFragment(), bit; for ( var i = 0; i < Slides.totalSlides; i++ ) { bit = $('<div id="#slide-' + i + '">'</div>') .load('slides/' + i + '.html')[0]; frag.appendChild(bit); } Slides.container.append(frag); }
Note that we’re no longer calling appendTo() within the for statement. Instead, it’s only being called once. The only note worth mentioning is the [0] section, after we call the load() method. What do we need that for?
Document fragments store — wait for it — HTML fragments, or elements. However, when we called the load() method above, the jQuery object is, of course, returned. That’s not compatible. Instead, we want to filter down to the HTML element, itself. We can do so by using [0], or the get() method. Either option will do.
Hide the Container
Our second option is to hide the container element. When we do so, regardless of how many times you append new elements to that element, no additional page reflows will take effect…because the element is hidden! It’s a nice little trick to have in your tool belt.
loadContent : function() { // Hide the container. Slides.container.hide(); for ( var i = 0; i < Slides.totalSlides; i++ ) { $(''<div id="#slide-' + i + '">'</div>') .load('slides/' + i + '.html') .appendTo(Slides.container); } // Now display the slides container again - causing exactly one reflow. Slides.container.show(); }
So either of these two options will do just fine. Choose whichever you prefer.
If you now view our project in the browser — assuming you’ve added some dummy slides to the slides/ directory — you’ll see something along the lines of:
If we use a tool like Firebug or Chrome’s developer tools, we’ll see that, as expected, the slides have been inserted into the #slides container div.
5 – Make it Pretty
Our next step takes place within our stylesheet. We’ll be focusing on both aesthetics as well as function here. To make each slide translate from left to right, we’ll need to be clever with our styling.
We’ll begin by creating our canvas. If you refer back to our markup…
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" />
<title>My Great Presentation</title>
</head>
<body>
<div class="wrap">
<div id="slides">
<!-- load in slides -->
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
</body>
</html>
…our wrapping container for our project is the div with a class of wrap. We’ll provide a width of 1180px and center it on the page.
.wrap {
margin: auto;
width: 1180px;
overflow: hidden; /* because children will be floated */
}
Next, because each slide should translate horizontally, we need to make our #slides div as wide as possible.
#slides {
width: 999999px;
}
Now, the fun — and a little scary — part. Think of a traditional presentation slide. Isn’t the content typically both vertically and horizontally centered on the slide, or page? Absolutely. Because we have 100% control over this project, we don’t need to worry about browser compliance. As the slides will only ever be used by us (during our presentation), we’re free to tailor it to our favorite browser. This way, we get to use lots of fun new features that haven’t yet made their way into all the browsers, such as the Flexible Box Model.
We’ll begin by specifying the dimensions for each slide, floating each slide, and providing some breathing room (margins).
#slides > div {
height: 600px;
width: 1180px;
float: left;
margin-right: 200px;
text-align: center;
}
But remember: that text needs to be vertically centered. Flexible Box Model to the rescue!
#slides > div {
height: 600px;
width: 1180px;
float: left;
margin-right: 200px;
text-align: center;
/* Flexible Box Model */
display: -webkit-box;
-webkit-box-align: center;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
display: box;
box-align: center;
box-orient: horizontal;
box-pack: center;
}
Next, we’ll add a nice radial gradient background to our slides. To be honest, CSS radial gradients still baffle me. I rarely remember the syntax. As such, I typically either make note of gradients in the wild that I like, or use any of the various gradient generators on the web to make mine. Feel free to do the same.
body {
background-image: -webkit-gradient(
radial,
50% 50%, 0,
50% 50%, 1000,
from(rgba(245,245,245,1)),
to(rgba(100,100,100,1))
);
}
And, with that, we have our basic structure in place. At this point, I encourage you to being styling your headings, perhaps creating a minimal grid, and anything else you can think of. We’ll finish our minimal styling by working on typography.
#slides h1,
#slides h2,
#slides h3,
#slides h4,
#slides h5 {
color: #292929;
font-family: 'League Gothic', sans-serif;
letter-spacing: -5px;
margin-top: 0;
text-shadow: 5px 3px 0 white;
}
#slides > div h2 {
font-size: 180px;
line-height: 1em;
margin: 0;
}
It’s amazing what we can achieve when we tweak letter-spacing, and apply some clever text shadows! Looks much better now!
6 – Next Slide, Please
The next step in this project is to handle the process of transitioning from slide to slide. Naturally, as there are no buttons in place, we should instead listen for when either the left or right arrow keys are pressed. We’ll store this functionality in a new keyPress method of our Slides object.
var Slides = {
...
init : function() { ... },
loadContent: function() { ... },
keyPress : function() {
$(document.body).keydown(function(e) {
// if left or right arrow key is pressed
if ( e.keyCode === 39 || e.keyCode === 37 ) {
e.preventDefault();
( e.keyCode === 39 ) ? Slides.next() : Slides.prev();
}
});
}
}
jQuery provides the helpful keydown method, which will attach the necessary event listeners. This means that the passed callback function will run for every key that is pressed; however, we’re only interested in the right and left arrow key, or key codes 39 and 37, respectively. If one of these keys is pressed, we first cancel the default action of the arrow key, and then call either the next() or prev() method, dependent upon which key was pressed.
Next Slide
Let’s now work on that next() method that we called above. When this method is called, it needs to execute a few operations:
- Update the hash in the URL. That way, we can easily send out links to specific slides.
- Animate the slide to the left, and reveal the next slide in the sequence.
But before moving forward, how do we know how much to translate the slide? It needs to be the width of the #slides container, but we should try not to hardcode that value into our JavaScript if we don’t have to. The reason for this is because, if we later decide to change the dimensions of our canvas, we’d also need to dig into our JavaScript file and update the dimensions as well. Instead, let’s dynamically determine what the width and margins of the container is.
Let’s add a new property to the Slides object, called slideWidth. However, we won’t be able to determine the width of the slides until they’ve been inserted into the DOM, via the loadContent method.
var Slides = {
...
// New property stores the width of each slide
slideWidth : '',
...
init : function( totalSlides ) { ... },
loadContent : function() { ... },
// Determine the width of the slide...
setSlideWidth : function() {
var each = Slides.container.children( 'div' );
Slides.slideWidth = each.width() + ~~( each.css('margin-right').split('px')[0] );
}
}
Yikes – this line looks a bit scary!
Slides.slideWidth = each.width() + ~~( each.css('margin-right').split('px')[0] );
But don’t worry; it’s really quite simple. We’re updating the Slides.slideWidth property, and are making it equal to the width of the slide plus its margin right. Because retrieving the margin-right value of the slides (specifically the first one) will return px as well, we need to slice that off, which we can achieve quite easily by using the split function.
The double bitwise NOT operator
~~is used, in this case, to cast a string to an integer. We could alternatively useMathto achieve this.
To clarify the need for casting, assuming that the margin right value is equal to 200px…
console.log ( typeof each.css('margin-right').split('px')[0] ); // value is 200. Typeof is still string, not integer.
And with that out of the way, we’re now dynamically determining the width of our slides. Back to our next()method; at all times, we need to track the positioning of our slides. That way, when we transition from slide four to five, we know exactly how much to translate the slide. We’ll store this value in a new property: translateAmount.
translateAmount : '',
next : function() {
Slides.translateAmount -= Slides.slideWidth;
}
Let’s break it down. When we press the right arrow key the first time, we set the translateAmount property equal to the slideWidth, or in our case, 1380px. If we press right again, this value will be updated to 2760px.
Update the Hash
Within this next method, we should also update the hash value in our url, such as example.com/index.html#slide-1, then example.com/index.html#slide-2, etc. To do so, we need to keep track of the current slide that the reader is viewing.
currentSlide : 0,
...
next : function() {
Slides.translateAmount -= Slides.slideWidth;
Slides.updateHash( ++Slides.currentSlide );
},
updateHash : function() {
location.hash = '#slide-' + Slides.currentSlide;
}
Note that we’re increasing the value of currentSlide by one before passing it to the updateHash function. This is appropriate; when we press the right arrow key, the hash should be updated to the next slide, not the current one.
Animate the Slide
Finally, now that we’ve tracked all of the necessary values, we can animate the slides.
next : function() {
Slides.translateAmount -= Slides.slideWidth;
Slides.updateHash( ++Slides.currentSlide );
Slides.animate();
},
animate : function() {
Slides.container
.children()
.css( '-webkit-transform', 'translateX(' + Slides.translateAmount + 'px)');
}
For best performance, we’ll take advantage of CSS3 to translate the slides. In order for this to not be an instant translation, we need to update our CSS file:
#slides div {
...
-webkit-transition: all 1s linear;
transition: all 1s linear;
}
And the Previous Slide, Please
The prev method will be really similar to the next method, except for a couple things.
prev : function() {
// No more left to go back.
if ( Slides.translateAmount === 0 ) return;
Slides.translateAmount += Slides.slideWidth;
Slides.updateHash( --Slides.currentSlide );
Slides.animate();
}
This time, as we’re transitioning back to the beginning of the slides, we need to reverse the translateAmount and hash value. Also, we must consider the possibility that the user is pressing the left arrow key even when they’re on the very first slide. If that happens to be the case, we shouldn’t do anything, as there’s nothing left to transition to!
Final JavaScript
var Slides = {
container : $('#slides'),
totalSlides : '',
translateAmount : 0,
currentSlide : 0,
slideWidth : '',
init : function(totalSlides) {
var each;
if ( !totalSlides ) throw new Error('Please pass the total number of slides.');
Slides.totalSlides = totalSlides;
Slides.loadContent();
each = Slides.container.children('div');
// Determine the width of our canvas
Slides.slideWidth = each.width() + ~~( each.css('margin-right').split('px')[0] );
Slides.keyPress();
},
loadContent : function() {
Slides.container.hide();
for ( var i = 0; i < Slides.totalSlides; i++ ) {
$('<div id="#slide-' + i + '"></div>')
.load('slides/' + i + '.html')
.appendTo(Slides.container);
}
Slides.container.show();
},
keyPress : function() {
$(document.body).keydown(function(e) {
// if left or right arrow key is pressed
if ( e.keyCode === 39 || e.keyCode === 37 ) {
e.preventDefault();
( e.keyCode === 39 ) ? Slides.next() : Slides.prev();
}
});
},
next : function( ) {
Slides.translateAmount -= Slides.slideWidth;
Slides.updateHash( ++Slides.currentSlide );
Slides.animate();
},
prev : function() {
// No more left to go back.
if ( Slides.translateAmount === 0 ) return;
Slides.translateAmount += Slides.slideWidth;
Slides.updateHash( --Slides.currentSlide );
Slides.animate();
},
animate : function() {
Slides
.container
.children()
.css( '-webkit-transform', 'translateX(' + Slides.translateAmount + 'px)' );
},
updateHash : function( direction ) {
// Update current Slides and hash.
location.hash = '#slide-' + Slides.currentSlide;
}
};
// All right; let's do this.
Slides.init(6);
Complete
All finished. That wasn’t too hard, once we dug in a bit! The great thing is that, if you view the presentation on a really high or low resolution, you can simply zoom in or out a few clicks to compensate, by pressing Command or Control +-. Let me know if you have any questions or recommendations!
SproutCore 1.5 Released
[Ajax, RIA (Rich Internet Apps)] (SproutCore Blog)We’re excited to announce the final release of SproutCore 1.5. It’s been almost four months since 1.4.5 shipped, and we have lots of exciting new stuff for you. Changes Template View SproutCore 1.5 offers a brand-new way to define your view layer. If you have an existing application, SC.TemplateView makes it easy to integrate Handlebars-flavored HTML into your view hierarchy. If you’re starting a brand new application, you can design your entire application using just HTML, CSS, and the po ...
We’re excited to announce the final release of SproutCore 1.5. It’s been almost four months since 1.4.5 shipped, and we have lots of exciting new stuff for you.
Changes
Template View
SproutCore 1.5 offers a brand-new way to define your view layer. If you have an existing application, SC.TemplateView makes it easy to integrate Handlebars-flavored HTML into your view hierarchy. If you’re starting a brand new application, you can design your entire application using just HTML, CSS, and the power of SproutCore’s binding system.
We’ve put together a tutorial that takes you through the process of creating a new template-based SproutCore application from scratch. You’ll learn how to define a model, create a controller structure, create HTML using Handlebars, then hook all of the pieces together using bindings.
Guide: Getting Started with HTML-Based Apps
Community member Greg Moeck created a screencast as part of his Dispatches from the Edge series that shows some of the power of these templates:
Dispatches from the Edge: Template Views
Theme
If you are building a native-style application using SproutCore’s library of controls, we have a great new default theme called Ace 2.0 that looks at home on modern desktop and mobile operating systems.
We’ve also significantly improved theming flexibility for developers that desire their own look-and-feel. You can even include multiple themes in the same application.
To learn how to theme your application, read the guide by Alex Iskander:
SCSS and Data URIs
Under the hood, the SproutCore build tools include a new CSS parser called Chance. Chance builds on top of SCSS to provide helpers for doing things like including data URI representations of your images. By using data URIs, you can reduce the overhead of additional HTTP requests.
You also get the benefits of SCSS: variables, nested rules, mixins, selector inheritance, and more.
To learn more about SCSS, see the Sass website:
To learn more about the SproutCore-specific features of Chance, read the guide:
Guide: Using Chance, SproutCore’s CSS Framework
WAI-ARIA Support
The desktop controls in 1.5 are now WAI-ARIA-enabled. WAI-ARIA defines a way to make web applications more accessible to people using assistive devices.
Modular Loading
Your applications can now be split into modules which can be loaded automatically when the user is idle or in response to user events. This allows you to reduce the size of your initial application payload.
For example, if your application contains a preference pane, the user probably does not need the code or resources for that pane to be loaded within the first few seconds of using the application.
You can learn about how modules work by reading the modular loading specification:
Specification: Modular Loading
To learn about how modular loading helped Google improve their load times, read this excellent article from the Gmail team:
Reducing Startup Latency (Note that unlike Google, we load your code as strings instead of block comments.)
Experimental Features
Going forward, we are placing an even higher emphasis on quality. This means that features that we do not feel are yet production-ready, but fill an important need, will be placed into an experimental framework. If you’d like to add an experimental feature to your application, you’ll need to list it in your Buildfile.
For example, if you wanted to use the rewrite of SC.SplitView that is not yet fully documented, you would add the following line to your Buildfile:
config :myapp, required => [:sproutcore, 'sproutcore/experimental/split_view']
Roadmap
In order to keep pace with the rapidly evolving world of web apps, and to ensure the development team remains focused, we will be switching to a six-week release cycle. These faster and more-focused releases will allow us to better react to the needs of the developer community.
We will have an announcement about the planned features shortly.
Getting the Gem
To get the updated gem, type the following in your command line:
gem install sproutcore
If you run into any problems, please contact the SproutCore Google Group or visit us in #sproutcore on irc.freenode.net.
If you find a bug, please file it on our GitHub Issues page.
Blog Post: Silverlight 4 Firestarter Series #1: How to migrate a Visual Basic Windows Form Application to Silverlight
[RIA (Rich Internet Apps)] (Site Home)In this walkthrough, I will demonstrate how to convert an existing Windows Forms application that consumes data from a Windows Communication Foundation (WCF) service to Silverlight. Also in the process of conversion we will ensure that the existing functionality is preserved. Here are some topics that we will cover: How to use the Visual Studio 2010 Silverlight Designer XAML and Silverlight control concepts How WCF services can be integrated into Silverlight applications Silverlight dat ...
In this walkthrough, I will demonstrate how to convert an existing Windows Forms application that consumes data from a Windows Communication Foundation (WCF) service to Silverlight. Also in the process of conversion we will ensure that the existing functionality is preserved.
Here are some topics that we will cover:
- How to use the Visual Studio 2010 Silverlight Designer
- XAML and Silverlight control concepts
- How WCF services can be integrated into Silverlight applications
- Silverlight data binding techniques
- How to make asynchronous calls to services
- How to work with cross domain services
- Similarities between Windows Forms and Silverlight applications
Before you begin you need to download the offline kit from the Firestarter Labs, to use the existing applications.
You can migrate the Windows Forms application to Silverlight in three simple steps as follows:
- Explore the Windows Forms application
- Migrate the Windows Forms application to Silverlight
- Call a WCF Service and Bind data
Step 1: Explore the Windows Forms Application
Let’s take a look at the code that we’ll be migrating. To start exploring the Visual Basic Windows Forms application, follow the following steps:
- Open Visual Studio 2010, and from the File menu and select Open Project. The Open Project dialog box is displayed.
-
Open the following Visual Studio Solution file from the downloaded offline kit:
“Firestarter\Labs[___DESCRIPTION___]1 - WinForms\Source\Starting Point\VB\CustomerViewer.sln” -
The following projects are available in the application:
- CustomerService.Model – This project contains the entities and data repository classes that are used to access the AdventureWorks LT database.
- CustomersService – This project is a WCF service application that displays the entities to various applications.
- CustomerViewer – This is a Windows Forms project that takes data from a WCF service.
-
CustomerViewer.Web – This is an ASP.NET Web Forms project that uses jQuery to make RESTful calls to a WCF service
- In Solution Explorer, right-click CustomerService.svc in the CustomersService project.
- From the popup menu select View in Browser. This will start a local WCF server and show a test page.
- Go back to Visual Studio application.
- In Solution Explorer, right-click the CustomerViewer project.
- From the popup menu select Set as StartUp Project.
- Press F5 to run the application. The first time the application runs there will be short delay before data is loaded.
- Select a customer from the drop-down list. The details of the customer are displayed in the form, allowing the data to be updated or deleted.
- Go back to Visual Studio application.
-
In Solution Explorer, open the CustomerForm.vb page and from the popup menu select View Code. Review the code and note the following:
- A WCF service proxy is used to call a service that supplies customer data
- Control data bindings are defined in the SetBindings method
- Customer data can be updated and deleted through interactions with the WCF service
-
To see the Entity Framework 4 model, in Solution Explorer, double-click the AdventureWorksLT.edmx file in the CustomerService.Model project.
Note: The entity model contains several entities including Customer which is used by the Windows Forms application. The DataSets are not used in the application because the data will be exposed through a Web Service and consumed by different types of clients. This project contains strongly typed objects that work well in a Silverlight application where the DataSet class and related classes such as DataTable aren't supported. - From the Repository folder, open the CustomerRepository.vbpage and review the code that interacts with the entity model. The RepositoryBase class is responsible for all communication with Entity Framework and acts as a reusable repository layer in the application.
-
Open the ICustomerService.vb page. The methods in the page are used to load the customer objects and handle the update and delete operations.
Note: The Windows Forms project currently uses a WCF service proxy object to communicate with the different service operations. The Silverlight project will need to call the same service. These service calls are forwarded to the CustomerRepository class. The WCF services work well in environments where data must be exposed to different types of clients without requiring a specific technology or framework. This application uses WCF services to promote data re use, allow different types of clients to consume data, and provide a standards compliant way to access the data.
Step 2: Migrate the Windows Forms Application to Silverlight
Now let’s migrate the application to Silverlight. We'll create a new Silverlight project, work with XAML, create a WCF service proxy to interact with the service, and design a user interface that mirrors the existing Windows Forms user interface.
- To add a new Silverlight Application, right-click the application name and add a new project. The Add New Project dialog box is displayed.
- Browse to the Silverlight node and select the Silverlight Application template.
- Enter the name for the project as SilverlightCustomerViewer and click OK. The New Silverlight Application dialog box is displayed.
- Select <New Web Project> from the drop-down list options and confirm that the New Web project name is displayed as SilverlightCustomerViewer.Web. This project will be used to host the Silverlight application in a web page.
- Click OK to proceed. The MainPage.xaml page of the SilverlightCustomerViewer project is displayed.
-
Replace the <UserControl> tag with the following code:
Note: The d:DesignHeight and d:DesignWidth attributes control the size of the design surface in the design mode. However, they don't have any effect at runtime. The Height and Width attributes constrain the size of the Silverlight screen at runtime. If you don't define the Height and Width attributes, Silverlight will automatically fill the entire area of its container.<UserControl x:Class="SilverlightCustomerViewer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="545" d:DesignWidth="550" Width="545" Height="550">
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
</UserControl>
-
From the toolbox, drag and drop 9 TextBlock controls, 1 ComboBox control, 5 TextBox controls and 2 Button controls to the designer surface and arrange them as the following:
- Change the Text property of each TextBlock control such that it matches the user interface as shown above.
- Set the Content property of the first Button control to “Update” and the Content property of the second Button Control to “Delete”.
-
Select the ComboBox control, and change the name of the control to a value of CustomersComboBox as shown in the following image:
-
Change the DisplayMemberPath property of the ComboBox control to a value of FullName.
Note: The DisplayMemberPath is used to define the property that will be displayed is the ComboBox when it binds to a collection of Customer objects. - Change the names of the update and delete buttons in the interface to “Update” and “Delete” respectively using the Properties window.
- To simulate an HTML frameset tag, from the toolbox drag and drop a Rectangle control to the designer surface.
- Right-click the Rectangle control, and from the popup menu select Order, and then select Send to Back.
-
Resize and arrange the Rectangle control as shown in the following figure:
-
From the toolbox, drag and drop a Border control to the design surface.
- Select the Border control and change its Background property to “White” and its BorderBrush property to “White”.
- From the Toolbox, drag and drop a TextBlock control into the Border control.
- Select the TextBlock control and change the Text property to a value of Customer Details.
- Right-click the Customer Details TextBlock and from the popup menu select Reset Layout, and then select Size.
-
The user interface should look like the following:
Step 3: Call a WCF Service and Bind Data
Now let’s create a WCF service proxy that can be used to call an existing WCF service. You'll also use a clientaccesspolicy.xml file to handle cross domain issues and bind data to controls.
- Right-click on the SilverlightCustomerViewer project and then select Add Service Reference. Add Service Reference dialog box is displayed.
- Click Discover to browse and locate the WCF services.
- To expand the CustomerService.svc service, click on the icon next to CustomerService.svc service. Drill down to browse to the ICustomerService contract. Click the contract name and ensure that it has several service operations available.
- Enter the namespace as CustomerService.Proxies.
- To create the WCF service proxy, click OK.
- Add a new Customer class to the SilverlightCustomerViewer project.
-
Add the following namespace of the new class:
Note: This namespace is added so as to match the namespace of the new class with that of the namespace generated by the WCF proxy.Namespace CustomerService.Proxies
-
To display the FullName property in the ComboBox control, add the following code in the Customer class:
Partial Public Class Customer
Public ReadOnly Property FullName() As String
Get
Return FirstName & " " & LastName
End Get
End Property
End Class
-
To import the proxy namespace, open the MainPage.xaml.vb page and add the following code:
Imports CustomerService.Proxies
-
To hook the Loaded event to an event handler, add the following code within the constructor:
AddHandler Loaded, AddressOf MainPage_Loaded
-
To use the WSF service proxy and make an asynchronous data request, add a MainPage_Loaded method with the following code:
Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim proxy = New CustomerServiceClient()
AddHandler proxy.GetCustomersCompleted, AddressOf proxy_GetCustomersCompleted
proxy.GetCustomersAsync()
End Sub
-
Add the following method and associated code to handle the asynchronous callback, which will be made when the data from the WCF service is returned to the Silverlight application:
Note: Once the WCF service proxy returns data it can be accessed through the GetCustomersCompletedEventArgs object's Result property which is typed as an ObservableCollection of Customer. This collection is assigned to the ItemsSource property of the ComboBox.Private Sub proxy_GetCustomersCompleted(ByVal sender As Object, ByVal e As GetCustomersCompletedEventArgs)
CustomersComboBox.ItemsSource = e.Result
End Sub
- Open the MainPage.xaml page.
- Select the TextBlock control next to Customer ID and select Properties from the menu.
- Remove the text from the Text property and click on the click the icon next to the property and from the popup menu select Apply Data Binding. The Data Binding property dialog box is displayed.
-
Click ElementName and then select CustomersComboBox to set the ComboBox as the data binding source as shown in the following figure:
-
Click Path area and select SelectedItem from the properties as shown in the following figure:
-
In the XAML editor, locate the TextBlock control modified in the previous step and change the Text property value to the following:
Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.CustomerID }”
-
Similarly add data bindings to all of the TextBox controls in the designer surface. For this you'll have to modify the Text property of each control within the XAML as done in the previous step to specify the appropriate property of the SelectedItem to bind to. To set the properties for each TextBox, add the following XAML code:
Note: Each TextBox binding has Mode=TwoWay added to it. This allows any change made to a TextBox control to be propagated back to the bound property automatically.<TextBox Height="23" HorizontalAlignment="Left" Margin="158,225,0,0" VerticalAlignment="Top" Width="219" Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.FirstName,Mode=TwoWay}" DataContext="{Binding}" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="158,270,0,0" VerticalAlignment="Top" Width="219" Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.LastName,Mode=TwoWay}" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="158,316,0,0" VerticalAlignment="Top" Width="219" Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.CompanyName,Mode=TwoWay}"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="158,366,0,0" VerticalAlignment="Top" Width="219" Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.EmailAddress,Mode=TwoWay}" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="158,416,0,0" VerticalAlignment="Top" Width="219" Text="{Binding ElementName=CustomersComboBox, Path=SelectedItem.Phone,Mode=TwoWay}" />
- Right-click the SilverlightCustomerViewer.Web project and select Set as StartUp Project.
- To set the html page in the project as the startup page, right-click the appropriate file and select Set As Start Page.
-
Press F5 to compile and run the application.
Note: An error will occur once the Silverlight application loads. This is due to a cross domain call that is being made from Silverlight to the WCF service. This service uses a different port than the Silverlight host Web project, which causes this cross domain exception to be thrown. - To fix this cross domain issue, rename the existing clientaccesspolicy.exclude file in the CustomersService project to clientaccesspolicy.xml.
- Press F5 to compile and run the application again. Now the data loads in the ComboBox control.
- Select a customer from the drop-down list. The data from it is bound to the appropriate TextBlock and TextBox controls.
- Go back to Visual Studio application to add the Click event handlers.
-
To add the event handler for the Update button, add the following code in the Update button's click event handler:
Dim proxy = New CustomerServiceClient()
Dim cust = TryCast(CustomersComboBox.SelectedItem, Customer)
cust.ChangeTracker.State = ObjectState.Modified
AddHandler proxy.SaveCustomerCompleted, Sub(s, args)
Dim opStatus = args.Result
Dim msg As String = If(opStatus.Status, "Customer Updated!", "Unable to update Customer: " & opStatus.Message)
MessageBox.Show(msg)
End Sub
proxy.SaveCustomerAsync(cust)
-
To add the event handler for the Delete button, add the following code in the Delete button's click event handler:
Dim proxy = New CustomerServiceClient()
Dim cust = TryCast(CustomersComboBox.SelectedItem, Customer)
cust.ChangeTracker.State = ObjectState.Deleted
AddHandler proxy.SaveCustomerCompleted, Sub(s, args)
Dim opStatus As OperationStatus = args.Result
If opStatus.Status Then
CType(CustomersComboBox.ItemsSource, ObservableCollection(Of Customer)).Remove(cust)
MessageBox.Show("Customer deleted!")
Else
MessageBox.Show("Unable to delete Customer: " & opStatus.Message)
End If
End Sub
proxy.SaveCustomerAsync(cust)
- Run the application and test the update and delete functionality.
Summary
Thus, in this walkthrough you have examined an existing Windows Forms application and supporting data access and service layers. You have now successfully migrated the existing functionality from the Windows Forms application to Silverlight.
You can find the full source code for the application here.
Mantracourt's Strain Gauge Indicator Proves a Major Success
[Military] (Military Embedded Systems)Mantracourt, a leading manufacturer of industrial measurement technologies, is celebrating the success of its portable strain gauge display for use as a strain gauge indicator or load cell indicator, (PSD). Developed for a range of industrial customer applications, the PSD is now used by thousands of engineers and technicians around the world. Enabled for automatic sensor calibration and having a waterproof enclosure, a long battery life and with a sister handheld for RS232 output, the PSD strai ...
|
Mantracourt, a leading manufacturer of industrial measurement technologies, is celebrating the success of its portable strain gauge display for use as a strain gauge indicator or load cell indicator, (PSD). Developed for a range of industrial customer applications, the PSD is now used by thousands of engineers and technicians around the world.
Enabled for automatic sensor calibration and having a waterproof enclosure, a long battery life and with a sister handheld for RS232 output, the PSD strain gauge indicator is ideal for a broad range of industrial applications where load, pressure, force or weight measurement data needs to be reliably and accurately recorded.
“The PSD strain gauge display has been developed over recent years for use in harsh environments and is also small and light, so it has proven ideal for the collection of strain gauge and load cell data in industrial and architectural environments,” said Kelly Voysey. “There is also a great deal of intelligence that has been developed into the PSD, making it a highly effective and powerful tool for the collection and analysis of this type of data.”
Mantracourt are specialists in the design and manufacture of electronics for measurement technologies including strain gauge, temperature, pressure, voltage, current, potentiometer, rate, flow, LVDT and displacement in either analogue, digital or wireless form.
“Having been in production since 2003, the PSD strain gauge indicator has proven to be a highly effective solution in a broad range of applications,” said Kelly Voysey, Marketing Manager at Mantracourt. “We have sold many thousands worldwide and we expect a similar growth in sales over the next few years. Indeed, it is successful products such as the PSD that helped Mantracourt gain the Queen's Award for Enterprise in International Trade.”
A typical application of the PSD digital display has been the monitoring of the loads on ‘guide wires’, used to maintain the stability of radio masts. Load shackles with built-in TEDS capabilities are used in the guylines. The TEDS shackles have the intelligence to hold the calibration details of the associated shackle. Using a TEDS enabled PSD load cell display, an operative can connect the PSD to each shackle through a 5 pin Binder socket and the mating connector supplied.
Once connected, the load can be easily read by the PSD using the simple peak/valley hold or gross/net keyboard functions. As calibration is not required, the handheld does not need to be informed as to what shackle it is currently measuring which prevents mix-ups occurring when taking readings. Anyone attending the mast can simply take the readings required by connecting and switching on the PSD. Alternatively, using the PSD232, the sister of the PSD, connection to RS232 can be made which will allow users to interface with a PC or datalogger, for example. The PSD232 sends the displayed value to the RS232 port at 9600 baud in an ASCII format, terminated by a carriage return and line-feed.
“The PSD is designed and entirely manufactured at our factory in the UK, “ said Kelly. “This enables us to ensure the quality and provide the very best technical and engineering support for the PSD.”
· Portable digital display for use as a strain gauge indicator or load cell indicator
· Easy to carry - weighing only 250 grams
· Waterproof IP65 NEMA4 battery powered
· 7 digit LCD display, with battery, peak, trough, net, overload and shunt cal indication
· Battery life of 450 hrs in power save mode
· Tactile keyboard, with on/off, range select, peak/valley hold, and gross/net controls
· Microprocessor based, allowing single pass calibration facility (direct mV/V)
· Dual range for values such as Kg and lb
· TEDS enabled for automatic sensor calibration (IEEE1451 - template 33)
· Option for display with RS232 output option
About Mantracourt
Mantracourt design and manufacture instrumentation for load cells, strain gauges, pressure sensors, torque transducers, flow sensors, temperature sensors, for signal conditioning, display and control using analogue, digital and radio wireless telemetry techniques. Based near Exeter in the South-West of England, Mantracourt Electronics has become firmly established as a key technology provider to many major OEM sensor manufacturers and now has a product range of over 1000 designs. Key to its commercial success is Mantracourt's specialist engineering team that is constantly developing new products using the latest technologies and components. Mantracourt was awarded the Queen's Award for Enterprise in International Trade in 2009. www.mantracourt.co.uk/
OWB 11gR2 – XML
[Corporate Blogs] (Blogs.oracle.com Recent Posts (English-language only))An XML post I did a while back was on Leveraging XDB, which illustrated how to leverage the XML SQL capabilities of the Oracle database. A couple years on, this post could as well have been titled Leveraging ODI, since here I’ll show how with a new XML platform defined, you can leverage the ODI XML JDBC driver and build code template mappings to extract and integrate XML in the same manner as ODI. First up copy the snpsxmlo.jar file from ODI 10g into the OWB owb/lib/ext directory and also on ...
An XML post I did a while back was on Leveraging XDB, which illustrated how to leverage the XML SQL capabilities of the Oracle database. A couple years on, this post could as well have been titled Leveraging ODI, since here I’ll show how with a new XML platform defined, you can leverage the ODI XML JDBC driver and build code template mappings to extract and integrate XML in the same manner as ODI.
First up copy the snpsxmlo.jar file from ODI 10g into the OWB owb/lib/ext directory and also on the server if you are running distributed. You’ll need to restart any processes (OWB, control center agent etc.) to pick up the jar. Next download and execute this XML platform script here to define the XML platform in the OWB repository – the platform script defines datatypes and some properties of the platform (default URL, driver class etc.). You can execute this from OMBPlus or from the OMBPlus panel in the designer. This will create an XML node under the Databases node in the Projects panel and Locations panel.
You build XML modules in the same way as you build any other module.
Create the module and define the location using the URL for the XML JDBC driver (see doc here) – just like in ODI we can stage the data in a different database rather than what the default driver does, below we are just using the default in-memory mode;
You can hit Test Connection and validate your input. If you get class not found ensure you put the driver JAR file in the correct directory. There is a bit of typing going on as you can see, so double check your work.
After that thought its easy street. Now you can import just like regular modules.
Select the table type, the import for XML will use the JDBC database metadata to return ‘tables’ corresponding to the elements in the XML.
I just selected all the elements…
After the import is complete, just like in ODI we get the columns for our elements and a table for each element, You can then view the data in the elements;
Note there are some extra columns from our XML element definition;
<!ELEMENT city (client*)>
<!ATTLIST city
CITY_ID ID #REQUIRED
CITY_NAME CDATA #IMPLIED
POPULATION CDATA #IMPLIED
>
The columns CITYORDER, CITYPK and REGIONFK are the additional columns the XML JDBC driver has added to ‘relationalize’ the nested XML element.
You can then go on and build code template mappings manipulating the XML, below I join region, city and client and load the result into an Oracle target table, I could do all sorts along the way.
The execution unit view has the XML elements being joined in one execution unit and the Oracle target in another, this was the default execution plan generated by OWB. I used the SQL to Oracle load code template shipped with OWB and the Oracle target code template for the integration execution unit.
Deploying and executing the mapping we can then view the resultant data in the target table, pretty straightforward.
That’s a whirlwind pass on extracting information from XML in a very straight forward manner. The platform definition provided has been used here for demonstration to illustration how simple this can be. Writing XML is another interesting capability also possible with such an approach using this driver, but that’s another story.
Blog Post: Migrating Access Jet Databases to SQL Azure
[SharePoint] (Site Home)In this blog, I’ll describe how to use SSMA for Access to convert your Jet database for your Microsoft Access solution to SQL Azure. This blog builds on Access to SQL Server Migration: How to Use SSMA using the Access Northwind 2007 template. The blog also assumes that you have a SQL Azure account setup and that you have configured firewall access for your system as described in the blog post Migrating from MySQL to SQL Azure Using SSMA. Creating a Schema on SQL Azure If you are using a tri ...
In this blog, I’ll describe how to use SSMA for Access to convert your Jet database for your Microsoft Access solution to SQL Azure. This blog builds on Access to SQL Server Migration: How to Use SSMA using the Access Northwind 2007 template. The blog also assumes that you have a SQL Azure account setup and that you have configured firewall access for your system as described in the blog post Migrating from MySQL to SQL Azure Using SSMA.
Creating a Schema on SQL Azure
If you are using a trial version of SQL Azure, you’ll want to get the most out of your free 1 GB Web Edition database. By using a SQL Server schema, you can accommodate multiple Jet database or MySQL migrations into a single database and limit access to users for each schema via the SQL Server permissions hierarchy.
SSMA for Microsoft Access version 4.2 doesn’t support the creation of a database schema within the tool, so you will need to create the schema using the Windows Azure Portal. Launch the Windows Azure Portal with your Live ID and follow the steps as shown below.
- Click on the Database node in the left hand navigation pane.
- Expand out the subscription name for your Azure account until you see your databases
- Select the target database that you created when you first connected to the Azure portal – see Migrating from MySQL to SQL Azure Using SSMA for how the SSMADB was created for this blog.
- Click on the Manage command to launch the Database Manager. You will log in into SQL Azure database as shown below.
Once in the Database Manager, you will need to press the New Query command as shown below so that you can create the target schema for the Northwind2007 database.
Now that you have the new query window, you can do the following steps as illustrated below.
- Type in the Transact-SQL command to create your target schema: create schema Northwind2007
- Press the Execute command in the toolbar to run the statement.
- Click on the Message window command to show that the command was completed successfully.
You are now ready to use SSMA for Access to migrate your database to SQL Azure into the Northwind2007 schema.
Creating a Migration Project with SQL Azure as the Destination
Start SSMA for Access as usual, but close the Migration Wizard that starts by default. The Migration Wizard will end up creating the tables in the dbo schema instead of the Northwind2007 schema that you created. Follow the steps shown below to create your manual migration project.
- Click on the New Project command.
- Enter in the name of your project.
- Select SQL Azure for the Migration To option and click OK. If you forget to select SQL Azure, you’ll need to create a new project again because you can’t change the option once you have competed the dialog.
The next step is to add the Northwind2007 database file to the project and connect to your SQL Azure database as shown below.
- Click on the Add Databases command and select the Northwind2007 database.
- Expand the Access-metadata node in the Access Metadata Explorer to show the Queries and Tables nodes and select the Tables checkbox.
- Click on the Connect to SQL Azure command
- Complete the connection dialog to your SQL Azure database
Choosing the Target Schema
To change the target schema, you need to Modify the default value from master.dbo to database name and schema that you created for your SQL Azure database – in this example – SSMADB.Northwind2007 following the steps below.
- Click on the Modify button in the Schema tab.
- Click on the […] brose button in the Choose Target Schema dialog.
- Choose the target schema – Northwind2007 - and then click the Select and the OK button.
Migrate the Tables and Data with the Convert, Load, and Migrate Command
At this point, you are ready to proceed with the standard migration steps for SSMA which includes (ignoring errors):
- Click on the Tables folder for the Northwind2007 database in the Access Metadata Explorer to enable the migration toolbar commands.
- Click on the Convert, Load, and Migrate command to do all the steps to compete the migration with the one command.
- Click OK for the Synchronize with the Database dialog as shown below to create the tables in the Northwind2007 schema within the SSMADB database.

- Dismiss the Convert, Load, and Migrate dialog assuming everything worked.
Using SSMA to Verify the Migration Result
To verify the results, you can use the Access and SQL Azure Metadata Explorers to compare data after the transfer as follows.
- Click on the source table Employees in the Access Metadata Explorer
- Select the Data tab in the Access workspace to see the data
- Click on the target table Employees in the SQL Azure Metadata Explorer
- Select the Table tab in the SQL Azure workspace to see the schema or the Data tab to view the data.
You can also use the SQL Azure Database Manager to view the table schema and data as described at the end of the blog post Migrating from MySQL to SQL Azure Using SSMA.
Creating Linked Tables to SQL Azure for your Access Solution
To make your Access solution use the SQL Azure tables, you need to create Linked tables to the SQL Azure database. To create the Linked tables, you need to select the Tables folder in the Access Metadata Explorer as shown below.
Right click on the Tables folder and select the Linked Tables command. SSMA will create a backup of the tables in your Access solution file and then create the Linked Table that connects to the table in SQL Azure.
Summary
As you can see, migrating your Access solution that uses Jet tables as easy as:
- Creating a target schema in your target SQL Azure database.
- Creating a project with the Migrate To option set to SQL Azure.
- Following the normal steps for migrating schema and data within SSMA.
- Verifying the reports within SSMA or through the SQL Azure Database Manager.
- Creating Linked Tables to the SQL Azure database within your Access solution using SSMA.
Additional SQL Azure Resources
To learn more about SQL Azure, see the following resources.
Blog Post: April 2011 - Technical Rollup Mail - Platforms
[Data Centre] (Site Home)The TRM blog can be found here http://blogs.technet.com/trm/ Platforms News Windows Internet Explorer 9 Released to Web You can now download Internet Explorer 9. Check out the latest features for IT professionals, and get guidance to help you pilot and deploy this enterprise-ready browser in your organization with the Springboard Series for Internet Explorer 9. http://windows.microsoft.com/en-US/internet-explorer/products/ie/home http://technet.microsoft.com/en-us/ie/default Visual Stud ...
The TRM blog can be found here http://blogs.technet.com/trm/
Platforms
News
Windows Internet Explorer 9 Released to Web
You can now download Internet Explorer 9. Check out the latest features for IT professionals, and get guidance to help you pilot and deploy this enterprise-ready browser in your organization with the Springboard Series for Internet Explorer 9.
http://windows.microsoft.com/en-US/internet-explorer/products/ie/home
http://technet.microsoft.com/en-us/ie/default
Visual Studio 2010 Service Pack 1 now available
Visual Studio 2010 Service Pack 1 includes a host of improvements and fixes based on your feedback, including a local Help Viewer, Silverlight performance tuning, .NET 3.5 unit testing, IntelliTrace for 64-bit and SharePoint, and much, much more.
Microsoft App-V 4.6 Service Pack 1 and MED-V 2.0 Released
Microsoft Desktop Optimization Pack (MDOP) 2011, featuring Microsoft App-V 4.6 Service Pack 1 (SP1) and MED-V 2.0, is now available. App-V 4.6 SP1 makes App-V packaging easy, fast, and predictable, and MED-V 2.0 is easier to use than ever, with no dedicated infrastructure required
Windows Virtualization Team Blog
http://blogs.technet.com/virtualization
The World Simplified is a Virtual World
http://blogs.technet.com/virtualworld
The App-V Product Team Blog
The Official MDOP Blog
Ask the Directory Services Team
http://blogs.technet.com/askds
Ask the Performance Team
http://blogs.technet.com/askperf
Microsoft Enterprise Networking Team
http://blogs.technet.com/networking
Ask the Core Team
http://blogs.technet.com/askcore
Ntdebugging Blog
http://blogs.msdn.com/ntdebugging
The Hot Blog
TRM
http://blogs.technet.com/trm/default.aspx
Downloads
Windows Internet Explorer 9 Released to Web
You can now download Internet Explorer 9. Check out the latest features for IT professionals, and get guidance to help you pilot and deploy this enterprise-ready browser in your organization with the Springboard Series for Internet Explorer 9.
http://windows.microsoft.com/en-US/internet-explorer/products/ie/home
http://technet.microsoft.com/en-us/ie/default
Group Policy Settings Reference Windows Internet Explorer 9
This spreadsheet lists the policy settings for computer and user configurations included in the administrative template files (admx/adml) delivered with Windows Internet Explorer 9.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=21e84c24-b967-4d6d-850a-5eb554d18447
Visual Studio 2010 Service Pack 1 now available
Visual Studio 2010 Service Pack 1 includes a host of improvements and fixes based on your feedback, including a local Help Viewer, Silverlight performance tuning, .NET 3.5 unit testing, IntelliTrace for 64-bit and SharePoint, and much, much more.
VMM 2012 Beta Eval (VHD)
System Center Virtual Machine Manager 2012 delivers industry leading fabric managment, virtual machine management and services deployment in private cloud environments.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=76002803-4fe8-4573-a76d-6b2b11adfe58
System Center Virtual Machine Manager (VMM) 2012 Beta Documentation
This download provides technical documentation for the Beta release of VMM 2012.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=f7c174f2-1d39-4fed-9778-3f41b84f744b
Server App-V Beta Documentation
The beta documentation for Microsoft Server Application Virtualization (Server App-V) is available in this download.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=d24bbf9b-9d9c-4966-90e5-575852c9258b
Microsoft Windows Server 2008 Hyper-V Common Criteria Guide
This is the supplemental administrator guidance documentation that was used in the Common Criteria evaluation of Microsoft Windows Server 2008 Hyper-V.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=cb19538d-9e13-4ab6-af38-8f48abfdad08
System Center Virtual Machine Manager 2008 R2 Service Pack 1 – Evaluation
System Center Virtual Machine Manager 2008 R2 SP1 (VMM) is a comprehensive management solution for the virtualized data center. It enables increased physical server utilization, centralized management of virtual machine infrastructure, and rapid provisioning of new virtual machines by the administrator, delegated administrator, and authorized end users.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=9189bbce-d970-4c6c-9dd3-9e65798ecd70
BranchCache Learning Roadmap
This learning roadmap provides you with links to prerequisite information you need to understand and deploy BranchCache, and also provides links to BranchCache information from level 100 to level 300. In addition there are links to optional information that will enhance your ability to expand and manage your BranchCache deployment.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=cda652cf-c954-4b78-9e1b-7a660dc3b867
Microsoft Desktop Virtualization Data Sheets
Learn how Microsoft Desktop Virtualization Solutions can empower organizations to provide employees with the flexibility to work everywhere on a range of devices, while simplifying compliance and business continuity through a centralized and unified management infrastructure.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=81d1d1b8-e0e2-43c1-be91-6a5382f8ac39
Managing Microsoft Desktop Virtualization
Learn about managing desktop virtualization solutions with Microsoft technologies..
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=59e220ee-e7a1-4f8e-a86a-538b854c5e18
Microsoft RemoteFX for Remote Desktop Virtualization Host Capacity Planning Guide for Windows Server 2008 R2 Service Pack 1
This white paper is intended as a guide for capacity planning of Microsoft RemoteFX in Windows Server 2008 R2 Service Pack 1.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=679193cb-9b74-4590-a2be-00bde429c990
Remote Desktop Session Host Capacity Planning in Windows Server 2008 R2 and Microsoft RemoteFX in Windows Server 2008 R2 with Service Pack 1
This white paper is intended as a guide for capacity planning of RD Session Host in Windows Server 2008 R2 and RemoteFX in Windows Server 2008 R2 with Service Pack 1 (SP1).
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=ca837962-4128-4680-b1c0-ad0985939063
Events/Webcasts
Live Webcasts:
Full list can be found at: http://www.microsoft.com/events/default.mspx
Interactive Webcast Calendar
The interactive calendar will help you quickly find live webcasts that fit your schedule as well as on-demand webcasts to view at your convenience. You can choose your time zone and filter this list by audience, webcast series, product or topic, and presenter.
http://www.microsoft.com/events/webcasts/calendar/monthview.aspx
Monthly Archived Webcasts:
http://www.microsoft.com/events/webcasts/library/default.mspx
April WebCast Calendar
New KB's
Problems with Windows 7 parsing MCCS information
http://support.microsoft.com/kb/2515532
“Access Denied” error trying to rename a file on share available offline
http://support.microsoft.com/kb/2457622
Event ID 7000 or 7026 may be logged in the System log on a computer that is running Windows 7, Windows Vista, Windows Server 2008 R2, or Windows Server 2008
http://support.microsoft.com/kb/933757
Error message when installing RSAT: 'This update is not applicable to your computer'
http://support.microsoft.com/kb/2517239
Display Changes Resolution on Windows 7 While Pressing Win+P Hot Key and Select Duplicate When Only One Monitor is Connected
http://support.microsoft.com/kb/2518084
The GetGlyphOutline() function incorrectly returns glyphs from the GulimChe font in Windows 7
http://support.microsoft.com/kb/2447659
A DNS Update is recorded as failed: Event ID 5774, 1196, or 1578
http://support.microsoft.com/kb/977158
Windows 7 stops responding when you print to a Bluetooth printer
http://support.microsoft.com/kb/2461648
[SDP 3][506d1864-dc2a-493c-be61-defc84fbcd60] Volume License Activation - Windows 7 and Windows Server 2008 R2
http://support.microsoft.com/kb/2518799
[SDP 3][10678477-8f52-4968-8848-01305cbcc3c1] Performance - Windows 7 and Server 2008 R2
http://support.microsoft.com/kb/2516512
Microsoft inbox DSM may not perform well on system with many processors
http://support.microsoft.com/kb/2517251
Using Hyper-V with large sector drives on Windows Server 2008 and Windows Server 2008 R2
http://support.microsoft.com/kb/2515143
Remote Desktop or RemoteApp session does not terminate due to spawned splwow64.exe process
http://support.microsoft.com/kb/2513330
Physical Memory Limits in Crash Dump files for Windows 7 and Windows 2008 R2
http://support.microsoft.com/kb/2510168
Group Policy Editor incorrectly displays value for drop-down box data in Group Policy settings if opened maximized
http://support.microsoft.com/kb/2494205
The DNS Zone Transfer setting is not retained in Windows Server 2008
http://support.microsoft.com/kb/2514953
You cannot manage a remote DNS server by using RSAT or the DNS MMC on Window Server-based computers
http://support.microsoft.com/kb/2514936
A static record is not updated to a dynamic record as expected in the Windows Server MMC
http://support.microsoft.com/kb/2514994
[SDP 3][460be109-5977-4f5b-ba8b-a605415c973e] Directory Services - Windows 7 and Server 2008 R2
http://support.microsoft.com/kb/2515358
A hotfix is available to update the Daylight Saving Time for Chile for Windows Operating Systems
http://support.microsoft.com/kb/2519231
MS11-015: Description of the security update for Windows XP Media Center Edition 2005: March 8, 2011
http://support.microsoft.com/kb/2502898
MS11-017: Description of the security update for Remote Desktop client 5.2: March 8, 2011
http://support.microsoft.com/kb/2483618
Offline File Synchronization – In Windows 7 the "Work Offline/Work Online" option button disappears from Windows Explorer after an offline / online transition and the Client Side Cache remains offline until the next restart of the computer
http://support.microsoft.com/kb/2512089
A device may be displayed as the default icon on the network map when the network has more than 10 hosts
http://support.microsoft.com/kb/2506718
Keys in the CNG user interface are always described as having no description in Windows 7 or in Windows Server 2008 R2
http://support.microsoft.com/kb/2507840
A black screen is displayed when a Windows 7 SP1-based or Windows Server 2008 R2 SP1-based computer tries to enter hibernation
http://support.microsoft.com/kb/2496744
MS11-017: Vulnerability in Remote Desktop client could allow remote code execution: March 8, 2011
http://support.microsoft.com/kb/2508062
MS11-017: Description of the security update for Remote Desktop client 7.0: March 8, 2011
http://support.microsoft.com/kb/2483614
MS11-015: Vulnerability in Microsoft DirectShow could allow remote code execution: March 8, 2011
http://support.microsoft.com/kb/2510030
Stop D1 (DRIVER_IRQL_NOT_LESS_OR_EQUAL) bugcheck in USBPORT while transferring USB data and selecting USB interface
http://support.microsoft.com/kb/2507083
How to fix the FLECS Add-on and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520458
How to fix the Superfish Window Shopper add-on and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520435
How to fix the Shopping Reports add-on and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520397
How to fix the Verification Engine add-on and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520408
How to fix the iPlus add-on and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520419
How to fix the PC Tools Browser Guard Toolbar and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520365
How to fix the Xunlei Thunder Download Manager add-on and Internet Explorer 9 incompatibility
http://support.microsoft.com/kb/2520403
Errors occur when using Canon printer drivers to print from Windows Internet Explorer 9
http://support.microsoft.com/kb/2522071
MS11-017: Description of the security update for Remote Desktop client 6.1 and Remote Desktop client 6.0: March 8, 2011
http://support.microsoft.com/kb/2481109
Windows Management Framework (Windows PowerShell 2.0, WinRM 2.0, and BITS 4.0)
http://support.microsoft.com/kb/968929
MS11-015: Description of the security update for Windows Media Center TV Pack 2008 for Windows Vista: March 8, 2011
http://support.microsoft.com/kb/2494132
How to Compress "Bloated" Registry Hives
http://support.microsoft.com/kb/2498915
Adding Simplified Chinese MUI Pack to English Windows Server 2003 SP2 can cause operating system to hang
http://support.microsoft.com/kb/2524277
Recovering missing FRS objects and FRS attributes in Active Directory
http://support.microsoft.com/kb/312862
IEEE 1394 Asynchronous Request May Time Out Due to Dropped Response from Remote Node
http://support.microsoft.com/kb/2519460
An ADO application that is re-compiled on a Windows 7 Service Pack 1-based computer does not run on down-level operating systems
http://support.microsoft.com/kb/2517589
Configuring Internet Explorer proxy settings in response to malware
http://support.microsoft.com/kb/2522894
Internet Explorer 8 may stop responding when you browse some webpages in Windows 7 or in Windows Server 2008 R2
http://support.microsoft.com/kb/2510901
The XSS Filter in Internet Explorer 8 improperly blocks HTTP response rendered as XML
http://support.microsoft.com/kb/2524198
Description for "Turn On ActiveX filtering" Group Policy Setting contains incorrect text
http://support.microsoft.com/kb/2522815
Missing or Garbled Text Printing from IE9 to Adobe PDF Printer
http://support.microsoft.com/kb/2522987
SharePoint 2010: Receiving error: “Security token service is not available”
http://support.microsoft.com/kb/2493524
Performance issues are caused by a memory leak on a Windows Server 2008-based print server that has many Windows Vista-based printer clients
http://support.microsoft.com/kb/951638
Performance counter 'Redirector-Current Commands' always shows a value of '0'
http://support.microsoft.com/kb/2523382
You may experience problems in Windows Explorer or in the Windows shell after you install security update MS06-015
http://support.microsoft.com/kb/918165
When a user offers a Remote Assistance invitation to an expert user who is running Windows Server 2003 Service Pack 2, the expert’s logon attempt forcibly logs off the original user
http://support.microsoft.com/kb/2526705
WriteEncryptedFileRaw fails if the file contained a sparse stream
http://support.microsoft.com/kb/2525290
PXE Clients do not receive an IP address from a DHCP server
http://support.microsoft.com/kb/257579
Various issues may occur on a Windows Server 2003-based computer that is running the Volume Shadow Copy Service
http://support.microsoft.com/kb/940032
Configuring opportunistic locking in Windows
http://support.microsoft.com/kb/296264
[SDP 3][f2567265-e251-4fce-a78a-dd0a01d22182] Repro Recorder
http://support.microsoft.com/kb/2520621
Applications that use the Virtual Wi-Fi technology do not work after you restart a computer that is running Windows 7 or Windows Server 2008 R2
http://support.microsoft.com/kb/2496820
An update to support the new currency symbol for the Indian Rupee in Windows Vista, in Windows Server 2008, in Windows 7 and in Windows Server 2008 R2
http://support.microsoft.com/kb/2496898
Slow response working with WebDAV resources on Windows Vista or Windows 7
http://support.microsoft.com/kb/2445570
Error message "Windows SIM was unable to generate a catalog" or "Parameter count mismatch"
http://support.microsoft.com/kb/2524737
Error when you install an update for Windows: "Error Code FFFFFFFE"
http://support.microsoft.com/kb/2525311
You have to refresh the web page to load the current page in Internet Explorer 8
http://support.microsoft.com/kb/2525067
[SDP 3][a0f578a1-731c-4437-92cf-c246fa98be36] Hyper-V - Windows Server 2008 R2
http://support.microsoft.com/kb/2518791
Clientname and Sessionname enviroment variable may be missing
http://support.microsoft.com/kb/2509192
CryptSetProvider function or CryptSetProviderEx fails to change the user’s default cryptographic service provider (CSP).
http://support.microsoft.com/kb/2513526
FIX: Error message when you access an NFS share from a computer that is running Windows Vista SP2 or Windows Server 2008 SP2: "Incorrect Function. Access Denied"
http://support.microsoft.com/kb/978845
QFE1 for Windows Live Essentials 2011
http://support.microsoft.com/kb/2520039
All USB Downstream ports are disabled on a EHCI Host Controller after restart or inserting a USB device on Windows Vista or Windows 7
http://support.microsoft.com/kb/2521680
This content is for informational purposes only. Microsoft makes no warranties express or implied, as to the information in this document. If you are a customer of Microsoft, please refer to the terms and conditions which cover the provision of support and consulting services to you/your organization. If you are not corresponding in the course of, or in connection with a Microsoft contract or program with its own terms and conditions, please note that no liability is accepted by Microsoft for the contents of this document.
TRM contributors this month were Tracy Gillespie and Justin Zarb.
Top 15+ Best Practices for Writing Super Readable Code
[Web Design] (Nettuts+)Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Nettuts+. Code readability is a universal subject in the world of computer programming. It’s one of the first things we learn as developers. This article will detail the fifteen most important best practices when writing readable code. 1 - Commenting & Documentation IDE’s (Integrated Development Environment) have come a long way in the past few years. This made commenting your code ...
Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Nettuts+.
Code readability is a universal subject in the world of computer programming. It’s one of the first things we learn as developers. This article will detail the fifteen most important best practices when writing readable code.
1 - Commenting & Documentation
IDE’s (Integrated Development Environment) have come a long way in the past few years. This made commenting your code more useful than ever. Following certain standards in your comments allows IDE’s and other tools to utilize them in different ways.
Consider this example:
The comments I added at the function definition can be previewed whenever I use that function, even from other files.
Here is another example where I call a function from a third party library:
In these particular examples, the type of commenting (or documentation) used is based on PHPDoc, and the IDE is Aptana.
2 - Consistent Indentation
I assume you already know that you should indent your code. However, it’s also worth noting that it is a good idea to keep your indentation style consistent.
There are more than one way of indenting code.
Style 1:
function foo() {
if ($maybe) {
do_it_now();
again();
} else {
abort_mission();
}
finalize();
}
Style 2:
function foo()
{
if ($maybe)
{
do_it_now();
again();
}
else
{
abort_mission();
}
finalize();
}
Style 3:
function foo()
{ if ($maybe)
{ do_it_now();
again();
}
else
{ abort_mission();
}
finalize();
}
I used to code in style #2 but recently switched to #1. But that is only a matter of preference. There is no “best” style that everyone should be following. Actually, the best style, is a consistent style. If you are part of a team or if you are contributing code to a project, you should follow the existing style that is being used in that project.
The indentation styles are not always completely distinct from one another. Sometimes, they mix different rules. For example, in PEAR Coding Standards, the opening bracket "{" goes on the same line as control structures, but they go to the next line after function definitions.
PEAR Style:
function foo()
{ // placed on the next line
if ($maybe) { // placed on the same line
do_it_now();
again();
} else {
abort_mission();
}
finalize();
}
Also note that they are using four spaces instead of tabs for indentations.
Here is a Wikipedia article with samples of different indent styles.
3 - Avoid Obvious Comments
Commenting your code is fantastic; however, it can be overdone or just be plain redundant. Take this example:
// get the country code
$country_code = get_country_code($_SERVER['REMOTE_ADDR']);
// if country code is US
if ($country_code == 'US') {
// display the form input for state
echo form_input_state();
}
When the text is that obvious, it’s really not productive to repeat it within comments.
If you must comment on that code, you can simply combine it to a single line instead:
// display state selection for US users
$country_code = get_country_code($_SERVER['REMOTE_ADDR']);
if ($country_code == 'US') {
echo form_input_state();
}
4 - Code Grouping
More often than not, certain tasks require a few lines of code. It is a good idea to keep these tasks within separate blocks of code, with some spaces between them.
Here is a simplified example:
// get list of forums
$forums = array();
$r = mysql_query("SELECT id, name, description FROM forums");
while ($d = mysql_fetch_assoc($r)) {
$forums []= $d;
}
// load the templates
load_template('header');
load_template('forum_list',$forums);
load_template('footer');
Adding a comment at the beginning of each block of code also emphasizes the visual separation.
5 - Consistent Naming Scheme
PHP itself is sometimes guilty of not following consistent naming schemes:
- strpos() vs. str_split()
- imagetypes() vs. image_type_to_extension()
First of all, the names should have word boundaries. There are two popular options:
- camelCase: First letter of each word is capitalized, except the first word.
- underscores: Underscores between words, like: mysql_real_escape_string().
Having different options creates a situation similar to the indent styles, as I mentioned earlier. If an existing project follows a certain convention, you should go with that. Also, some language platforms tend to use a certain naming scheme. For instance, in Java, most code uses camelCase names, while in PHP, the majority of uses underscores.
These can also be mixed. Some developers prefer to use underscores for procedural functions, and class names, but use camelCase for class method names:
class Foo_Bar {
public function someDummyMethod() {
}
}
function procedural_function_name() {
}
So again, there is no obvious “best” style. Just being consistent.
6 - DRY Principle
DRY stands for Don’t Repeat Yourself. Also known as DIE: Duplication is Evil.
The principle states:
“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.”
The purpose for most applications (or computers in general) is to automate repetitive tasks. This principle should be maintained in all code, even web applications. The same piece of code should not be repeated over and over again.
For example, most web applications consist of many pages. It’s highly likely that these pages will contain common elements. Headers and footers are usually best candidates for this. It’s not a good idea to keep copy pasting these headers and footers into every page. Here is Jeffrey Way explaining how to create templates in CodeIgniter.
$this->load->view('includes/header'); $this->load->view($main_content); $this->load->view('includes/footer');
7 - Avoid Deep Nesting
Too many levels of nesting can make code harder to read and follow.
function do_stuff() {
// ...
if (is_writable($folder)) {
if ($fp = fopen($file_path,'w')) {
if ($stuff = get_some_stuff()) {
if (fwrite($fp,$stuff)) {
// ...
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
For the sake of readability, it is usually possible to make changes to your code to reduce the level of nesting:
function do_stuff() {
// ...
if (!is_writable($folder)) {
return false;
}
if (!$fp = fopen($file_path,'w')) {
return false;
}
if (!$stuff = get_some_stuff()) {
return false;
}
if (fwrite($fp,$stuff)) {
// ...
} else {
return false;
}
}
8 - Limit Line Length
Our eyes are more comfortable when reading tall and narrow columns of text. This is precisely the reason why newspaper articles look like this:
It is a good practice to avoid writing horizontally long lines of code.
// bad
$my_email->set_from('test@email.com')->add_to('programming@gmail.com')->set_subject('Methods Chained')->set_body('Some long message')->send();
// good
$my_email
->set_from('test@email.com')
->add_to('programming@gmail.com')
->set_subject('Methods Chained')
->set_body('Some long message')
->send();
// bad
$query = "SELECT id, username, first_name, last_name, status FROM users LEFT JOIN user_posts USING(users.id, user_posts.user_id) WHERE post_id = '123'";
// good
$query = "SELECT id, username, first_name, last_name, status
FROM users
LEFT JOIN user_posts USING(users.id, user_posts.user_id)
WHERE post_id = '123'";
Also, if anyone intends to read the code from a terminal window, such as Vim users, it is a good idea to to limit the line length to around 80 characters.
9 - File and Folder Organization
Technically, you could write an entire application code within a single file. But that would prove to be a nightmare to read and maintain.
During my first programming projects, I knew about the idea of creating “include files.” However, I was not yet even remotely organized. I created an “inc” folder, with two files in it: db.php and functions.php. As the applications grew, the functions file also became huge and unmaintainable.
One of the best approaches is to either use a framework, or imitate their folder structure. Here is what CodeIgniter looks like:
10 - Consistent Temporary Names
Normally, the variables should be descriptive and contain one or more words. But, this doesn’t necessarily apply to temporary variables. They can be as short as a single character.
It is a good practice to use consistent names for your temporary variables that have the same kind of role. Here are a few examples that I tend use in my code:
// $i for loop counters
for ($i = 0; $i < 100; $i++) {
// $j for the nested loop counters
for ($j = 0; $j < 100; $j++) {
}
}
// $ret for return variables
function foo() {
$ret['bar'] = get_bar();
$ret['stuff'] = get_stuff();
return $ret;
}
// $k and $v in foreach
foreach ($some_array as $k => $v) {
}
// $q, $r and $d for mysql
$q = "SELECT * FROM table";
$r = mysql_query($q);
while ($d = mysql_fetch_assocr($r)) {
}
// $fp for file pointers
$fp = fopen('file.txt','w');
11 - Capitalize SQL Special Words
Database interaction is a big part of most web applications. If you are writing raw SQL queries, it is a good idea to keep them readable as well.
Even though SQL special words and function names are case insensitive, it is common practice to capitalize them to distinguish them from your table and column names.
SELECT id, username FROM user; UPDATE user SET last_login = NOW() WHERE id = '123' SELECT id, username FROM user u LEFT JOIN user_address ua ON(u.id = ua.user_id) WHERE ua.state = 'NY' GROUP BY u.id ORDER BY u.username LIMIT 0,20
12 - Separation of Code and Data
This is another principle that applies to almost all programming languages in all environments. In the case of web development, the “data” usually implies HTML output.
When PHP was first released many years ago, it was primarily seen as a template engine. It was common to have big HTML files with a few lines of PHP code in between. However, things have changed over the years and websites became more and more dynamic and functional. The code is now a huge part of web applications, and it is no longer a good practice to combine it with the HTML.
You can either apply the principle to your application by yourself, or you can use a third party tool (template engines, frameworks or CMS’s) and follow their conventions.
Popular PHP Frameworks:
Popular Template Engines:
Popular Content Management Systems
13 - Alternate Syntax Inside Templates
You may choose not to use a fancy template engine, and instead go with plain inline PHP in your template files. This does not necessarily violate the “Separation of Code and Data,” as long as the inline code is directly related to the output, and is readable. In this case you should consider using the alternate syntax for control structures.
Here is an example:
<div class="user_controls">
<?php if ($user = Current_User::user()): ?>
Hello, <em><?php echo $user->username; ?></em> <br/>
<?php echo anchor('logout', 'Logout'); ?>
<?php else: ?>
<?php echo anchor('login','Login'); ?> |
<?php echo anchor('signup', 'Register'); ?>
<?php endif; ?>
</div>
<h1>My Message Board</h1>
<?php foreach($categories as $category): ?>
<div class="category">
<h2><?php echo $category->title; ?></h2>
<?php foreach($category->Forums as $forum): ?>
<div class="forum">
<h3>
<?php echo anchor('forums/'.$forum->id, $forum->title) ?>
(<?php echo $forum->Threads->count(); ?> threads)
</h3>
<div class="description">
<?php echo $forum->description; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
This lets you avoid lots of curly braces. Also, the code looks and feels similar to the way HTML is structured and indented.
14 - Object Oriented vs. Procedural
Object oriented programming can help you create well structured code. But that does not mean you need to abandon procedural programming completely. Actually creating a mix of both styles can be good.
Objects should be used for representing data, usually residing in a database.
class User {
public $username;
public $first_name;
public $last_name;
public $email;
public function __construct() {
// ...
}
public function create() {
// ...
}
public function save() {
// ...
}
public function delete() {
// ...
}
}
Procedural functions may be used for specific tasks that can be performed independently.
function capitalize($string) {
$ret = strtoupper($string[0]);
$ret .= strtolower(substr($string,1));
return $ret;
}
Object-Oriented Programming in PHP
Take your skills to the next level with this Premium video course.
15 - Read Open Source Code
Open Source projects are built with the input of many developers. These projects need to maintain a high level of code readability so that the team can work together as efficiently as possible. Therefore, it is a good idea to browse through the source code of these projects to observe what these developers are doing.
16 - Code Refactoring
When you “refactor,” you make changes to the code without changing any of its functionality. You can think of it like a “clean up,” for the sake of improving readability and quality.
This doesn’t include bug fixes or the addition of any new functionality. You might refactor code that you have written the day before, while it’s still fresh in your head, so that it is more readable and reusable when you may potentially look at it two months from now. As the motto says: “refactor early, refactor often.”
You may apply any of the “best practices” of code readability during the refactoring process.
I hope you enjoyed this article! Any that I missed? Let me know via the comments.
Blog Post: How do I get the title of a dialog from a dialog resource?
[SAP] (Site Home)A customer submitted the following question: We are developing automated tests for our application. Among other things, our application uses property sheets, which means that the name of the tab is stored as the title of the dialog template resource. Since we want our automated tests to run on all language versions of our application, we don't want to hard-code the tab names in our automated test. I have not been able to find any information on how to programmatically extract the dialog titles ...
A customer submitted the following question:
We are developing automated tests for our application. Among other things, our application uses property sheets, which means that the name of the tab is stored as the title of the dialog template resource. Since we want our automated tests to run on all language versions of our application, we don't want to hard-code the tab names in our automated test. I have not been able to find any information on how to programmatically extract the dialog titles from the dialog resources. Any pointers would be appreciated.
I replied with some pointers:
- Classic dialog templates use DLGTEMPLATE.
- Extended dialog templates use DLGTEMPLATEEX.
- Walkthrough of a classic dialog template.
- Walkthrough of an extended dialog template.
The customer was grateful for the pointers, then asked:
Then the only way to do this is to load the dialog resource and parse the data looking for the string I want? Is it even possible to do this in C#?
Well it depends on what your definition of "the only way" is.
At the end of the day, somebody has to load the dialog resource and parse it, because after all that is what you said you want to do: "I want to get the title of the dialog from the dialog resource." The alternative is, what, psychic powers?
There is no dialog template parsing library that comes with Win32. If you don't want to do the parsing, then maybe you can find somebody else who will. And if you're lucky, that other person may even have provided a C# interface.
Blog Post: Using SCVMM 2012, NetApp SMI-S provider, and Visio to visualize storage
[Enterprise] (Site Home)Hello everyone, So the VMM team finally announced BETA at MMS in March. One request we heard from you during the event is the need to visualize storage. VMM 2012 goes a long way to integrate storage automation into VMM using SMI-S based providers. Through these providers, VMM gets a lot of great data. You can use this data to visualize your storage environment. Below is one example of how I modified an existing NetApp PowerShell script that generates a Visio diagram with aggregate, volume, an ...
Hello everyone,
So the VMM team finally announced BETA at MMS in March. One request we heard from you during the event is the need to visualize storage. VMM 2012 goes a long way to integrate storage automation into VMM using SMI-S based providers. Through these providers, VMM gets a lot of great data. You can use this data to visualize your storage environment. Below is one example of how I modified an existing NetApp PowerShell script that generates a Visio diagram with aggregate, volume, and LUN information.
For this example, I downloaded the following:
- Download the NetApp Visio stencils and extract them all into your "My Documents\My Shapes" directory. if the link is broken then go to http://www.visiocafe.com/downloads/netapp/ too get the latest.
- Install the NetApp Data OnTap Powershell ToolKit.
Make sure to launch this script from a client that has the VMM administrator console installed on it. This script assumes LOCALHOST.
Here is a sample of a small environment:
And a more complicated environment:
##script##
$shpFile1 = "\NetApp-Logic-Icons.vss"
$shpFile2 = "\NetApp-Equipment-Icons.vss"
function connect-visioobject ($firstObj, $secondObj) {
$shpConn = $pagObj.Drop($pagObj.Application.ConnectorToolDataObject, 0, 0)
#// Connect its Begin to the 'From' shape:
$connectBegin = $shpConn.CellsU("BeginX").GlueTo($firstObj.CellsU("PinX"))
#// Connect its End to the 'To' shape:
$connectEnd = $shpConn.CellsU("EndX").GlueTo($secondObj.CellsU("PinX"))
}
function add-visioobject ($mastObj, $item) {
Write-Host "Adding $item"
# Drop the selected stencil on the active page, with the coordinates x, y
$shpObj = $pagObj.Drop($mastObj, $x, $y)
# Enter text for the object
$shpObj.Text = $item
#Return the visioobject to be used
return $shpObj
}
#Establish connection to VMM Server (assumes the VMM server is running on the same machine executing the script)
$vmm = Get-VMMServer localhost | Out-Null
#list the arrays under VMM's management
Get-SCStorageArray | ft Name, Model, FirmwareVersion
$FASName = read-host "Enter the Name of your NetApp array"
If ($FASName -eq "") { Write-Host "No selection made, script now exiting." ; exit }
#Get array, pools, LUNs
$array = Get-SCStorageArray -Name $FASName.Trim()
$allPools = @()
$allPools = Get-SCStoragePool | where {$_.StorageArray -eq $array}
$allLUNs = @()
$allLUNs = Get-SCStorageLogicalUnit | where {$allPools -contains $_.StoragePool}
#construct the volumes manually from the list of LUNs and their containing pool
$allVols = @()
#get all volumes
Foreach ($lun in $allLUNs)
{
$volName = $lun.Name.split("/")
$volume = New-Object PSObject
Add-Member -MemberType NoteProperty -Name Name -Value ($volName[2] + "/") -InputObject $volume
Add-Member -MemberType NoteProperty -Name StoragePool -Value $lun.StoragePool -InputObject $Volume
$hashmatch = $false
$allVols += $volume
}
$allVols = $allVols | Select-Object -Unique Name, StoragePool
# Create an instance of Visio and create a document based on the Basic Diagram template.
$AppVisio = New-Object -ComObject Visio.Application
$AppVisio.AutoLayout = $true
$docsObj = $AppVisio.Documents
$DocObj = $docsObj.Add("Basic Network Diagram.vst")
# Set the active page of the document to page 1
$pagsObj = $AppVisio.ActiveDocument.Pages
$pagObj = $pagsObj.Item(1)
# Load a set of stencils and select one to drop
$stnPath = [system.Environment]::GetFolderPath('MyDocuments') + "\My Shapes"
$stnObj1 = $AppVisio.Documents.Add($stnPath + $shpFile1)
$FlexVOLObj = $stnObj1.Masters.Item("FlexVol")
$poolObj = $stnObj1.Masters.Item("Raid Grp Aggregate Storage")
$LUNObj = $stnObj1.Masters.Item("Cylinder")
$stnObj2 = $AppVisio.Documents.Add($stnPath + $shpFile2)
$FASObj = $stnObj2.Masters.Item("FAS3000 Double controllers")
#Draw objects
$y = $allPools.Count * 1.50 / 2
$x = 1.50
$FASObj = add-visioobject $FASObj $FASName
$x = 3.50
$y += 2
Foreach ($pool in $allPools) {
$poolObj = add-visioobject $poolObj $pool.Name
connect-visioobject $FASObj $poolObj
$y += 1.5
Foreach ($volume in $allVols) {
If ($volume.StoragePool -eq $pool) {
$x += 2.50
$volInfo = "Volume Name: " + $volume.Name # + "`r`n" + "Total Size (GB): " + "{0:n2}" -f + ($volume.SizeTotal / 1gb) + "`r`n" + "Size Used: " + "{0:n2}" -f ($volume.SizeUsed / 1gb)
$FlexVOLObj = add-visioobject $FlexVOLObj $volInfo
connect-visioobject $poolObj $FlexVOLObj
$t_x = $x
$t_y = $y
Foreach ($lun in $allLUNs) {
If (($lun.StoragePool -eq $pool) -and ($lun.Name -match $volume.Name)) {
$x += .5
$y += .25
$lunInfo = "LUN Name: " + $lun.Name + "`r`n" + "Total Size (GB): " + "{0:n2}" -f ($lun.TotalCapacity / 1024 / 1024)
$LUNObj = add-visioobject $LUNObj $lunInfo
connect-visioobject $FlexVOLObj $LUNObj
}
}
$x = $t_x
$y = $t_y
}
}
$x = 3.50
$y += 2.50
}
# Resize to fit page
$pagObj.ResizeToFitContents()
You can find the original script here: http://communities.netapp.com/docs/DOC-6411
--Hector
New Articles Published for week ending 3/26/11
[Virtualization] (VMware)New Articles Published for week ending 3/26/11 VMware ESX High Availability fails to configure with error: HA agent on xxxxxx in cluster xxxxx in xxxxxxx has an error: error while running health check script (1021173) Date Published: 3/25/2011 How to set up sudo with active directory accounts (1027766) Date Published: 3/21/2011 When using NetXen 1G NX3031 or multiple 10G NX2031 devices, ESX hosts fail to boot with the error: Out of interrupt vectors err ...
Java Training expandable through Micro SD slot up to 32GB. EDGE and GPRS technology
[Africa] (Afrigator)Java Training expandable through Micro SD slot up to 32GB. EDGE and GPRS technology Java Training expandable through Micro SD slot up to 32GB. EDGE and GPRS technology Free Online Articles Directory Why Submit Articles? Top Authors Top Articles FAQ AB Answers Publish Article 0 && $.browser.msie ) { var ie_ ...
Blog Post: Building Windows Azure Service Part3: Table Storage
[RIA (Rich Internet Apps)] (Site Home)This post shows how to create a project that contains the classes which enables the GuestBook application to store guest entries in the Windows Azure Table Storage. The Table Storage service offers semi-structured storage in the form of tables that contain collections of entities. Entities have a primary key and a set of properties, where a property is a (name, typed-value) pair. The Table Storage service primary key has the following two properties: PartitionKey and RowKey keys that uniq ...
This post shows how to create a project that contains the classes which enables the GuestBook application to store guest entries in the Windows Azure Table Storage.
The Table Storage service offers semi-structured storage in the form of tables that contain collections of entities. Entities have a primary key and a set of properties, where a property is a (name, typed-value) pair. The Table Storage service primary key has the following two properties:
- PartitionKey and RowKey keys that uniquely identify each entity in the table.
- Every entity in the Table Storage service also has a Timestamp system property, which allows the service to keep track of when an entity was last modified. This Timestamp field is intended for system use and should not be accessed by the application.
The Table Storage API provides a TableServiceEntity class that defines the necessary properties, which you can use as the base class for your entities. This API is compliant with the REST API provided by Data Services and allows you to use the.NET Client Library for Data Services to work with data in the Table service using .NET objects.
Although the Table Storage does not enforce any schema for tables, which makes it possible for two entities in the same table to have different sets of properties, the GuestBook application uses a fixed schema to store its data.
In order to use the.NET Client Library for Data Services to access the data in Table Storage, you need to create a context class that derives from TableServiceContext, which itself derives from DataServiceContext in .NET Client Library for Data Services .
The Table Storage API allows applications to create the tables from the context class. For this to happen, the context class must expose each required table as a property of type IQueryable<SchemaClass>, where SchemaClass is the class that models the entities stored in the table.
In this post, you will perform the following tasks:
- Create the data model project.
- Create a schema class for the entities stored by the GuestBook application.
- Create a context class to use .NET Client Library for Data Services to access the data in Table Storage.
- Create a data source class that enables the creation of a data source object. The data source object can be bound to ASP.NET data controls which enable the user to access the Table Storage data.
To create the data model project
- In Solution Explorer dialog right-click the GuestBook solution and select New Project.
- In the Add New Project dialog window, choose the Windows category.
- Select Class Library in the templates list.
- In the Name box enter the GuestBook_Data.
- Click OK.
Figure 5 Creating the Data Model Project
- In Solution Explorer right-click the GuestBook_Data project.
- Select Add Reference.
- In the Add Reference window dialog select .NET tab.
- Add reference to System.Data.Services.Client.
- Repeat the previous steps to add a reference to Microsoft.WindowsAzure.StorageClient.
- Delete the default Class1.cs generated by the template.
To create the schema class
This section shows how to create the class that defines the schema for the GuestBook table entries. Its parent class Microsoft.WindowsAzure.StorageClient.TableServiceEntity defines the properties required by every entity stored in a Windows Azure table.
- Right-click the GuestBookData project, click Add New item.
- In the Add New Item dialog window, select the Code category.
- In the templates list select Class.
- In the Name box enter GuestBookEntry.cs.
- Click Add.
- Open the GuestBookEntry.cs file and replace its content with the following code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.WindowsAzure.StorageClient; namespace GuestBook_Data { /// <summary> /// The GuestBookEntry class defines the schema for each entry (row) /// of the guest book table that stores guest information. /// <remarks> /// The parent class TableServiceEntity defines the properties required /// by every entity that uses the Windows Azure Table Services. These /// properties include PartitionKey and RowKey. /// </remarks> /// </summary> public class GuestBookEntry : Microsoft.WindowsAzure.StorageClient.TableServiceEntity { /// <summary> /// Create a new instance of the GuestBookEntry class and /// initialize the requires keys. /// </summary> public GuestBookEntry() { // The partition key allows partitioning the data so that // there is a separate partition for each day of guest // book entries. The partition key is used to assure // load balancing for data access across fabric nodes (servers). PartitionKey = DateTime.UtcNow.ToString("MMddyyyy"); // Row key allows sorting, this assures the rows are // returned in time order. RowKey = string.Format("{0:10}_{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid()); } // Define the properties that contain guest information. public string Message { get; set; } public string GuestName { get; set; } public string PhotoUrl { get; set; } public string ThumbnailUrl { get; set; } } }
To create the context class
This section shows how to create the context class that enables Windows Azure Table Storage services. Its parent class Microsoft.WindowsAzure.StorageClient.TableserviceContext manages the credentials required to access your account and provides support for a retry policy for update operations.
- Right-click the GuestBookData project, click Add New item.
- In the Add New Item dialog window, select the Code category.
- In the templates list select Class.
- In the Name box enter GuestBookDataContext.cs.
- Click Add.
- Open the GuestBookDataContext.cs file and replace its content with the following code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.StorageClient; namespace GuestBook_Data { /// <summary> /// The GuestBookDataContext class enables Windows /// Azure Table Services. /// <remarks> /// The parent class TableserviceContext manages the credentials /// required to access account information and provides support for a /// retry policy for update operations. /// </remarks> /// </summary> public class GuestBookDataContext : TableServiceContext { /// <summary> /// Create an instance of the GuestBookDataContext class /// and initialize the base /// class with storage access information. /// </summary> /// <param name="baseAddress"></param> /// <param name="credentials"></param> public GuestBookDataContext(string baseAddress, StorageCredentials credentials) : base(baseAddress, credentials) { } /// <summary> /// Define the property that returns the GuestBookEntry table. /// </summary> public IQueryable<GuestBookEntry> GuestBookEntry { get { return this.CreateQuery<GuestBookEntry>("GuestBookEntry"); } } } }
To create the data source class
This section shows how to create the data source class that allows for the creation of data source objects that can be bound to ASP.NET data controls and allow for the user to interact with the Table Storage.
- Right-click the GuestBookData project, click Add New item.
- In the Add New Item dialog window, select the Code category.
- In the templates list select Class.
- In the Name box enter GuestBookDataSource.cs.
- Click Add.
- Open the GuestBookDataSource.cs file and replace its content with the following code.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.StorageClient; namespace GuestBook_Data { /// <summary> /// The GuestBookEntryDataSource class allows for the creation of data /// source objects that can be bound to ASP.NET data controls. These /// controls enable the user to perform Create, Read, Update and Delete /// (CRUD) operations. /// </summary> public class GuestBookEntryDataSource { // Storage services account information. private static CloudStorageAccount storageAccount; // Context that allows the use of Windows Azure Table Services. private GuestBookDataContext context; /// <summary> /// Initializes storage account information and creates a table /// using the defined context. /// <remarks> /// This constructor creates a table using the schema (model) /// defined by the /// GuestBookDataContext class and the storage account information /// contained in the configuration connection string settings. /// Declaring the constructor static assures that the initialization /// tasks are performed only once. /// </remarks> /// </summary> static GuestBookEntryDataSource() { // Create a new instance of a CloudStorageAccount object from a specified configuration setting. // This method may be called only after the SetConfigurationSettingPublisher // method has been called to configure the global configuration setting publisher. // You can call the SetConfigurationSettingPublisher method in the OnStart method // of the web or worker role before calling FromConfigurationSetting. // If you do not do this, the system raises an exception. storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString"); // Create table using the schema (model) defined by the // GuestBookDataContext class and the storage account information. CloudTableClient.CreateTablesFromModel( typeof(GuestBookDataContext), storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials); } /// <summary> /// Initialize context used to access table storage and the retry policy. /// </summary> public GuestBookEntryDataSource() { // Initialize context using account information. this.context = new GuestBookDataContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials); // Initialize retry update policy. this.context.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1)); } /// <summary> /// Gets the contents of the GuestBookentry table. /// </summary> /// <returns> /// results: the GuestBookEntry table contents. /// </returns> /// <remarks> /// This method retrieves today guest book entries by using the current /// date as the partition key. The web role uses this method to /// bind the results to a data grid to display the guest book. /// </remarks> public IEnumerable<GuestBookEntry> Select() { var results = from g in this.context.GuestBookEntry where g.PartitionKey == DateTime.UtcNow.ToString("MMddyyyy") select g; return results; } /// <summary> /// Insert new entries in the GuestBookEntry table. /// </summary> /// <param name="newItem"></param> public void AddGuestBookEntry(GuestBookEntry newItem) { this.context.AddObject("GuestBookEntry", newItem); this.context.SaveChanges(); } /// <summary> /// Update the thumbnail URL for a table entry. /// </summary> /// <param name="partitionKey"></param> /// <param name="rowKey"></param> /// <param name="thumbUrl"></param> public void UpdateImageThumbnail(string partitionKey, string rowKey, string thumbUrl) { var results = from g in this.context.GuestBookEntry where g.PartitionKey == partitionKey && g.RowKey == rowKey select g; var entry = results.FirstOrDefault<GuestBookEntry>(); entry.ThumbnailUrl = thumbUrl; this.context.UpdateObject(entry); this.context.SaveChanges(); } } }
For related topics, see the following posts.
- Building Windows Azure Service Part1: Introduction
- Building Windows Azure Service Part2: Service Project.
- Building Windows Azure Service Part4: Web Role UI Handler
- Building Windows Azure Service Part5: Worker Role Background Tasks Handler
- Building Windows Azure Service Part6: Service Configuration
- Building Windows Azure Service Part7: Service Testing
Microapps and the Art of Widget Maintenance
[Guardian] (Blogposts | guardian.co.uk)The rather strange practice of killing your own servers by pointing Guardian traffic at them, and how not to let that happen otherwise known as what I learnt about caching in a very short space of timeA few weeks ago we rolled out a small update to the website, with luck you didn't notice. Up there is the screenshot, I've added a subtle label to help too!The bit the arrow's pointing to I'll call the Widget for simplicity's sake. It looks pretty much the same as the last one but now with the add ...
The rather strange practice of killing your own servers by pointing Guardian traffic at them, and how not to let that happen ... otherwise known as what I learnt about caching in a very short space of time
A few weeks ago we rolled out a small update to the website, with luck you didn't notice. Up there is the screenshot, I've added a subtle label to help too!
The bit the arrow's pointing to I'll call the Widget for simplicity's sake. It looks pretty much the same as the last one but now with the added Zeitgeist tab instead of the Comments tab of old. We haven't gotten rid of comments, we're just fiddling with them a bit and once all that's sorted they'll come back into the Widget.
So far, so good, so what's the difference? Well, it's about our new(ish)-way-of-doing-stuff called Microapps. Microapps allow us to place content from other sites into our page ... but not in an iframe/javascript hacky type way, it's part of our Open Platform. You can read more about microapps over here. Of course to make sure it all works, we've been testing it with our own content first.
Anatomy of a Microapp
For a microapp to appear on the page you need to have somewhere for it to appear, rather like this ...
The template for this page, a "section front" page, has been told to expect a microapp component to appear in that space. Should things go horribly wrong and the Microapp breaks – which of course it won't, hahahahah – then the space collapses down as though nothing had happened and everything else just carries on as normal.
Now for the smart part – where does the information for the microapp come from? Well, anywhere!
Should you have something particularly newsy that you think should appear on pages on the Guardian, then just get in touch, go through several meetings, sign some papers, a few more meetings that probably involve lawyers ... and if everyone by the end of that agrees it's all a good idea then you set up your server ready to serve the content to appear on the page and it's ready to go.
It'd probably look like this, in an iconic representation anyway ...
The Guardian gets lots and lots and lots of page views each second, sending all those request to your server (or whatever the target server happens to be) would probably be bad for at least two reasons.
- The page load times on the Guardian would be dependant on the remote server
- It'd probably kill the remote server.
Instead we uses a thing called the Microapp Cache, which essentially sits between us and them/you. We, the Guardian, ask the cache for the latest version of the information. You, the source of the information tell us (using nift Cache-Headers) how often we should ask for fresh information.
This allows the owner of the remote server to control just how fast we kill it instead. Then the fine dance around freshness can start. There's no point caching information for two hours at a time, if the data to display is supposed to be updated every five minutes for example. But maybe you can get away with caching "real-time" information for 30 seconds if it stops your server falling over.
Back to our case study
Right, so we have the Zeitgeist. You can read all about how that works over here: Behind the curtains of Zeitgeist, the Open Platform, APIs and Google App Engine. But in short, it tracks what stories are "hot", but in a slightly different way to "Most Viewed", in that it's tracking conversations happening around the internet and not just the raw numbers.
This is all done using Google's AppEngine, a service from Google that allows you to host code in "the cloud". The theory being that it can scale up as needed and shouldn't die ... which is nice.
So how did that work out for the Zeitgeist? Well, we started by turning the new widget on for all article pages. Which get a huge amount of traffic, here's the graph showing what happened to the server...
Over on the left you can see the calm before the storm, the nearly flat line is the widget sat on the staging server, just before deploying it. The first, relatively tiny peak, not that one, the one just before it, at just under 0.200 request/second is everything getting loaded into the microapp cache.
The "Ooops", is because there were extra sections in the Guardian that the Zeitgeist didn't have information about, such as Guardian Extra. When the page was asking for the "Top Viewed" in the Extras section the backend didn't have any information and returned an error (for various dull technical reasons that was a conscious decision). Because the microapp cache doesn't cache the error, the backend was being asked for data each time someone visited the extras section.
With a quick fix rolled out everything calmed down a tick ... also just under 1 request per second isn't exactly panic-stations anyway which means the microapp cache is basically doing its job.
However, I was still seeing these spikes every 15 minutes. Because the Guardian is visited constantly, each section (News, Music, Books, Sport etc) is always being loaded by someone somewhere, the pages were always going into cache and expiring from cache at the same time. So every 15 minutes everything would fall out of the microapp cache which would ask for all of them at the same time.
Here's an improvement...
... after leaving the code long enough to produce a nice looking graph ... I mean, you don't want to fix these thing too soon if there's a good chance they'll make good image for blog posts later ;) ... I then set a separate staggered cache time for each section of the site.
At the right hand side of the graph you can see where the Cache spikes start to go out of sync, until further to the right everything looks nice and calm ... rather like gentle ocean waves.
The wrap up
Right, so, in short, the Guardian's new(ish) microapp framework and microapp cache allows us to put content developed by third parties onto the site. Because when each page asks for the microapp content it passes over its own context the microapp can serve different content based on the current section and so on.
This is all part of the Guardian mutualisation plans, to not only allow our content to spread out across the web (via our APIs), but also to allow other peoples content to show up on our site. Preferably without killing their servers though.
And if you do end up creating content to appear on these pages, then the microapp cache is your friend, but you'll still need to do some thinking and planning on your end to make the most out of it.
Windows Server 2008 R2 File System Technologies
[Windows] (Computing Tech)Windows Server 2008 R2 provides many services that can be leveraged to deploy a highly reliable, manageable, and fault-tolerant file system infrastructure. Windows Volume and Partition Formats When a new disk is added to a Windows Server 2008 R2 system, it must be configured by choosing what type of disk, type of volume, and volume format type will be used. To introduce some of the file system services available in Windows Server 2008 R2, you must understand a disk’s volume partition f ...
Windows Volume and Partition Formats
When a new disk is added to a Windows Server 2008 R2 system, it must be configured by choosing what type of disk, type of volume, and volume format type will be used. To introduce some of the file system services available in Windows Server 2008 R2, you must understand a disk’s volume partition format types.
Windows Server 2008 R2 enables administrators to format Windows disk volumes by choosing either the file allocation table (FAT) format, FAT32 format, or NT File System (NTFS) format. FAT-formatted partitions are legacy-type partitions used by older operating systems and floppy disk drives and are limited to 2GB in size. FAT32 is an enhanced version of FAT that can accommodate partitions up to 2TB and is more resilient to disk corruption. Data stored on FAT or FAT32 partitions is not secure and does not provide many features. NTFS-formatted partitions have been available since Windows NT 3.51 and provide administrators with the ability to secure files and folders, as well as the ability to leverage many of the services provided with Windows Server 2008 R2.
NTFS-Formatted Partition Features
NTFS enables many features that can be leveraged to provide a highly reliable, scalable, secure, and manageable file system. Base features of NTFS-formatted partitions include support for large volumes, configuring permissions or restricting access to sets of data, compressing or encrypting data, configuring per-user storage quotas on entire partitions and/or specific folders, and file classification tagging.
Several Windows services require NTFS volumes; as a best practice, we recommend that all partitions created on Windows Server 2008 R2 systems are formatted using NT File System (NTFS).
File System Quotas
File system quotas enable administrators to configure storage thresholds on particular sets of data stored on server NTFS volumes. This can be handy in preventing users from inadvertently filling up a server drive or taking up more space than is designated for them. Also, quotas can be used in hosting scenarios where a single storage system is shared between departments or organizations and storage space is allocated based on subscription or company standards.
The Windows Server 2008 R2 file system quota service provides more functionality than was included in versions older that Windows Server 2008. Introduced in Windows 2000
Server as an included service, quotas could be enabled and managed at the volume level only. This did not provide granular control; furthermore, because it was at the volume level, to deploy a functional quota-managed file system, administrators were required to create several volumes with different quota settings. Windows Server 2003 also included the volume-managed quota system, and some limitations or issues with this system included the fact that data size was not calculated in real time. This resulted in users exceeding their quota threshold after a large copy was completed. Windows Server 2008 and Windows Server 2008 R2 include the volume-level quota management feature but also can be configured to enable and/or enforce quotas at the folder level on any particular NTFS volume using the File Server Resource Manager service. Included with this service is the ability to screen out certain file types, as well as real-time calculation of file copies to stop operations that would exceed quotas thresholds. Reporting and notifications regarding quotas can also be configured to inform end users and administrators during scheduled intervals, when nearing a quota threshold, or when the threshold is actually reached.
Data Compression
NTFS volumes support data compression, and administrators can enable this functionality at the volume level, allowing users to compress data at the folder and file level. Data compression reduces the required storage space for data. Data compression, however, does have some limitations, as follows:
• Additional load is placed on the system during read, write, and compression and decompression operations.
• Compressed data cannot be encrypted.
Data Encryption
NTFS volumes support the ability for users and administrators to encrypt the entire volume, a folder, or a single file. This provides a higher level of security for data. If the disk, workstation, or server the encrypted data is stored on is stolen or lost, the encrypted data cannot be accessed. Enabling, supporting, and using data encryption on Windows volumes and Active Directory domains needs to be considered carefully as there are administrative functions and basic user issues that can cause the inability to access previously encrypted data.
File Screening
File screening enables administrators to define the types of files that can be saved within a Windows volume and folder. With a file screen template enabled, all file write or save operations are intercepted and screened and only files that pass the file screen policy are allowed to be saved to that particular volume or folder. The one implication with the file screening functionality is that if a new file screening template is applied to an existing volume, files that would normally not be allowed on the volume would not be removed if they are already stored on it. File screening is a function of the File Server Resource Manager service.
File Classification Infrastructure
Windows Server 2008 R2 includes a new feature called the File Classification Infrastructure (FCI). The FCI enables administrators to create classification policies that can be used to identify files and tag or classify files according to properties and policies defined by the file server administrators. FCI can be managed by using the File Server Resource Manager console and allows for file server administrators to identify files and classify these files by setting specific FCI property values to these files based on the folder they are stored in and/or based on the content stored within the file itself. When a file is classified by FCI, if the file is a Microsoft Office file, the FCI information is stored within the file itself and follows the file wherever it is copied or moved to. If the file is a different type of file, the FCI information is stored within the NTFS volume itself, but the FCI information follows the file to any location it is copied or moved to, provided that the destination is an NTFS volume hosted on a Windows Server 2008 R2 system.
Source of Information : Sams - Windows Server 2008 R2 Unleashed
Blog Post: Why does ContentManager.Load or TitleContainer.OpenStream say file not found?
[Business Intelligence] (Site Home)The simple answer is that the file you are trying to load must not actually exist in the location you are trying to load it from! And yet people sometimes get stuck on this error, unable to open their file and with no idea how to figure out why this is failing. I suspect this is a side effect of the Content Pipeline being so automated in XNA. When the usual experience is to just drop an image into Visual Studio, then ContentManager.Load it into your game, there is no need to learn the details o ...
The simple answer is that the file you are trying to load must not actually exist in the location you are trying to load it from!
And yet people sometimes get stuck on this error, unable to open their file and with no idea how to figure out why this is failing. I suspect this is a side effect of the Content Pipeline being so automated in XNA. When the usual experience is to just drop an image into Visual Studio, then ContentManager.Load it into your game, there is no need to learn the details of what happens to the file in between. But if you do not know this, you will not have the tools to debug when things go wrong, or to understand the differences between files built by the Content Pipeline versus deployed some other way.
In fact, the Content Pipeline is just an optional layer over the top of a simple file deployment mechanism. In order to load a file, exactly three things must take place:
- The file must be copied to your build output folder
- The file must be deployed from build output to the target device
- You must specify the right name and path when you load it
Copy to build output folder
Your Visual Studio Solution Explorer contains source files, not build outputs. These source files are not directly available to your game at runtime. To make them available, we must copy them to the build output folder.
The build output folder is located inside whatever directory contains your project. This will typically be called something like bin\x86\Debug. There is a separate output folder for each target platform and build configuration (x86 vs. Xbox 360, Debug vs. Release, etc.)
There are three main ways to arrange for files to end up in this folder:
- Add files to your content project, so the Content Pipeline will compile them, creating .xnb format outputs which can be loaded using ContentManager.Load
- Add files to your main game project and set their Copy to Output Directory property to Copy if newer, so they will be copied directly to the output, from where they can be loaded using TitleContainer.OpenStream
- Customize your MSBuild project XML to add additional file copying tasks
To make sure all your files have been correctly copied, just open up the build output folder in Windows Explorer and take a look at what is there.
Deploy to the target device
When you debug a game on Windows, it runs directly from the build output folder, so no additional deployment is necessary.
When you run on Xbox or package as a .ccgame, the packaging tool gathers all the files from your build output folder (skipping only a few known-to-be-irrelevant formats such as .pdb), so all the same files are sure to be available even though the game actually runs elsewhere.
But on Windows Phone, or if you distribute a Windows game using ClickOnce, the deployment process only includes files that were declared as outputs by MSBuild. This includes all files created by the Content Pipeline or the Copy to Output Directory property, but will leave out anything you manually copied to the output folder, or if you incorrectly customized your MSBuild XML to copy files without also declaring them as build outputs (which is a topic for another day).
To make sure all your files have been correctly packaged for Windows Phone, rename the output .xap package to a .zip extension, so you can open it in Windows Explorer and see what it contains.
Specify the right name and path
TitleContainer.OpenStream paths are relative to the game executable. If the build output folder is bin\x86\Debug:
- To load bin\x86\Debug\cats.txt, call TitleContainer.OpenStream("cats.txt")
- To load bin\x86\Debug\Content\Levels\cats.txt, call TitleContainer.OpenStream("Content/Levels/cats.txt")
ContentManager.Load internally calls TitleContainer.OpenStream, but first it modifies the path in two ways:
- It automatically adds the current value of ContentManager.RootDirectory in front of the supplied path
- It automatically adds the .xnb file extension
So you should not include the .xnb extension when using ContentManager.Load (and also do not include the extension of whatever source file was used to create this .xnb - remember you are loading the compiled output file, not the source asset that was added to Visual Studio Solution Explorer).
The default game template sets ContentManager.RootDirectory to "Content", so:
- To load bin\x86\Debug\Content\cat.xnb, call Content.Load<Texture2D>("cat")
- To load bin\x86\Debug\Content\Levels\cat.xnb, call Content.Load<Texture2D>("Levels/cat")
You can create multiple ContentManager instances with whatever RootDirectory you like. For instance, to load bin\x86\Debug\Foo\Bar\cat.xnb, you could:
- Set RootDirectory to String.Empty, then call Content.Load<Texture2D>("Foo/Bar/cat")
- Set RootDirectory to "Foo/Bar", then call Content.Load<Texture2D>("cat")
Note: this article is about immutable content which is deployed as part of your game package. If you are looking to save and load data at runtime, you want Isolated Storage or StorageContainer instead.
Blog Post: Handling SQL Azure Connections issues using Entity Framework 4.0
[Geography] (Site Home)The underlying platform within SQL Azure consists of many instances of SQL Server, each of which is managed by the SQL Azure fabric. The SQL Azure fabric is a distributed computing system composed of tightly integrated networks, servers, and storage. It enables automatic failover, load balancing, and automatic replication between physical servers. Troubleshooting Connection-loss Errors Connection-loss is not uncommon when databases encounter resource shortages. A unique feature of SQL Azure i ...
The underlying platform within SQL Azure consists of many instances of SQL Server, each of which is managed by the SQL Azure fabric. The SQL Azure fabric is a distributed computing system composed of tightly integrated networks, servers, and storage. It enables automatic failover, load balancing, and automatic replication between physical servers.
Troubleshooting Connection-loss Errors
Connection-loss is not uncommon when databases encounter resource shortages. A unique feature of SQL Azure is its ability to monitor and rebalance active and online user databases in the Microsoft data centers automatically. To achieve this, SQL Azure continuously gathers and analyzes database usage statistics and will terminate connections when necessary.
SQL Azure provides a large-scale multi-tenant database service on shared resources. In order to provide a good experience to all SQL Azure customers, your connection to the service may be closed due to several conditions. For more info regarding SQL Azure and how it handles connections, see:
Therefore, this is an issue we have to handle in our ADO.NET code, and specifically, in this case, in our Entity Framework 4.0 code.
It is recommended that you implement the retry logic in your SQL Azure applications to handle the connection and transaction failures. Addressing such issues in SQL Azure applications is no different than addressing them in on-premise SQL Server applications.
The established connections are the connections that are returning data or the open connections in the connection pool, or the connections being cached in the client side variables. To provide a seamless user experience when a connection is closed, re-establish the connection and then re-execute the failed commands or the query.
In case you are using plain ADO.NET, you can check the link I wrote up-above. But if you are using Entity Framework, it must be implemented in a certain way (code down-below is just a possibility, you could do your own).
The problem we are trying to address is the issue of connection.Open() handing back timed out connections from the pool. The idea/solution is to manually take control of the connection by explicitly opening the connection on the context in an OnContextCreated partial method. What you can then do is test the connection to make sure that it is indeed a healthy connection, by running a low overhead command over it. It is not perfect because of the overhead of issuing the extra command, but it does work.
Basically, you should implement a ‘retry’ logic that you can incorporate into the OnContextCreated() method.
|
partial void OnContextCreated() { // Explicitly open the connection on the context so that the context does not // open/close every time requests are issued Connection.Open(); // Get the underlying store connection so we can issue a dummy command var storeConnection = (SqlConnection)((EntityConnection)Connection).StoreConnection; try { // Issue a low overhead test command new SqlCommand("declare @i int", storeConnection).ExecuteNonQuery(); } catch (SqlException) { // Close and reopen the connection // ADO.NET should take care of invalidating the pool // since an exception was already detected Connection.Close(); Connection.Open(); } } |
In case you are using one of the new T4 templates in EF 4.0 (POCO or Self Tracking Entities), you’ll need to change the T4 file.
For example, in our NLayered Sample-App (you can download it from http://microsoftnlayerapp.codeplex.com) we are using Self Tracking Entities, so we added code changing our T4 template file called ‘MainModuleUnitOfWork.Context.tt’. Basically we added the OnContextCreated() method and we’re referencing to it from the constructors of our UoW class implementation (EF Context, in our case, the class called MainModuleUnitOfWork).
Create ASP.NET Server Controls from Scratch
[Web Design] (Nettuts+)In this tutorial, you will learn how to build an ASP.NET server control by creating a HTML5 video player control. Along the way, we’ll review the fundamental process of server control development from scratch. Introduction ASP.NET comes with its own set of server-side controls, so why create our own? By creating our own controls, we can then build powerful, reusable visual components for our Web application’s user interface. This tutorial will introduce you to the process of ASP.NE ...
In this tutorial, you will learn how to build an ASP.NET server control by creating a HTML5 video player control. Along the way, we’ll review the fundamental process of server control development from scratch.
Introduction
ASP.NET comes with its own set of server-side controls, so why create our own?
By creating our own controls, we can then build powerful, reusable visual components for our Web application’s user interface.
This tutorial will introduce you to the process of ASP.NET server control development. You’ll also see how creating your own controls can simultaneously improve the quality of your Web applications, make you more productive and improve your user interfaces.
ASP.NET custom controls are more flexible than user controls. We can create a custom control that inherits from another server-side control and then extend that control. We can also share a custom control among projects. Typically, we will create our custom control in a web custom control library that is compiled separately from our web application. As a result, we can add that library to any project in order to use our custom control in that project.
HTML5 Video Overview
Until now, there has never been a native way to display video on a web page. Today, most videos are shown, via the use of a plugin (like Flash or Silverlight). However, not all browsers have the same plugins. HTML5 specifies a standard, native way to include video, with the video element.
Currently, there are two widely supported video formats for the video element: Ogg files [encoded with Theora and Vorbis for video and audio respectively] and MPEG 4 files [encoded with H.264 and AAC].
To show a video in HTML5, this is all we need:
<video width="320" height="240" controls="controls">
<source src="movie.ogg" type="video/ogg" />
<source src="movie.mp4" type="video/mp4" />
</video>
The controls attribute is for adding play, pause and volume controls. Without this attribute, your video would appear to be only an image. It is also always a good idea to include both the width and height attributes. The following table shows all attributes of the <video> element:
- autoplay: Specifies that the video will start playing as soon as it is ready
- controls: Specifies that controls will be displayed, such as a play button
- height: The height of the video player
- loop: Specifies that the media file will start over again, every time it is finished
- preload: Specifies that the video will be loaded at page load, and ready to run. Ignored if “autoplay” is present
- src: The URL of the video to play
- width: The width of the video player
- poster: The URL of the image to show while no video data is available
Step 0: Getting Started
All that is required to get started is a copy of Visual Studio of Visual Web Developer Express. If you don't have the full version of Visual Studio, you can grab the free Express Edition.
The HTML5 video player that we will create here is only a simple video player that will render whatever native interface the browser provides. Browsers that support HTML5 video have video players built in, including a set of controls (play/pause etc.), so you will see a different interface for each browser when running this control.
Step 1: Creating a Custom Control Project
First, we need to create a new class library project to hold our custom controls. By creating the custom control in a separate class library, we can compile the project into a separate DLL and use the custom control in any application that requires it.
Open your ASP.NET project with Visual Studio or Visual Web Developer. In Solution Explorer, right click the solution name, and select Add New Project from the context menu. In the Add New Project dialog box, choose the project type to be a Web project, and select ASP.NET Server Control as the template, like so:
Name the project CustomControls. Click OK. The new ASP.NET Server Control project is created, and Visual Studio also provides you with a simple Web control to start with. Delete this custom control because we don't need it.
Step 2: Adding a Web Custom Control to the Project
In Solution Explorer, right click the CustomControls project, and select Add New Item from the context menu. In the Add New Item dialog box, choose the category type to be a Web category, and select ASP.NET Server Control in the templates.
Name the new custom control VideoPlayer. Click Add. The new custom control (VideoPlayer.cs) is created and added to the CustomControls project.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:VideoPlayer runat=server></{0}:VideoPlayer>")]
public class VideoPlayer : WebControl
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get
{
String s = (String)ViewState["Text"];
return ((s == null) ? "[" + this.ID + "]" : s);
}
set
{
ViewState["Text"] = value;
}
}
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
}
}
The code above is the default code generated by Visual Studio for a web control library. To start working with VideoPlayer.cs, we need to modify the code above. The first thing that we should do is delete everything between the class declaration line and the end of the class. That leaves us with this code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:VideoPlayer runat=server></{0}:VideoPlayer>")]
public class VideoPlayer : WebControl
{
}
}
As you see above, the VideoPlayer class derives from the System.Web.UI.WebControl class. In fact, all ASP.NET server-side controls derive from the WebControl class.
Step 3: Modifying the Class Declaration Line
The class declaration line in the default code also specifies the default property for the VideoPlayer control as the Text property. The VideoPlayer control that we create here doesn't have a property called Text. So, delete the reference to Text as the default property. After all the modifications, the VideoPlayer.cs code file should look like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomControls
{
[ToolboxData("<{0}:VideoPlayer runat=server></{0}:VideoPlayer>")]
public class VideoPlayer : WebControl
{
}
}
Step 4: Adding Properties
In this step, we will add some properties to the VideoPlayer control to handle the control's behaviour. The following is the list of properties that we will add to the VideoPlayer.cs code file:
-
VideoUrl: A string property which specifies the URL of the video to play. -
PosterUrl: A string property which specifies the address of an image file to show while no video data is available. -
AutoPlay: A boolean property to specify whether the video should automatically start playing or not, when the webpage is opened. -
DisplayControlButtons: A boolean property that specifies whether the player navigation buttons are displayed or not. -
Loop: A boolean property that specifies whether the video will start over again or not, every time it is finished.
Add the following code to the VideoPlayer class:
private string _Mp4Url;
public string Mp4Url
{
get { return _Mp4Url; }
set { _Mp4Url = value; }
}
private string _OggUrl = null;
public string OggUrl
{
get { return _OggUrl; }
set { _OggUrl = value; }
}
private string _Poster = null;
public string PosterUrl
{
get { return _Poster; }
set { _Poster = value; }
}
private bool _AutoPlay = false;
public bool AutoPlay
{
get { return _AutoPlay; }
set { _AutoPlay = value; }
}
private bool _Controls = true;
public bool DisplayControlButtons
{
get { return _Controls; }
set { _Controls = value; }
}
private bool _Loop = false;
public bool Loop
{
get { return _Loop; }
set { _Loop = value; }
}
After we have added the above properties, the VideoPlayer class should look like
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomControls
{
[ToolboxData("<{0}:VideoPlayer runat=server></{0}:VideoPlayer>")]
public class VideoPlayer : WebControl
{
private string _Mp4Url;
public string Mp4Url
{
get { return _Mp4Url; }
set { _Mp4Url = value; }
}
private string _OggUrl = null;
public string OggUrl
{
get { return _OggUrl; }
set { _OggUrl = value; }
}
private string _Poster = null;
public string PosterUrl
{
get { return _Poster; }
set { _Poster = value; }
}
private bool _AutoPlay = false;
public bool AutoPlay
{
get { return _AutoPlay; }
set { _AutoPlay = value; }
}
private bool _Controls = true;
public bool DisplayControlButtons
{
get { return _Controls; }
set { _Controls = value; }
}
private bool _Loop = false;
public bool Loop
{
get { return _Loop; }
set { _Loop = value; }
}
}
}
Step 5: Creating the RenderContents Method
The primary job of a server control is to render some type of markup language to the HTTP output stream, which is returned to and displayed by the client. It is our responsibility as the control developer to tell the server control what markup to render. The overridden RenderContents method is the primary location where we tell the control what we want to render to the client.
Add the following override RenderContents method to the VideoPlayer class:
protected override void RenderContents(HtmlTextWriter output)
{
}
Notice that the RenderContents method has one method parameter called output. This parameter is an HtmlTextWriter object, which is what the control uses to render HTML to the client. The HtmlTextwriter class has a number of methods you can use to render your HTML, including AddAttribute and RenderBeginTag.
Step 6: Adding Tag Attributes
Before we write the code to render the <video> element, the first thing to do is add some attributes for it. We can use the AddAttribute method of the HtmlTextWriter object to add attributes for HTML tags.
Append the following code into the RenderContents method:
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ID);
output.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString());
output.AddAttribute(HtmlTextWriterAttribute.Height, this.Height.ToString());
if (DisplayControlButtons == true)
{
output.AddAttribute("controls", "controls");
}
if (PosterUrl != null)
{
output.AddAttribute("poster", PosterUrl);
}
if (AutoPlay == true)
{
output.AddAttribute("autoplay", "autoplay");
}
if (Loop == true)
{
output.AddAttribute("loop", "loop");
}
You can see that, by using the AddAttribute method, we have added seven attributes to the tag. Also notice that we are using an enumeration, HtmlTextWriterAttribute, to select the attribute we want to add to the tag.
After we have added the code above, the RenderContents method should look like so:
protected override void RenderContents(HtmlTextWriter output)
{
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ID);
output.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString());
output.AddAttribute(HtmlTextWriterAttribute.Height, this.Height.ToString());
if (DisplayControlButtons == true)
{
output.AddAttribute("controls", "controls");
}
if (PosterUrl != null)
{
output.AddAttribute("poster", PosterUrl);
}
if (AutoPlay == true)
{
output.AddAttribute("autoplay", "autoplay");
}
if (Loop == true)
{
output.AddAttribute("loop", "loop");
}
}
Step 7: Rendering the <video> Element
After adding some tag attributes for the video element, it's time to render the <video> tag with its attributes onto the HTML document. Add the following code into the RenderContents method:
output.RenderBeginTag("video");
if (OggUrl != null)
{
output.AddAttribute("src", OggUrl);
output.AddAttribute("type", "video/ogg");
output.RenderBeginTag("source");
output.RenderEndTag();
}
if (Mp4Url != null)
{
output.AddAttribute("src", Mp4Url);
output.AddAttribute("type", "video/mp4");
output.RenderBeginTag("source");
output.RenderEndTag();
}
output.RenderEndTag();
We use the RenderBeginTag method of output object to render the opening tag of the video element, and RenderEndTag to render its closing tag. We also added the <source> element between the <video> element. The video element allows multiple source elements. Source elements can link to different video files. The browser will use the first recognized format.
The RenderContents method should look like this after we have added the code above:
protected override void RenderContents(HtmlTextWriter output)
{
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ID);
output.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString());
output.AddAttribute(HtmlTextWriterAttribute.Height, this.Height.ToString());
if (DisplayControlButtons == true)
{
output.AddAttribute("controls", "controls");
}
if (PosterUrl != null)
{
output.AddAttribute("poster", PosterUrl);
}
if (AutoPlay == true)
{
output.AddAttribute("autoplay", "autoplay");
}
if (Loop == true)
{
output.AddAttribute("loop", "loop");
}
output.RenderBeginTag("video");
if (OggUrl != null)
{
output.AddAttribute("src", OggUrl);
output.AddAttribute("type", "video/ogg");
output.RenderBeginTag("source");
output.RenderEndTag();
}
if (Mp4Url != null)
{
output.AddAttribute("src", Mp4Url);
output.AddAttribute("type", "video/mp4");
output.RenderBeginTag("source");
output.RenderEndTag();
}
output.RenderEndTag();
}
Notice that the order in which we place the AddAttributes methods is important. We place the AddAttributes methods directly before the RenderBeginTag method in the code. The AddAttributes method associates the attributes with the next HTML tag that is rendered by the RenderBeginTag method, in this case the video tag.
Step 8: Removing the Span Tag
By default, ASP.NET will surround the control tag with a <span> element when rendering the control's HTML markup. If we have provided an ID value for our control, then the Span tag will also, by default, render an ID attribute. Having the tags can sometimes be problematic, so if we want to prevent this in ASP.NET, we can simply override the Render method and call the RenderContents method directly. Here's how to do that:
protected override void Render(HtmlTextWriter writer)
{
this.RenderContents(writer);
}
After we have added the code above, the VideoPlayer class should look like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CustomControls
{
[ToolboxData("<{0}:VideoPlayer runat=server></{0}:VideoPlayer>")]
public class VideoPlayer : WebControl
{
private string _Mp4Url;
public string Mp4Url
{
get { return _Mp4Url; }
set { _Mp4Url = value; }
}
private string _OggUrl = null;
public string OggUrl
{
get { return _OggUrl; }
set { _OggUrl = value; }
}
private string _Poster = null;
public string PosterUrl
{
get { return _Poster; }
set { _Poster = value; }
}
private bool _AutoPlay = false;
public bool AutoPlay
{
get { return _AutoPlay; }
set { _AutoPlay = value; }
}
private bool _Controls = true;
public bool DisplayControlButtons
{
get { return _Controls; }
set { _Controls = value; }
}
private bool _Loop = false;
public bool Loop
{
get { return _Loop; }
set { _Loop = value; }
}
protected override void RenderContents(HtmlTextWriter output)
{
output.AddAttribute(HtmlTextWriterAttribute.Id, this.ID);
output.AddAttribute(HtmlTextWriterAttribute.Width, this.Width.ToString());
output.AddAttribute(HtmlTextWriterAttribute.Height, this.Height.ToString());
if (DisplayControlButtons == true)
{
output.AddAttribute("controls", "controls");
}
if (PosterUrl != null)
{
output.AddAttribute("poster", PosterUrl);
}
if (AutoPlay == true)
{
output.AddAttribute("autoplay", "autoplay");
}
if (Loop == true)
{
output.AddAttribute("loop", "loop");
}
output.RenderBeginTag("video");
if (OggUrl != null)
{
output.AddAttribute("src", OggUrl);
output.AddAttribute("type", "video/ogg");
output.RenderBeginTag("source");
output.RenderEndTag();
}
if (Mp4Url != null)
{
output.AddAttribute("src", Mp4Url);
output.AddAttribute("type", "video/mp4");
output.RenderBeginTag("source");
output.RenderEndTag();
}
output.RenderEndTag();
}
protected override void Render(HtmlTextWriter writer)
{
this.RenderContents(writer);
}
}
}
Our control is now finished! All we have left to do is build the project before we use it on a ASP.NET web page.
Step 9: Building the Project
It's time to build the project. Select Build, and then click Build Solution from the main menu.
After building the project, the next step is to add the VideoPlayer control into the Toolbox Explorer.
Step 10: Adding VideoPlayer Control to the Visual Studio Toolbox
- To add the VideoPlayer control to the Toolbox, right click in the
Toolbox Explorer -
Choose Itemsfrom the context menu - Click the
Browsebutton in the Choose Toolbox Items dialog box - Navigate to the ASP.NET project directory
- Go to the
CustomControlsdirectory - Open the
Bin\Debugdirectory (Visual Studio builds debug versions by default.) - Select the
CustomControls.DLLassembly and click on theOpenbutton
VideoPlayer will appear in the Choose Toolbox Items dialog box as shown in the image above. The check box will show it as selected. As soon as you click the OK button in the Choose Toolbox Items dialog box, the new VideoPlayer control will appear in the toolbox.
Step 11: Placing the VideoPlayer Control on ASP.NET Web Page
To see how the control works, we need to give it a home. Add a new page to the website. Right click the ASP.NET project from the Solution Explorer. Select Add New Item, and add a Web Form. Name the Web Form VideoPlayerTest.aspx.
To place the control on the page, switch to Design mode. Drag the VideoPlayer control from the Toolbox and drop it onto the VideoPlayerTest.aspx design view.
The following Listing shows how the control is declared on the page:
<cc1:VideoPlayer ID="VideoPlayer1" runat="server" Mp4Url="videos/movie.mp4" OggUrl="videos/movie.ogg" Width="400" Height="300" />
The following line of code is what Visual Studio added to the ASPX file to accommodate the control. You can see it by selecting the Source tab from the bottom of the code window in Visual Studio. The Register directive tells the ASP.NET runtime where to find the custom control (which assembly) and maps it to a tag prefix.
<%@ Register assembly="CustomControls" namespace="CustomControls" tagprefix="cc1" %>
We can now test the control.
Summary
In this tutorial, you learned how to create your own ASP.NET custom server control from scratch. You now know every step of the process – from how to create a web custom control library project, how to add properties to a custom control, how to render the HTML markup of the control to the client, and, finally, how to use the ASP.NET custom control in a web form.
Hopefully, you now have the skills to create custom controls that have all the functionality of the standard ASP.NET server-side controls. Thank you so much for reading!
Oracle Support Master Note for Streams Downstream Capture - 10g and 11g [Video] (Doc ID 1264598.1)
[Corporate Blogs] (Blogs.oracle.com Recent Posts (English-language only))Master Note for Streams Downstream Capture - 10g and 11g [Video] (Doc ID 1264598.1) Copyright (c) 2008, Oracle Corporation. All Rights Reserved. In this Document Purpose Scope and Application Master Note for Streams Downstream Capture - 10g and 11g [Video] Downstream Capture Transport Considerations in Downstream Capture Instantiation - Implications for Primary Database Recommended Parameter Settings TroubleShooting Performance Issues Ongoing Streams Rela ...
Master Note for Streams Downstream Capture - 10g and 11g [Video] (Doc ID 1264598.1)
Copyright (c) 2008, Oracle Corporation. All Rights Reserved.
In this Document
Purpose
Scope and Application
Master Note for Streams Downstream Capture - 10g and 11g [Video]
Downstream Capture
Transport Considerations in Downstream Capture
Instantiation - Implications for Primary Database
Recommended Parameter Settings
TroubleShooting
Performance Issues
Ongoing Streams Related Activities on the Primary Database
References
Applies to:
Oracle Server - Enterprise Edition - Version: 10.1.0.2 to 11.2.0.2 - Release: 10.1 to 11.2
Information in this document applies to any platform.
Oracle Server Enterprise Edition - Version: 10.1.0.2 to 11.2.0.2
Purpose
This note provides background and troubleshooting information applicable in a Streams Downstream Capture Environment . Its main purpose as a master note is to bring together reference material in a form which allows a clear understanding of what is required in the configuration as well as to where to look when there are issues. Its focus is setup and understanding relevant parameters settings which are required.
Scope and Application
This document is intended for anyone with an interest in understanding Streams downstream capture. Where scripts details are references these are provided for reference to DBAs and Application developers. It is likely these will require modification in view of the fact that the examples presented are for illustration purposes. In order to have a better insight into how the examples work, please ensure you have read and understood the information below.
Whilst every care has been taken to ensure that the details are correct and accurate , code should be thoroughly tested to verify it is fit for intended purpose.
Please note , the approach adopted in relation to knowledge has been to improve Oracle's product documentation . Thus, relevant content in relation to downstream capture is now found in the primary reference guides for Streams ; if possible, please refer to the latest documentation :
- Streams Concepts and Administration;
- Streams Replication Administrator's Guide
A useful reference in relation to downstream capture specific details is found in :
Streams Configuration Best practices ; this was created with respect to 10.2 but is applicable to all later versions.
Further questions in relation to this subject may be answered in :
FAQ on Downstreams Capture, Note:394575.1 .
Master Note for Streams Downstream Capture - 10g and 11g [Video]
Downstream Capture
Downstream capture is a configuration in which the capture process runs on a database other than the source database. The source database has to be in archive log mode since the key aspect of downstream Capture of offloading the burden or load of processing the redo generated on the Primary to the downstream database.
The following types of downstream capture configurations are possible: real-time downstream capture and archived-log downstream capture. The main consideration of a real-time downstream capture process is that it requires standby logs on the downstream database.
Requirements
- the source database must be running at least Oracle Database 10g and the downstream capture database must be running the same release of Oracle as the source database or later;
- the downstream database must be running Oracle Database 10g Release 2 to configure real-time downstream capture. In this case, the source database must be running Oracle Database 10g Release 1 or later;
- the operating system on the source and downstream capture sites must be the same, but the operating system release does not need to be the same. However, the downstream sites can use a different directory structure from the source site;
- the hardware architecture on the source and downstream capture sites must be the same. For example, a downstream capture configuration with a source database on a 32-bit Sun system must have a downstream database that is configured on a 32-bit Sun system. Other hardware elements, such as the number of CPUs, memory size, and storage configuration, can be different between the source and downstream sites.
Advantages
- capturing changes does not happen at the source database, this the overhead for supporting Streams on the source database is greatly reduced;
- capture process administration is greatly simplified, as you can locate capture processes with different source sites on the same database;
- provide additional protection against database failure and corruption as the redo logs are shipped to a downstream database.
Real-Time Downstream Capture
In a real-time downstream capture environment, the primary database is configured so that the transport services use the log writer process (LGWR) at the source database to send redo data to the downstream database either synchronously or asynchronously. At the same time, the LGWR records redo data in the online redo log at the source database.
A remote file server process (RFS) at the downstream database receives the redo data over the network and stores the redo data in the standby redo logs.
A log switch at the source database causes a log switch at the downstream database and the ARCX process at the downstream database archives the current standby redo log file as necessary.
The real-time downstream capture process captures changes from the standby redo log whenever possible and from the archived standby redo log files whenever necessary. A capture process can capture changes in the archived standby redo log files if it falls behind. When it catches up, it resumes capturing changes from the standby redo log.
Further detail relating to configuring real time downstream capture , with consideration for RAC source databases is found in : How To Configure Streams Real-Time Downstream Environment, Note:753158.1 .
Please note , configuration of Real-Time Downstream differs from Archive-Log Downstream Capture configuration in the following ways :
- log_archive_* parameters are different ; refer below for example parameter definitions;
- the downstream capture database must be in archive log mode;
- standby logs must be created in the downstream capture database ;
- the capture process should be altered to use real time downstream mine
Archived-Log Downstream Capture
The primary database is configured so that the transport services use the archiver process (ARCX) at the source database. Archived logs from the source database are shipped to the downstream database and the capture process captures the relevant changes.
Multiple capture process can be configured in downstream database to capture changes and these capture processes can capture changes from archived redo log files of multiple source databases.
Further details on how to set up archive log downstream capture are found in : How To Setup Schema Level Streams Replication with a Downstream Capture Process with Implicit Log Assignment, Note:733691.1.
Video - Demonstration of Archive Log Downstream Capture based on : Note:733691.1 (05:00)
Transport Considerations in Downstream Capture
A fundamental requirement is that the source can ship redo to the destination database. The sys user on the source database must be able to connect as sys as sysdba at the downstream database. This will require configuration of Secure Sockets Layer (SSL) protocol or more commonly the addition of a remote login password file at the downstream database. If the source database has a remote login password file, then copy it to the appropriate directory on the downstream capture database system.
- configure archive log mode on the source ; the downstream database does not need to running in archive log mode if it is running archived-log downstream capture ;
- configure network sqlnet tns entries to allow connectivity between the databases ;
- create a password file to ensure that user sys on the source can connect as sys on the downstream capture database. The following example creates password files for both databases however it is the downstream database which requires the password file.
cd $ORACLE_HOME/dbs
orapwd file=orapwstrm1 password=oracle entries=20
cp orapwstrm1 orapwstrm2
show parameter remote_login_passwordfile
-- should show exclusive (non RAC) ; shared (RAC).
-- shutdown / startup
Following the above, you should be able to connect from the source to the destination as follows :
connect sys/oracle@strm1.net as sysdba
select * from global_name;
-- Following is the important connection requirement
connect sys/oracle@strm2.net as sysdba
select * from global_name;
For more details refer to the Streams documentation. The main streams activities and their significance in a downstream capture environment are outlined below.
Instantiation - Implications for Primary Database
Recall that instantiation in a Streams environment involves :
- preparing the object for instantiation at the source database.
- ensuring a copy of the object exists at the destination database;
- setting the instantiation SCN for the database object at the destination (apply) database. The instantiation SCN is an SCN connected with the source database from which the apply process will apply captured changes to the object
Although the capture process is running on a different database , there are still implications for the primary database which need to be understood so that the downstream capture environment can be created successfully. These details are discussed as follows.
The examples detailed in Note:753158.1 and Note:733691.1 do not separate the build operation from the creation of the capture process. Furthermore , they do not refer to a flashback scn on the primary database corresponding to the first_scn returned from the build - refer to 'create capture' below below.
Notes :
- any export of data from the primary should always be made in consistent mode export ;
- if there is little or no change to the primary database , the referenced notes serve as a reasonable guide . However, this can potentially present a gap in the changes captured relative to when the datapump export was performed which may result in confusing ora-1403 errors after setup.
Streams can be configured so that the build activity is separated from the create capture operation. When performed as a separate operation, the build returns a first scn which can be used in subsequent configuration activities :
- when the capture process is created on the downstream database it should refer to the first SCN returned from the build;
- if datapump / export is used to move the data to the downstream database, a flashback_scn corresponding to the first scn from the build should be used ;
- when setting the instantiation scn , this should again refer to the file scn returned from the build.
When the above considerations are met, the data on both databases as well as the relevant capture and apply processes are in sync when the processes are started .
Create Capture
In a downstream capture environment , the capture process has to be created separately from add_{tables|schema|database}_rules. The create_capture operation will internally generate a copy of the data dictionary at the primary in the redo stream when a database link to the primary is specified. Additionally, it creates the capture process referring to the first scn returned from the primary.
A build operation can be performed on the primary as a separate operation using :
set serveroutput on
DECLARE
scn NUMBER;
BEGIN
DBMS_CAPTURE_ADM.BUILD(first_scn => scn);
DBMS_OUTPUT.PUT_LINE('First SCN Value = ' || scn);
commit;
END;
/
The first_scn returned should then be used as the first_scn to the create_capture() call.
Prepare {tables|schema|database} instantiation
This will result in supplemental logging being generated for the related objects at the primary database when run at the downsteam database .
If there is no database link to the primary , perform the prepare at the primary or add the necessary supplemental logging at the primary database for the related objects.
Add {tables|schema|database} rules
This operation also performs a prepare (refer above) operation at the primary database for the related objects and configures relevant rules at the downstream database . This functions also configures relevant rules to allow the related object(s) to be processed by the relevant process.
Set {table|schema|database} instantiation scn
This operation sets the {table|schema|database} instantiation scn for the Apply process . This is the SCN at which changes will be applied from.
An export / import does not set the instantiation SCN for user objects. Therefore, this is also a required action if you use export / import to move objects from one database to another. Once you have performed this action, verify that instantiation SCNs are set for the schema and tables where appropriate as follows before starting the apply and capture processes.
connect strmadmin/strmadmin
select * from dba_apply_instantiated_global;
select * from dba_apply_instantiated_schemas;
select * from dba_apply_instantiated_objects;
Recommended Parameter Settings
The main issue which may be encountered will relate to log transport or redo not appearing at the downstream database. Before discussing what to look at and where consider the relevant parameters which should be examined. Understanding the related parameters and their values is important in ensuring that set up is free from significant issues. The following outlines which parameters are required.
- it is assumed in the following that log_archive_dest_1 is used as the archive log location and both databases is in archive log mode;
- the log_archive_* parameter settings have their meaning defined in the Oracle Server reference manual;
- directory specification values are for illustrative purposes only
Real-Time Downstream Capture Parameter Values
The following serves as a guide of parameter requirements in setting up real-time downstream capture. For further details consult the Streams Concepts and Administration Guide.
Primary database
alter system set log_archive_dest_1 = 'location=/bugmnt12/em/celclnx7/SR3.2274614446/user/strm1 mandatory reopen=5' scope=spfile
/
alter system set log_archive_dest_state_1 = enable scope=spfile
/
alter system set log_archive_format = 'strm1_%t_%s_%r.dbf' scope=spfile
/
alter system set log_archive_dest_2 = 'service=strm2.net lgwr async noregister valid_for=(online_logfile,all_roles) db_unique_name=strm2' scope=spfile -- Note 1
/
alter system set log_archive_dest_state_2 = enable scope=spfile -- Note 2
/
alter system set log_archive_config='send,dg_config=(strm1,strm2)' scope=both -- Note 3
/
Note 2 - this can be set to 'deferred' until the setup is complete; then amended to enable , i.e. initially there is no need to ship the redo information until the setup is complete ; once complete , this parameter can be set to 'enable' to activity activity;
Note 3 - this details the unique names involved, again issue show parameter db_unique_name on both databases; also indicates that this is the sending database.
Downstream Database
alter system set log_archive_dest_1 = 'location=/bugmnt12/em/celclnx7/SR3.2274614446/user/strm2 mandatory reopen=5 valid_for=(online_logfile, primary_role)' scope=spfile
/
alter system set log_archive_dest_state_1 = enable scope=spfile
/
alter system set log_archive_format = 'strm2_%t_%s_%r.dbf' scope=spfile
/
alter system set log_archive_dest_2 = 'location=/bugmnt12/em/celclnx7/SR3.2274614446/user/standby mandatory valid_for=(standby_logfile, primary_role)' scope=spfile -- note 4
/
alter system set log_archive_dest_state_2 = enable scope=spfile
/
alter system set log_archive_config='receive,dg_config=(strm1,strm2)' scope=both -- note 5
/
Note 5 - this details the unique names involved, again show parameter db_unique_name on both databases; also indicates that this is the sending database.
Archived-Log Downstream Capture Parameter Values
The following serves as a guide of parameter requirements in setting up archived log downstream capture. For further details consult the
Streams Concepts and Administration Guide.
Primary database
alter system set log_archive_dest_1 = 'location=/bugmnt12/em/celclnx7/SR3.2274614446/user/strm1 mandatory reopen=5' scope=spfile
/
alter system set log_archive_dest_state_1 = enable scope=spfile
/
alter system set log_archive_format = 'strm1_%t_%s_%r.dbf' scope=spfile
/
alter system set log_archive_dest_2 = 'service=strm2.net arch async noregister valid_for=(online_logfile,all_roles) template=/bugmnt12/em/celclnx7/SR3.2274614446/user/logsfromstrm1/strm1_%t_%s_%r.dbf db_unique_name=strm2' scope=spfile -- Note 6
/
alter system set log_archive_dest_state_2 = enable scope=spfile -- Note 7
/
alter system set log_archive_config='send,dg_config=(strm1,strm2)' scope=both -- Note 8
/
Note 7 - as per Note 2 above;
Note 8 - this details the unique names involved, again show parameter db_unique_name on both databases; also indicates that this is the sending database.
Downstream Database
alter system set log_archive_dest_1 = 'location=/bugmnt12/em/celclnx7/SR3.2274614446/user/strm2 mandatory reopen=5 valid_for=(online_logfile, primary_role)' scope=spfile
/
alter system set log_archive_dest_state_1 = enable scope=spfile
/
alter system set log_archive_format = 'strm2_%t_%s_%r.dbf' scope=spfile
/
alter system set log_archive_dest_2 = 'location=/bugmnt12/em/celclnx7/SR3.2274614446/user/standby mandatory valid_for=(standby_logfile, primary_role)' scope=spfile -- Note 9
/
alter system set log_archive_dest_state_2 = enable scope=spfile
/
alter system set log_archive_config='receive,dg_config=(strm1,strm2)' scope=both -- Note 10
/
Note 10 - this details the unique names involved, again issue show parameter db_unique_name on both databases ; also indicates that this is the receiving database.
TroubleShooting
If downstream Capture is not working , it is usually the case that the primary database cannot connect to the downsteam database. i.e typically, the cause may be ora-1017: invalid username/password; logon denied.
If the remote_login_passwordfile is not configured or is not the same as that on the primary or the sys passwords are different you will likely encounter ora-1017.
Please also refer to the section Troubleshooting Implicit Capture in the 11.2 Streams Concepts and Administration Guide.
Errors
Once the environment is active, the most common type of error which is likely to be encountered in a streams environment will be data related , i.e this may typically be : ora-1403.
For more information, refer to:
Master Note for Troubleshooting Streams Apply Errors ORA-1403, ORA-26787 or ORA-26786 , Note:265201.1.
During setup, it may not be obvious what is happening. Please make sure that you review the source alert.log file as well as the related log writer or archiver trace files - whichever process will be managing redo transport. These will likely point to the root cause of the issue which will likely be configuration / parameter related .
Tracing Log Transport Issues
If there is still no clear indication as to why redo is not being shipped , log transport related trace can be enabled by the setting of the parameter : LOG_ARCHIVE_TRACE. For more information , refer to the Oracle Database Reference Guide for trace levels.
Note : most issues will not require any reference to this parameter . Most problems will likely have a visible error which will be connected with an obvious cause and resolution.
Performance Issues
Issues relating to performance in a downstream capture environment should be treated in the same way as issues in a local capture environment. If a capture process is waiting on redo , this would suggest that there is a lag in the transport of redo information from the primary ; this will typically depend on whether real-time downstream capture or archived-log downstream capture is used. Other issues should be addressed in the same manner as with a local capture process and streams . A useful starting reference is :
Master Note for Streams Performance Recommendations , Note:335516.1.
Ongoing Streams Related Activities on the Primary Database
Once the downstream capture activity is active, there is one piece of relevant maintenance which should be scheduled at the primary database. Recall in the discussion above, that when the Capture process is created, it will refer to a build in the redo which is linked to its first_scn value. If there is any problem which demands that a Capture process has to be dropped and recreated , the new capture process can refer back to the most recent build scn. This will be the first_scn of the new capture process.
References
NOTE:335516.1 - Master Note for Streams Performance Recommendations
NOTE:394575.1 - FAQ on Downstreams Capture
NOTE:413353.1 - 10.2 Best Practices For Streams in RAC environment
NOTE:418755.1 - Master Note for Streams Recommended Configuration
NOTE:733691.1 - How To Setup Schema Level Streams Replication with a Downstream Capture Process with Implicit Log Assignment
NOTE:753158.1 - How To Configure Streams Real-Time Downstream Environment
NOTE:265201.1 - Master Note for Troubleshooting Streams Apply Errors ORA-1403, ORA-26787 or ORA-26786
Must Have Tools for Hackers
[Africa] (Afrigator)Hi guys, am here again with another gbam. This tools are selected from varieties of tools around the web. They are what i call the "must have" tools for tech geeks. These collection has been with me for some time now so i've decided to share it with you guys. In case you're knew to hacking, you can see my previous post on the most important basic hacking skills you must acquire and you can also read my prevous post on 1000 Hacking Tutorial. If you have any question you can use the comment box be ...
Setup of ODI 11g Agents for High Availability
[IT, Enterprise] (Blogs.oracle.com Recent Posts (all languages))Thanks to Sachin Thatte for contributing this article! Introduction Oracle recently introduced the latest release of Oracle Data Integrator (ODI) Enterprise Edition 11g the summer of 2010. With this offering, Oracle raised the bar on performance, scalability and highly availability for data movement and transformation solution. With the unique E-LT (Extract, Load and Transform) approach, the total cost of ownership for ODI is a fraction of its competition. In this article we will show how to t ...
Thanks to Sachin Thatte for contributing this article!
Introduction
Oracle recently introduced the latest release of Oracle Data Integrator (ODI) Enterprise Edition 11g the summer of 2010. With this offering, Oracle raised the bar on performance, scalability and highly availability for data movement and transformation solution. With the unique E-LT (Extract, Load and Transform) approach, the total cost of ownership for ODI is a fraction of its competition.In this article we will show how to take advantage of ODI's E-LT data movement and transformation capabilities in a highly scalable and highly available way.
ODI performs the execution and orchestration of the E-LT jobs via the lightweight ODI runtime Agent. The ODI Agent can be deployed in a standalone environment as well as on the industry leading WebLogic Server. It is with the WebLogic server deployment that ODI achieves scalability and high availability. For deploying on WebLogic server ODI ships with a JEE application and a domain configuration deployment template to assist in configuration of the agent. ODI also supports using Oracle RAC database for storing ODI Master and Work Repository data.
Deployment Topology
The following is a depiction of ODI agent deployed in a cluster, connected to Oracle RAC repository.
By deploying ODI Agent in a clustered WebLogic server, the incoming requests can be supported by a farm of machines each able to take on a slice of the incoming requests. The Proxy or Load Balancer that receives all the incoming requests can intelligently distribute the load across the managed servers to maximize the capacity. Depending upon your business needs the managed servers on which ODI Agents are deployed can be increased or decreased without disrupting your business.
It is recommended that the ODI Repositories (Master and Work) should be deployed on Oracle RAC for load balancing and high availability on the database side. Using Oracle RAC will allow ODI to retry failed connections in case one of the RAC node goes down and continue the execution of running ODI Sessions without failing the E-LT tasks.
ODI agent supports an internal scheduling service. This scheduling service is used to execute the E-LT jobs automatically based on the schedule that a user can define. The schedule for the job can be one time or recurring. There are various fault handling options to handle any unforeseen errors that can be caused due to environmental problems. This scheduling service runs as a singleton when deployed on a WebLogic cluster. If for any reason the managed server where the Scheduling service is deployed goes down or is brought down for maintenance, the scheduling service automatically migrates to one of the available managed servers in the cluster. Thus it provides a fail-safe and highly available scheduling service for executing ODI schedules.
How do I set up ODI for HA?
- Configure a named ODI Agent in ODI Studio ODI Agents are declared and defined in ODI Studio Topology panel. Define the ODI Agent that will be used in WebLogic cluster for high availability and for scalable deployment. The host/port defined in the Agent configuration must match with the Load Balancer host/port address. All ODI Agent requests will be routed through this host/port address.
- Generate ODI Agent dynamic template for deployment Using ODI Studio, generate a deployment template for ODI Agent. When generating the deployment template, you can choose the Data Servers that should get deployed as JEE Data Sources so that they are managed and pooled via WebLogic configuration.
- Deploy ODI Agent using the dynamically generated Agent deployment template. Use WebLogic configuration wizard to deploy the template generated in previous step to a WebLogic cluster. This will allow you to create the set of managed servers that are part of a WebLogic cluster to which the ODI Agent should be deployed. You can also deploy the ODI Agent template on an existing WebLogic Domain.
- Configure Coherence cache properties in JAVA_OPTIONS for managed server startup.
- tangosol.coherence.localport configuration parameter defines the port which a node in the cluster can use for coherence cluster. It would be pinged by an agent nodes to detect coherence cluster existence and other coherence communication.
- All the ODI Agents deployed on a cluster must be connected to the same Coherence cluster cloud. This enables the agents to share the knowledge of the tasks performed by each of them as well as allow for the Scheduling Service migration when needed. Following properties are introduced to configure the Coherence listen addresses. oracle.odi.coherence.wkaN : The host name of a Managed Server oracle.odi.coherence.wkaN.port : Coherence Unicast port configured on that Managed Server Where N = 1..10
Node 1: "-Dtangosol.coherence.localport=8095
-Doracle.odi.coherence.wka1=<MS1_HOSTNAME>
-Doracle.odi.coherence.wka1.port=8095
-Doracle.odi.coherence.wka2=<MS2_HOSTNAME>
-Doracle.odi.coherence.wka2.port=8096" Node 2: "-Dtangosol.coherence.localport=8096
-Doracle.odi.coherence.wka1=<MS1_HOSTNAME>
-Doracle.odi.coherence.wka1.port=8095
-Doracle.odi.coherence.wka2=<MS2_HOSTNAME>
-Doracle.odi.coherence.wka2.port=8096"
Such an ODI agent deployment will be highly available and will allow scalability to address the load on the Agent based on your business needs.
Conclusion
There is a lot more to be leveraged from a highly available cluster deployed ODI Agent. ODI also supports ODI Master and Work repositories deployed in Oracle RAC configuration to ensure that repository connectivity is always available. This article gets you introduced and quickly up to speed on this new feature of ODI 11g. You can read all about High Availability for Oracle Data Integrator in the documentation here. You can also read details on Coherence and how to configure Coherence here.
Apply the DRY Principle to Build Websites With ExpressionEngine 2
[Web Design] (Nettuts+)ExpressionEngine 2 is a wonderful CMS and arguably the most designer-friendly one out there, used by many well-known names like A List Apart, Andy Clarke and Veerle Pieters. Ironically, however, its default configuration is poorly suited for use in a professional web development workflow, which usually involves multiple sites, servers, and developers. This tutorial will show you how to customize ExpressionEngine 2 so you can hit the ground running with a rock solid yet flexible starting point t ...
ExpressionEngine 2 is a wonderful CMS and arguably the most designer-friendly one out there, used by many well-known names like A List Apart, Andy Clarke and Veerle Pieters. Ironically, however, its default configuration is poorly suited for use in a professional web development workflow, which usually involves multiple sites, servers, and developers.
This tutorial will show you how to customize ExpressionEngine 2 so you can hit the ground running with a rock solid yet flexible starting point that can easily be deployed to multiple environments in minutes.
Overview
I’m not a programmer. However, the programming mantra don’t repeat yourself, or the DRY principle for those acronym lovers among us, has really begun to resonate within me as I get more involved both with web development and running my own business. In fact, DRY is good advice for living out your life in general. Repeating yourself costs more time up front, and potentially a lot more down the road if you have to go back and make the same change in multiple places.
Plus it’s a hindrance for personal growth because if you’re doing something you’ve already done, you’re not learning something new. What’s better is to identify those places where you do repeat yourself and come up with a system to standardize that task or piece of data.
A Little History
When I first started working with ExpressionEngine a year and a half ago, it was a one-off project and I was a novice designer. Needless to say, the DRY mentality was the furthest thing from my mind. I was happily humming along, mucking with settings as the situation dictated, not documenting anything and having a blast with custom fields and template groups, those things that make EE a designer’s dream come true. It was sort of like my first date with the software. In the end, I liked EE so much that I decided to get exclusive and “marry” it as my CMS of choice for all future projects.
After about the third or fourth site, however, I began to see flaws in our relationship (as is liable to happen when you really get familiar with something) and got frustrated doing menial, repetitive tasks related to deploying and managing EE. This was especially apparent with some ongoing projects which required twice or thrice weekly updates from development to staging to live servers. It got to the point that I was spending nearly as much time managing deployments as I was actually coding.
The Solution
Not content to lose money and slave away at boring drudgery, I sought to tidy up the mess.
What follows is the fruit of my and others’ labor, a guide to applying the DRY principle to developing and deploying sites with EE.
It walks you through how I’ve tweaked and customized ExpressionEngine 2’s flabby, nonsensical default configuration into a lean, efficient workhorse that takes nearly all the repetition out of working with EE. Specifically, these modifications will:
- Provide a starting point with all the commonly used addons installed and settings turned on so you’re not running the installation wizard and starting from scratch every time.
- Integrate EE with a version control system of your choice for rapid deployment to multiple web servers or developers’ workstations and easy management of code. My experience is with SVN but all the principles apply to Git as well.
- Centralize all settings and configs to facilitate easy migration from one server to another, so launching and pushing updates is a cinch rather than a headache.
This has been a rather large endeavour and I couldn’t have done it alone. A big thanks go out to the following people, who helped me whether they knew it or not:
- Tony Chester of OnWired and Casey Reid of Clearfire Studios, for getting me going in the right direction.
- Jamie Pittock of Erskine Design and his awesome talk at EECI 2010, for figuring out a lot of this stuff in EE 1 and inspiring me to get it going in EE 2.
- Jeff Freeman of Futurity Web Design for helping me with a little bit of PHP to make it happen (like I said, I’m not a programmer).
Step 1: Download and Installation
For your sanity’s sake get a fresh copy of the latest build of EE 2 before you do any of this. Download and install as normal, preferably on a local server, as you’ll be making lots of changes to the files. Leave out the Agile Records templates when you are prompted.
Step 2: Pouring Out the Config Soup
If you’ve ever had to migrate ExpressionEngine from one server to another, you know that this task is no easy feat; in fact, it’s a complete nightmare if you’re unprepared. A lot of this stems from the fact that ExpressionEngine stores config variables and server paths all over creation, to the point that it’s difficult to track them all down and adjust them when you move servers.
Deployment requires updating the URL and path information in literally about a dozen places. It’s clumsy, time-consuming, and error prone.”
Kenn Christ’s quote on your right is right on. Fortunately, there’s another way. Rather than editing all those variables in a dozen places in the control panel and probably forgetting some, you can consolidate them in one place—the config file. By default, ExpressionEngine stores config information that you will need to worry about in two files. These are:
-
system/expressionengine/config/config.php -
system/expressionengine/config/database.php
Ditching database.php
As you might imagine, database.php stores the MySQL database connection information. I suppose EllisLab takes the position that it’s easier to find the DB information if it’s in it’s own aptly named file, but I’m going to argue the opposite. This is DRY, damn it! I’d rather open one file and edit my settings from one place, not two, so I did away with database.php altogether. Well, not quite, but I did take all the database settings from it and move them to config.php with a little PHP.
<?php
if ( ! defined('EXT')){
exit('Invalid file request');
}
/* THIS FILE WILL NEED PERMISSIONS SET TO 400 OR SIMILAR SO EE DOESN'T OVERWRITE IT. */
require 'config.php';
break;
}
?>
You’ll never need to open database.php again.
Consolidating config.php
So now we have our database settings in config.php, plus a bunch of other stuff that you’ve probably edited in the control panel but had no idea it could hang out here as well, like template preferences, themes preferences, CP URL etc. Great, so we’re all done and we can deploy from our version control repository to any server we please with the tap-tap-tap of an SSH command.
Wrong. Here’s our problem; when EE moves from one server to another, things like the database connection settings need to change to reflect the new server environment. If we want to use EE with a version control system (and trust me, we do), then every time we deploy a working copy to a new server, we would need to download a copy of the config.php, edit the settings so they’re correct for that server, FTP it back up to the server, and make sure to tell our version control to ignore it when we issue a commit or update.
Best case scenario, we’d have a separate, non version-controlled config file for each additional server on which the site resides. For me (and I’m a one-man show) that’s:
- iMac’s local server
- Macbook Pro’s local server
- staging server
- live server
Add another couple developers if you work at an agency and you’re looking at a lot of these buggers running around. So what happens when you need to change another config variable, like the license number? Do you email yourself and other developers a copy of this file and upload it to all servers one by one? DRY, my friends, DRY. The only logical answer is a single, version controlled config.php file which can accommodate all server environments.
Nonsense, you might say, but thanks to some clever PHP it is indeed possible. As you can see, the PHP case syntax looks for an IP address and serves what I call environmental config variables based on that IP. Now the only things you need to know and change when you deploy to a new server are the IP address and the database connection information, which should be readily available to you.
switch ( $_SERVER['SERVER_ADDR'] ) {
// local
case '127.0.0.1' :
$db['expressionengine']['hostname'] = "localhost";
$db['expressionengine']['username'] = "root";
$db['expressionengine']['password'] = "password";
$db['expressionengine']['database'] = "local-db";
break;
// staging
case '72.10.54.22' :
$db['expressionengine']['hostname'] = "mysql.exampleserver.com";
$db['expressionengine']['username'] = "admin";
$db['expressionengine']['password'] = "password";
$db['expressionengine']['database'] = "staging-db";
break;
// live
case '82.335.65.67' :
$db['expressionengine']['hostname'] = "mysql.exampleserver.com";
$db['expressionengine']['username'] = "admin";
$db['expressionengine']['password'] = "password";
$db['expressionengine']['database'] = "live-db";
break;
}
At this point I want to distinguish between what I call environmental variables and universal variables.
- Environmental variables are different on each server environment.
- Universal variables are the same no matter which server the site resides on, so they go outside the IP switch/case syntax.
These are things like the server paths and URLs to the themes folder, template folder, CAPTCHAs, the license number, basically anything besides the aforementioned database information and IP address (these are all commented in the included file for reference).
Did you hear me say that server paths and URLs stay the same no matter what server you’re on? Yes you did. As long as your site’s folder structure remains the same in every instance (and if you’re on version control it obviously will), PHP variables detect the root server path and URL and fill them in for you. Why EE doesn’t do this to begin with baffles me, but I digress. No more forgetting to change the server path to your themes folder when you migrate servers and spending an hour figuring out why you have a blank screen instead of a CP. Anyone excited yet?
/* |-------------------------------------------------------------------------- | ExpressionEngine Config Items |-------------------------------------------------------------------------- */ $config['app_version'] = "211"; $config['license_number'] = "0000-0000-0000-0000"; $config['debug'] = "1"; $config['install_lock'] = ""; $config['system_folder'] = "admin"; $config['doc_url'] = "http://expressionengine.com/user_guide/"; $config['is_system_on'] = "y"; $config['cookie_prefix'] = ""; $config['site_name'] = "Flourish Interactive Codebase"; $config['allow_extensions'] = "y"; /* General -------------------------------------------------------------------*/ $config['site_index'] = ""; $config['site_url'] = "http://".$_SERVER['HTTP_HOST']; $config['server_path'] = $_SERVER['DOCUMENT_ROOT']; $config['cp_url'] = $config['site_url']."/".$config['system_folder']; /* Universal database connection settings -------------------------------------------------------------------*/ $active_group = 'expressionengine'; $active_record = TRUE; $db['expressionengine']['dbdriver'] = "mysql"; $db['expressionengine']['dbprefix'] = "exp_"; $db['expressionengine']['pconnect'] = FALSE; $db['expressionengine']['swap_pre'] = "exp_"; $db['expressionengine']['db_debug'] = FALSE; $db['expressionengine']['cache_on'] = FALSE; $db['expressionengine']['autoinit'] = FALSE; $db['expressionengine']['char_set'] = "utf8"; $db['expressionengine']['dbcollat'] = "utf8_general_ci"; $db['expressionengine']['cachedir'] = $config['server_path'].$config['system_folder']."/expressionengine/cache/db_cache/"; /* Member directory paths and urls -------------------------------------------------------------------*/ $config['avatar_url'] = $config['site_url']."/uploads/system/avatars/"; $config['avatar_path'] = $config['server_path']."/uploads/system/avatars/"; $config['photo_url'] = $config['site_url']."/uploads/system/member_photos/"; $config['photo_path'] = $config['server_path']."/uploads/system/member_photos/"; $config['sig_img_url'] = $config['site_url']."/uploads/system/signature_attachments/"; $config['sig_img_path'] = $config['server_path']."/uploads/system/signature_attachments/"; $config['prv_msg_upload_path'] = $config['server_path']."/uploads/system/pm_attachments/"; /* Misc directory paths and urls -------------------------------------------------------------------*/ $config['theme_folder_url'] = $config['site_url']."/themes/"; $config['theme_folder_path'] = $config['server_path']."/themes/"; /* Templates Preferences -------------------------------------------------------------------*/ $config['save_tmpl_files'] = "y"; $config['tmpl_file_basepath'] = $config['server_path']."/templates/"; $config['site_404'] = "404/index"; $config['strict_urls'] = "n"; // END EE config items
Keep in mind that a universal variable can become an environmental variable if you need it to. Let’s say that you want to change your site name automatically based on the server it’s on, so you can tell at a glance if you’re looking at the local, dev or live version of your site. Just delete the variable from the “universal variables” area and copy it into each IP case syntax, assigning it whatever value you want.
Step 3: Cleaning House
Let’s face it; the default install of ExpressionEngine includes a lot of files you don’t need, especially if you’re a professional developer who’s not poking around for the first time.
These include the theme files for the Agile Records example site, smileys, wiki themes, and a lot more. Why fatten your site unnecessarily? Put EE on a diet and delete all this stuff, you can always grab a fresh copy and add it back in the unlikely event you need it for a wiki, forum or other community-based site. Delete only what makes sense for you, but I’ve done about a dozen EE sites and never used any of it.
-
/themes/wiki_themes -
/themes/site_themes/agile_records -
/themes/profile_themes/agile_records -
/images/smileys -
/images/avatars
Step 4: Create a Standard Top-level Folder Structure and .htaccess File
Like many tasks in web development, there’s no one right way to go about this, but what’s important is that you pick a way and stick to it. Some people like to put their static asset files (images, css, js, swf, etc.) in a /themes/site_themes/examplesite folder. I prefer to put each asset folder on the top level because I’m lazy and don’t like to click through three levels of subfolders to access these files during development, plus I like nice short URLs in my HTML and CSS.
Now that I’ve gotten used to a standard structure, I do not create additional top level files or folders unless absolutely necessary (you’ll see why in a minute). This is what my top level structure looks like.
-
.htaccess– will explain more in a minute -
system– rename this please -
css -
favicon.ico -
fw– this is short for “framework” e.g. my CSS background images -
images– non CMS-managed content images -
index.php -
js -
robots.txt -
templates -
themes– CP and fieldtype themes -
uploads– where all CMS-managed docs and images go
Now I get around to talking about .htaccess. It’s a mystery to many developers and frankly it is to me too, but I know enough to use it to remove that unsightly index.php from EE’s otherwise pretty URLs. I use a variant of the exclude method from the ExpressionEngine Wiki. This is in no way guaranteed to work on your web host, but it’s worked for me on MAMP Pro, HostGator and MediaTemple, both (gs) and (dv). The usual caveats apply, e.g. mod_rewrite must be enabled in Apache’s http.conf etc.
If you’re using this method of removing index.php and wish to add a new top level file or folder to your site (and I mean a “real” file or folder, not an EE entry, template or template group), you’ll need to add an exception in .htaccess or else that file/folder will be inaccessible.
Step 5: Install Your Default Add-ons and Configure Them
After developing several EE sites, there are add-ons I am either unwilling or unable to live without. These are the best the EE development community has to offer and they have the honor of being installed in my codebase so that every new site has them from the get-go. They are (and these are all free):
- Freeform by Solspace
- Dive Bar by Pixel & Tonic
- ED Image Resizer by Erskine Design
- Low Seg2Cat by Lodewijk Schutte (Low)
- Accessible CAPTCHA by Greg Salt (Purple Dogfish)
- Character Limiter by EllisLab
Don’t just install these, configure them. For example, I have set up all my email notification templates for Freeform, created additional custom form fields based on what I usually use for a standard contact form, and I have a template called contact.html which has the front end form code in it, including JavaScript validation and a success message. Even if I need to add a field or two, or move that form code into a different template, it’s a matter of tweaking, not creating from scratch every time. DRY. Minus CSS styling, that form is ready to go out of the box.
Be on the lookout for another article by me soon as I discuss these and a couple of commercial add-ons for EE2 in more detail.
Step 6: Set Up Your Client’s Member Group
Giving unlimited access to my client is scary for both them and me.
This is one of those things you likely forget to do until you’re nearly finished with the site, but it doesn’t need to be if it’s in your codebase. The default EE administrator account belongs to the Super Admins member group, which necessarily has access to everything. Giving unlimited access to my client is scary for both them and me, so I create a second member group called Admins. I usually wait until they’ve picked an email address before I actually make their account but that only takes a couple seconds once you have the member group permissions defined.
In this member group I’ve turned off all access to the templates, site and member administration, communication module, and add-ons. All that most clients need to do is create and edit content, and maybe view their Freeform submissions. That’s it. Simplify their life and yours and take away what they don’t need. Again, I’ve had to tweak this before but a starting point is better than starting from scratch.
Step 7: Working With Your Codebase
Congratulations, you should now have a far superior starting point for your next ExpressionEngine project. So that you can add to it and reuse it, create a new project in your version control and commit your customized ExpressionEngine codebase as version number one. Below are examples of some common operations you’ll likely need to do once you’ve got new projects in the pipeline (may vary depending on server setup, or if you’re using Git instead of SVN).
Create a New Project – 10 minutes
- Clear all caches of your codebase project.
- Export database and import under new project name using PHPMyAdmin or similar.
- SVN export a copy of your codebase to the working copy folder of a new SVN project. VERY IMPORTANT: Note that I said export, not checkout.
- Set the following folders and their contents to permissions 777:
-
/templates -
/uploads(or whatever your upload folder is named) -
/system/expressionengine/cache/db_cache
-
- Add DB connection info for new DB to
config.php. Change the site name, license numbers and any other preferences you need to change. - Load up your control panel and change the file upload preferences. These are stored in the database and cannot be put in the config for some asinine reason.
- Go nuts.
Deploy a Site to a New Server – 10 minutes
- Clear all caches.
- Export and import database using PHPMyAdmin or similar.
- Find IP address and database info and add a new IP case section to
config.php. - Commit
config.phpto your repository. - Check out your site’s repository to the public_html folder of your new server.
- If it’s a local server use your SVN client.
- If it’s a remote server use the SSH command svn checkout http://samplerepository.com/sampleproject/ . The space and dot after the trailing slash checks out the contents of the folder to the current folder, otherwise you’ll get public_html/sampleproject/index.php if you leave out the dot.
- Set the following folders and their contents to permissions 777:
-
/templates -
/uploads(or whatever your upload folder is named) -
/system/expressionengine/cache/db_cache
-
- Load up your control panel and change the file upload preferences.
Update a Site to an Existing Server – 1 to 5 minutes
- Clear all caches (only if you’ve made changes to the database).
- Export and import database using PHPMyAdmin or similar (only if you’ve made changes to the database).
- Run an SVN update on your site copy:
- If it’s a local server use your SVN client.
- If it’s a remote server use the SSH command svn update. You shouldn’t need to re-enter the URL or password.
- Load up your control panel and change the file upload preferences (only if you’ve made changes to the database).
Conclusion – Sojourning in the DRY Desert
As you go about your business designing and developing kick-ass ExpressionEngine websites, keep yourself mentally aware of what you’re doing at all times, from a big-picture, functionality perspective. Some pieces of website functionality are nearly identical across sites, they just need some minor markup tweaks and a CSS “skin” to easily transfer from one to another.
In the future, microformats will standardize the markup even more! These are ideal candidates for inclusion in your codebase. One we already discussed is the ubiquitous contact form. Some other potential “standard” functionality (I’ve had multiple clients ask for these things):
- Blogs and their associated comment forms
- Address or v cards
- News release sections
- XML or “Google” sitemaps
- Search and search results pages
- Custom Share This! type code
- Facebook or Twitter timelines
You could theoretically have channels, categories, custom field groups and templates built out and ready to go (I know I do for a lot of these). Your client is still getting the same amount of value that they would if you hand-built these pieces for their site (arguably more since they’ll be refined and tested more often) and you do less work, meaning you can price yourself more competitively, or if you sell fixed fee, charge the same price and turn more of a profit. Remember to have fun and enjoy developing with ExpressionEngine!
Translate report data export from RUEI into HTML for import into OpenOffice Calc Spreadsheets
[Corporate Blogs] (Blogs.oracle.com Recent Posts (English-language only))A common question of users is, How to import the data from the automated data export of Real User Experience Insight (RUEI) into tools for archiving, dashboarding or combination with other sets of data. XML is well-suited for such a translation via the companion Extensible Stylesheet Language Transformations (XSLT). Basically XSLT utilizes XSL, a template on what to read from your input XML data file and where to place it into the target document. The target document can be anything you like, i ...
XML is well-suited for such a translation via the companion Extensible Stylesheet Language Transformations (XSLT). Basically XSLT utilizes XSL, a template on what to read from your input XML data file and where to place it into the target document. The target document can be anything you like, i.e. XHTML, CSV, or even a OpenOffice Spreadsheet, etc. as long as it is a plain text format.
XML 2 OpenOffice.org Spreadsheet
For the XSLT to work as an OpenOffice.org Calc Import Filter:
How to add an XML Import Filter to OpenOffice Calc
- Start OpenOffice.org Calc and
select Tools > XML Filter Settings

- New...
- Fill in the details as follows:
Application: OpenOffice.org Calc (.ods)
Name of file type: Oracle Real User Experience Insight
File extension: xml

- Switch to the transformation tab and enter/select the following leaving the rest untouched
XSLT for import: ruei_report_data_import_filter.xsl
Please see at the end of this blog post for a download of the referenced file.

- Select RUEI Import filter from list and Test XSLT
Click on Browse to select
Transform file: export.php.xml

OpenOffice.org Calc will transform and load the XML file you retrieved from RUEI in a human-readable format.

- You can now select File > Open... and change the filetype to open your RUEI exports directly in OpenOffice.org Calc, just like any other a native Spreadsheet format.
Files of type: Oracle Real User Experience Insight (*.xml)
File name: export.php.xml

XML 2 XHTML
Most XML-powered browsers provides for inherent XSL Transformation capabilities, you only have to reference the XSLT Stylesheet in the head of your XML file. Then open the file in your favourite Web Browser, Firefox, Opera, Safari or Internet Explorer alike.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- inserted line below -->
<?xml-stylesheet type="text/xsl" href="ruei_report_data_export_2_xhtml.xsl"?>
<!-- inserted line above -->
<report>
You can find a patched example export from RUEI plus the above referenced XSL-Stylesheets here:
-
export.php.xml - Example report data export from RUEI
-
ruei_report_data_export_2_xhtml.xsl - RUEI to XHTML XSL Transformation Stylesheet
-
ruei_report_data_import_filter.xsl - OpenOffice.org XML import filter for RUEI report export data
If you would like to do things like this on the command line you can use either Xalan or xsltproc.
The basic command syntax for xsltproc is very simple:
xsltproc -o output.file stylesheet.xslt inputfile.xml
You can use this with the above two stylesheets to translate RUEI Data Exports into XHTML and/or OpenOffice.org Calc ODS-Format. Or you could write your own XSLT to transform into Comma separated Value lists.
Please let me know what you think or do with this information in the comments below.
Kind regards,
Stefan Thieme
References used:
- OpenOffice XML Filter - Create XSLT filters for import and export -
http://user.services.openoffice.org/en/forum/viewtopic.php?f=45&t=3490
- SUN OpenOffice.org XML File Format 1.0 -
http://xml.openoffice.org/xml_specification.pdf
VSO Image Resizer 4.0.2.5
[Africa] (Afrigator)VSO Image resizer is a free tool that organizes your photos by shrinking their resolution or moving them within your hard drive. It is the perfect tool for those who store their digital pictures and images on their PC and who want to resize, compress, convert, create copies, import or organize photos. VSO Image resizer is integrated into the Windows explorer shell, right click on your pictures and start working on your pictures!Using this free resize image software, you can create e-mail friendl ...
WCF RIA Services Part 9 - Structuring Your Application
[RIA (Rich Internet Apps)] (SilverlightShow: Silverlight Community)This article is Part 9 of the series WCF RIA Services: Getting Started with WCF RIA Services Querying Data Through WCF RIA Services Updating Data Through WCF RIA Services WCF RIA Services and MVVM Metadata Classes and Shared Code in WCF RIA Services Validating Data with WCF RIA Services Authenticating and Authorizing Calls in WCF RIA Services Debugging and Testing WCF RIA Services Applications Structuring WCF RIA Services Applications Exposing A ...
This article is Part 9 of the series WCF RIA Services:
- Getting Started with WCF RIA Services
- Querying Data Through WCF RIA Services
- Updating Data Through WCF RIA Services
- WCF RIA Services and MVVM
- Metadata Classes and Shared Code in WCF RIA Services
- Validating Data with WCF RIA Services
- Authenticating and Authorizing Calls in WCF RIA Services
- Debugging and Testing WCF RIA Services Applications
- Structuring WCF RIA Services Applications
- Exposing Additional Domain Service Endpoints for Other Clients
Introduction
In all the articles up to this point, I have been dumping all the domain service stuff into the single web project that also hosts the Silverlight application. Additionally, I've been putting a lot of functionality into the single Silverlight Application project on the client side. Granted it is only a couple of views so far. But if this app grew to a couple dozen views, I really would not want to be putting all those into a single project. In a real world app, too much code in one place, whether in a single method, a single class, or a single project is a maintenance liability. Additionally, on the server side you might want to start partitioning the code into logical layers, such as breaking out a separate data access layer project from the domain service code, and you might want separate projects for different sets of domain services.
In this article, I'm going to focus on how to break your solution up into multiple projects on both the server and client sides. I'll show how you can break things up into vertical slices that support different use cases in your application or different functional areas. You'll quickly learn how to use WCF RIA Services Class Library projects, as well as how to break things up into multiple XAP files on the client side. Using the Managed Extensibility Framework or Prism 4, you can break up your client application into multiple modules that get developed and built as separate XAP files, and then get downloaded asynchronously when needed by the client application. When you go down this path, unfortunately Visual Studio gets in your way a little bit for setting up your WCF RIA services link with the server project. I'll show you how to easily get past that limitation as well.
You can download the sample code for this article here.
Client-Server Project Relationships
One thing to understand up front is that there are some architectural constraints implied by the use of WCF RIA Services. Because of the way the RIA Services code generation process works, if you want to consume a WCF RIA DomainService from a client project, that client project has to have a link to the server project that contains the domain services you want to consume. That means that a single client project can only point to a single server project. You can have multiple client projects that point to the same server project, and the code generation will happen in each client project. But then if those projects are all used in the same scope, you will have duplicate types defined in the same scope, and will run into problems there. So to keep things clean, you will want to maintain a one-to-one correspondence between a single server project where a set of domain services is defined and a single client project where the client code for those services gets generated. However, the client project can just be a class library that can be reused across multiple modules or client applications. So your architecture will tend to look like this as you start to partition things:
Adding a WCF RIA Services Class Library Project
The way to get started breaking things up is to add a new WCF RIA Services Class Library project to your solution. In this case, say I wanted to add some separable functionality to my application, such as the ability to add notes. I would select Add > New Project from Solution Explorer with the solution node selected, and pick the WCF RIA Services Class Library project type.
Once you add this project, you really are adding two projects, the client Silverlight Class Library and a server normal Class Library project. The RIA Services link will already be set between the projects, and it adds them to the solution under a new solution folder.
You do not really have to use this project type to get this architecture though. You could just add a new Silverlight Class Library to the solution, then add a normal Class Library as well. Then you would just go to the project properties of the Silverlight Class Library and set the WCF RIA Services link drop down to point to the new server class library project. Then you would just have to add the WCF RIA Services references to the client and server projects. This just gets you all that done in one fell swoop. And since the client and server projects are linked, having them in a solution folder is a decent way to group them.
You would then add a new domain service to the TaskManager.Notes.Web project to get started defining your new service functionality and its associated entities. Its code generated client code will end up in the TaskManager.Notes client library. Then you would add a reference from the main client TaskManager Silverlight application to the TaskManager.Notes class library to start using those domain services in the main app. You could add new views and client functionality to that same class library as well. If you wanted to separate out your data access logic into its own class library, you would just add a new class library project and add your Entity Data Model into that project. Then add a reference to that class library from the class library that is going to contain your domain service. Remember to build before adding the new domain service so that it will see your entity framework model and let you pick from its entity types.
The resulting solution tree after adding some views and the data access library is shown below.
Using Entities Across Domain Services
In WCF RIA Services version 1, there is a limitation that you cannot have two domain services being used by the same client application that expose the same entity type. This is just a limitation of the way the client side code generation is done, because it will try to generate the same entity type once for each service and you will have duplicate definitions. This is fixed in WCF RIA Services SP1, which is available in beta form at the time of writing this here. But if you stick to the current release version, you will have to factor your vertical slices so that a given entity type is only used in one of the domain service vertical slices.
Breaking Your Client Application into Multiple Modules
As mentioned earlier, using MEF or Prism you can break your client architecture into modules – or separate chunks of functionality that can be downloaded asynchronously at runtime. For this article, I’ll just use MEF directly. I’ll be doing some articles on Prism 4 in the near future as well.
To do this, you need to put your functionality in a project that compiles to a separate XAP file from the main application. That requires you to pick the Silverlight Application template when creating the project, as opposed to a Silverlight Class Library or a WCF RIA Services Class Library project.
If I were going to do something similar to what I showed earlier in the article but wanted that new notes functionality to be downloaded separately as a module the first time it is used, the first step would be to create a new Silverlight Application project in my solution named TaskManager.NotesModule.
In the pop up that prompts for creating a server project, uncheck the box for hosting the application.
After the project is created, delete App.xaml and MainPage.xaml from the new project.
Next you will add the server class library where the domain service will live. Add a new Windows Class Library project and call it TaskManager.NotesModule.Services. You can delete Class1.cs from the new class library project as well. You could then add a domain service to that project, possibly using an entity data model defined in a separate class library as discussed earlier.
If you open the TaskManager.NotesModule Silverlight project settings at this point, the first thing you will need to do is set the startup object to (not set). That is because this project is not intended to be a standalone Silverlight application, but we are just using the Silverlight Application project type because its build output is to produce a XAP file with the contents of the project.
The trick comes when you go to set up the RIA Services link. If you drop down the WCF RIA Services link setting, you will not see the class library that contains your domain service listed as an option. This is really just a bug in the tooling. That drop down really just sets a relative path to the server project in the client project’s csproj file (or vbproj). Since the tool won’t let you set it correctly, you have to open the csproj file and edit it directly.
To do so, right click on the project and select Edit Project File.
If that option is not available for you, you can also just open the .csproj in any text editor. The setting you are looking for.is called LinkedServerProject. You need to fill it in with the relative path to the .csproj file of the server project. For example, in my case, the path is:
1: <LinkedServerProject>
2: ..\TaskManager.NotesModule.Services\TaskManager.NotesModule.Services.csproj
3: </LinkedServerProject>
After adding this and saving the file and closing it, you can then right click on the project in Solution Explorer and select Reload Project. If you then go look at the project settings again, you will see it is now showing the right server project. If you build, you will get the client generated code in your module project from the linked server project.
Using MEF To Dynamically Load the Module
Now all that is left is to load the module dynamically with MEF. I don’t have room for a full lesson on MEF, but what I am going to use here is MEF’s ability to download a separate XAP file asynchronously and then plug in the parts it finds in that XAP to the application dynamically. For a great overview of this capability, I recommend you check out this Silverlight TV Episode with Glenn Block.
The first step is to add the MEF System.ComponentModel.Composition reference to the TaskManager.NotesModule project. You can then mark the parts you want to plug in with appropriate Export attributes. In this case, I am going to plug in a single ChildWindow derived view called NotesView. So I add the following Export to the code behind of that view:
1: [Export(typeof(ChildWindow))]
2: public partial class NotesView : ChildWindow
3: {
4: }
Next, I need to add a little code the main app to download the separate XAP file asynchronously when appropriate. First step is to add references to the TaskManager project to System.ComponentModel.Composition.Initialization and System.ComponentModel.Composition. Then I add the following code to the code behind of the main page:
1: [Import]
2: public ChildWindow PlugInPopup { get; set; }
3: bool plugInsInitialized = false;
4:
5: private void button1_Click(object sender, RoutedEventArgs e)
6: {
7: if (!plugInsInitialized)
8: {
9: plugInsInitialized = true;
10: var deployment = new DeploymentCatalog("TaskManager.NotesModule.xap");
11: deployment.DownloadCompleted += (s, e2) =>
12: {
13: CompositionInitializer.SatisfyImports(this);
14: ShowPopup();
15: };
16: CompositionHost.Initialize(new DeploymentCatalog(), deployment);
17: deployment.DownloadAsync();
18: }
19: else
20: ShowPopup();
21: }
22:
23: private void ShowPopup()
24: {
25: if (PlugInPopup != null)
26: PlugInPopup.Show();
27: }
The PlugInPopup property will be populated by MEF after the extra XAP is downloaded. Constructing a DeploymentCatalog with a relative path to the XAP file in the host site of this application allows it to download that XAP file asynchronously when told to do so with the DownloadAsync call. You can see that the code subscribes to the completed event for that download, and then calls SatisfyImports to get the container to do dependency injection on this already existing view. At that point the PlugInPopup property gets populated, and is then shown.
The last part to making this work is to add the TaskManager.NotesModule project to the host Web site (it does not need a test page) so that it is in the ClientBin directory and can be downloaded.
The key point here is the need to manually edit the project file to point to the right server project where your domain services live. Just because Visual Studio doesn’t always let you point to any project in your solution (or outside of the solution for that matter), the only requirement is that the LinkedServerProject have a relative path to a compiled project that contains domain services. So a simple edit of the project file gets you want you want to organize your projects how ever you want, as long as you maintain that one-to-one relationship between client and server projects.
Summary
When it comes to organizing and structuring your solution, you can see that you have good flexibility to start breaking up chunks of functionality on the client and server sides into separate libraries and modules however it makes the most sense for your project. You should think in terms of breaking out vertical slices of functionality, composed of a server library project that contains a domain service or several that are related and all their supporting functionality and definitions on the server side. You will link that to a single client project, typically a Silverlight Class Library project or Silverlight Application project if you are trying to be more modular and download modules separately as separate XAPs. Even though you can have a single server project linked from multiple client projects, it will generally cause problems to do so within the same application because of duplicate definitions. So using client side class libraries that just contain the code generated RIA Services code and then referencing that class library from wherever that functionality is needed within the client application gives you flexibility to compose the client side however you want. Also remember that in WCF RIA Services SP1, the constraint on having one domain service “own” a single entity type is lifted.
You can download the sample code for this article here.
About the Author:
Brian Noyes is Chief Architect of IDesign, a Microsoft Regional Director, and Connected System MVP. He is a frequent top rated speaker at conferences worldwide including Microsoft TechEd, DevConnections, DevTeach, and others. He is the author of Developing Applications with Windows Workflow Foundation, Smart Client Deployment with ClickOnce, and Data Binding in Windows Forms 2.0. Brian got started programming as a hobby while flying F-14 Tomcats in the U.S. Navy, later turning his passion for code into his current career. You can contact Brian through his blog at http://briannoyes.net/ or on twitter @briannoyes.
links for 2010-11-19
[Corporate Blogs] (Blogs.oracle.com Recent Posts (English-language only))EAI in the Oracle MDM Foundation Layer David Butler identifies the Enterprise Application Integration (EAI) components in the MDM. "These are the Oracle Fusion Middleware (FMW) technologies used to support MDM Applications. These include application integration services, business process orchestration services, business rules engine, event-driven architecture, web services management, and user identity management." (tags: oracle otn eai entarch mdm) ...
-
David Butler identifies the Enterprise Application Integration (EAI) components in the MDM. "These are the Oracle Fusion Middleware (FMW) technologies used to support MDM Applications. These include application integration services, business process orchestration services, business rules engine, event-driven architecture, web services management, and user identity management."
-
"Our main reason to upgrade (Siebel to 8.1.1.1 and BI Publisher to 10.1.3.4.1) were issues with Active Directory. The Siebel Security Model provided by BI was not documented nor functioning properly in the earlier version. This has been resolved in the new version." -- Rick van Haasteren
-
"Contrary to popular belief, operating systems are not dead and indeed are fertile ground for innovations in virtualization, security, observability, file systems, performance and scalability, and yes, even Cloud Computing." -- Harry "Dr. Cloud" Foxwell.
-
"One of the latest proposals for creating a cloud computing standard has come from Oracle. The company has announced that it has contributed the Oracle Cloud Elemental Resource Model API to the Distributed Management Task Force (DMTF). That API is a actually a subset of an Oracle Cloud Resource Model application programming interface (API) that Oracle has made available to extend its overall management framework for the cloud." -- Mike Vizard
-
A short post from Wim with links to the relevant press release, a webcast, and template downloads.
-
"The results of our first benchmarking efforts of E-Business Suite on Oracle VM indicate that the virtualization overhead peaks are around 2-3%. In other words, most of the time the virtualization overhead is not measurable, and at peak overhead times, it is negligible." -- Ivo Dujmovic
-
Honglin Su shares a complete list of supported platforms for Oracle VM Server for SPARC 2.0.
-
"The future of cloud architectures will be much more hybrid in structure and scope, as every enterprise has legacy data that cannot be easily moved into private clouds. Add in the complexity of aggregating and normalizing unstructured content, and the direction of cloud architectures will be more hybrid, less private, over time." -- Louis Columbus
-
This DOAG update from Oracle ACE Director Markus "@myfear" Eisele includes some kind words about the OTN crew and a photo of OTN's Todd Trichler doing what appears to be a load of ironing.
-
Peter Paul van de Beek has taken the technology out for a spin on several different machines. He shares the results in this concise post.
-
Antony Reynolds continues his series with a post that illustrates how to "enable automatic migration of servers to ensure that no messages are lost or unnecessarily delayed."
-
Oracle ACE Director Andrejus Baranovskis shows you how.
-
"If you take a can of yellow paint and throw it at your CIO, followed by a liberal dosing of feathers. [it] doesn't result in innovation, but does result in CIO feature differentiation..." -- James McGovern
-
Daniel Cifuentes shares a no-frills list of links for Oracle Desktop Virtualization info and documentation.
-
"System Integrators are in a line of business where satisfying a client organization is very much the nature of what they do. A reconfiguration of the accounting logic is an escape from the classic struggle between vanilla functionality and business needs. I have seen various clients that would have had a better solution, had SLA been available in pre-R12 releases. But only in moderation and in combination with a large dose of common sense, and a lot of restraint." -- Henk Vermeulen
-
"As Service Providers reposition themselves to become the central enabler of social and business communications services, so the value delivered by IT solutions in general and data management technology in particular becomes increasingly critical." -- Mat Keep
-
Oracle ACE Director Markus "@myfear" Eisele's account of his very busy day at DOAG 2010.
SharePoint 2010 Cookbook: 4 Methods to Migrate a Single List to SharePoint 2010 from 2007
[SharePoint] (Bamboo Nation)Challenge: I recently needed to move a list from SharePoint 2007 to SharePoint 2010. I wanted the destination list to have the exact content as the source list including structure, list items, and attached files. Since SharePoint does not have an STSADM.EXE command line tool to accomplish this, it needs to be done manually. What are our options? Solution: After doing some research, I discovered that there are a few ways to accomplish this task. You can migrate a single list from SharePoint ...
Challenge:
I recently needed to move a list from SharePoint 2007 to SharePoint 2010. I wanted the destination list to have the exact content as the source list including structure, list items, and attached files. Since SharePoint does not have an STSADM.EXE command line tool to accomplish this, it needs to be done manually. What are our options?
Solution:
After doing some research, I discovered that there are a few ways to accomplish this task. You can migrate a single list from SharePoint 2007 to SharePoint 2010 using one of four methods:
- Migrate a single list from SharePoint 2007 to 2010 using a list template
- Migrate a single list from SharePoint 2007 to 2010 using an Access table
- Migrate a single list from SharePoint 2007 to 2010 using the detach database method
- Migrate a single list from SharePoint 2007 to 20110 using PowerShell
I'll summarize and show how each of these methods works in this article.
Method 1 - Migrate a single list from SharePoint 2007 to 2010 using a list template
The first method to consider is creating a list template from SharePoint 2007 and bringing it into SharePoint 2010. But you will likely be disappointed to learn that SharePoint 2010 will not recognize this template, so you cannot create any list from that template. A workaround for this problem is posted on Tom's Random Ranting blog, showing how you can easily modify the manifest file that gest created every time you create a template.
Tom's basic method is:
- Extract the contents of the STP file (it's really just a CAB file)
- Edit the manifest.xml file, changing the ProductVersion element from 3 to 4
- Repackage the STP file
It is quick way to bring a list over, but there is one important limitation: if your source list has many items (thousands), you might not be able to copy the entire contents of the list.
Method 2 - Migrate a single list from SharePoint 2007 to 2010 using an Access table
The second way to migrate a list from 2007 to 2010 is to use Access. Basically, you need to export a MOSS 2007 list to an Access table, and then import to SharePoint 2010:
Step 1: Open source list with Access
Step 2: Select Export a copy of the data to a new database
Step 3: The SharePoint list will now be imported into Access. You can add or modify columns in this mode to select what you need.
Step 4: From Access, select External Data tab in the Ribbon, and select SharePoint List in the Export section. Enter a SharePoint 2010 site address and select OK.
Your result should appear as follows:
You might be wondering if this method also works with a Document Library since the Document Library doesn't have the menu Action -> Open for Access! Yes, of course it does, but for a Document Library, you need to use the similar "Open with Windows Explorer" menu action.
Step 1: Open both Document Libraries with Window Explorer
Step 2: Select all the documents and folders that you need, and select Copy.
Step 3: Paste the data into the destination folder.
And the result is:
Limitation
You will notice that one of the limitations of this migration method is that the Modified Date, Created Date, Modified By, and Created By columns do not retain their values from the source list. You also need to have Microsoft's Access 2010 in order to use this method.
Method 3 - Migrate a single list from SharePoint 2007 to 2010 using the detach database method
In this method, we will try to migrate using an STSADM command that is supported by SharePoint 2007. All we have to do is use the Export and Import command to migrate a list.
Step 1: Migrate the entire SharePoint site that contains the list to be exported from 2007 to 2010. Please refer to an earlier post in the Cookbook series, How to Migrate a SharePoint 2007 Site to SharePoint 2010 Using Database Attach, for the exact steps on how to do this. You might have to create a new temporary site in SharePoint 2010 in order to migrate the site, but the site can then be removed after the desired list has been migrated.
Step 2: Once the site has been migrated to SharePoint 2010 server, let's try to move the sample Tasks list. From Central Admin -> Backup, select Restore -> Export a site or list:
In the selection drop down box,select the list that you want to move:
At this point, you have exported the desired list to a folder on your SharePoint server, and you are now ready to import that List to the correct destination.
Step 3. Next, import the Tasks list to your final destination site. SharePoint 2010 does not provide a User Interface in Central Admin for Granular Restore operations; therefore, you have to use PowerShell to accomplish this task:
And below is the result:
Method 4 - Migrate a single list from SharePoint 2007 to 2010 using PowerShell
Lastly, we will try to use PowerShell to Export/Import lists. We know that Microsoft has released a version of Windows PowerShell 1.0 that works with SharePoint 2007, and which can be downloaded here. After the installation has completed, we can write a PowerShell script to try to accomplish our goal of exporting a list.
The basic steps are:
- Run a shell script to export a list to a DAT file
- Change this DAT file to CAB, and extract this file so that you can access SystemData.xml in the CAB file, and modify version information there.
- Remake the CAB file and change the extension to .CMP.
- Use PowerShell in SharePoint 2010 to import the list from the .CMP file.
Step 1: Launch PowerShell and load SharePoint assemblies to the program. Refer to this blog post by Nick Grattan in order to prepare your PowerShell for SharePoint 2007: http://nickgrattan.wordpress.com/2007/09/03/preparing-powershell-for-sharepoint-and-moss-2007/:
Let's write a script to export a SharePoint 2007 List. I will follow an example from Kashish Sukhija's Blog here: http://blogs.sharepointdevelopers.info/2010/05/sharepoint-2010-deployment-using.html.
[System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c")
[System.Reflection.Assembly]::Load("Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c")
$spsite=[Microsoft.SharePoint.SPSite] ("http://cleanmoss/test")
$spweb=$spsite.OpenWeb()
$openList=$spweb.Lists["Tasks"]
$exportObject = New-Object Microsoft.SharePoint.Deployment.SPExportObject
$exportObject.Type = [Microsoft.SharePoint.Deployment.SPDeploymentObjectType]::List
$exportObject.IncludeDescendants = [Microsoft.SharePoint.Deployment.SPIncludeDescendants]::All
$settings = New-Object Microsoft.SharePoint.Deployment.SPExportSettings
$settings.ExportMethod = [Microsoft.SharePoint.Deployment.SPExportMethodType]::ExportAll
$versions = [Microsoft.SharePoint.Deployment.SPIncludeVersions]::All
$settings.IncludeVersions = $versions
$settings.IncludeSecurity = [Microsoft.SharePoint.Deployment.SPIncludeSecurity]::All
$settings.OverwriteExistingDataFile = 1
$settings.SiteUrl = $spweb.Url
$exportObject.Id = $openList.ID
$settings.FileLocation = "C:\Temp\BackupRestoreTemp\"ExportList-"+ $openList.ID.ToString() +".DAT"
$settings.BaseFileName = "
$settings.FileCompression = 1
$settings.ExportObjects.Add($exportObject)
$export = New-Object Microsoft.SharePoint.Deployment.SPExport($settings)
$export.Run()
After you have run this shell script you will have your export list in the c:\temp\BackupRestoreTemp\ folder.
Step 2: Next, all we have to do is change this DAT file to CAB, extract the file so that you can access SystemData.xml in the CAB file, and modify the version information there. Open the SystemData.xml file in a text editor, and change Version="12.0.0.0" to Version="14.0.0.0", and Build="12.0.0.6514" to Version="14.0.4762.1000".
Step 3: You will have to create a new CAB file again after making the changes above. Here is a reference showing how to make a CAB file using the Makecab.exe command: http://msdn.microsoft.com/en-us/library/dd583149(office.11).aspx. This is sample content to create the CAB:
.OPTION EXPLICIT ; Generate errors
.Set CabinetNameTemplate=ListTasks.cab
.set DiskDirectoryTemplate=CDROM ; All cabinets go in a single
.Set CompressionType=MSZIP;** All files are compressed in cabinet files
.Set UniqueFiles="OFF"
.Set Cabinet=on
.Set DiskDirectory1=ListTasks.CAB
; Include all files need to be presented in Cab.
manifest.xml
ExportSettings.xml
Requirements.xml
RootObjectMap.xml
SystemData.xml
UserGroup.xml
ViewFormsList.xml
00000000.dat
00000001.dat
00000002.dat
00000003.dat
00000004.dat
00000005.dat
00000006.dat
00000007.dat
00000008.dat
00000009.dat
0000000A.dat
0000000B.dat
0000000C.dat
Step 4: Finally, we can use PowerShell on SharePoint 2010 server and run the Import-spweb to import the list.
Result:
In summary, if you have a need to just import a SharePoint list from an existing SharePoint 2007 farm to a SharePoint 2010 farm, there are several ways to accomplish this task. Which method you should use will depend on the tools that you have available, and your familiarity with each of them. Note: There are also third-party tools available to help you with migrating sites and content in SharePoint and, for list migration specifically, you may wish to look at Bamboo's own List Bulk Import product.
Notes:
- Using the last two methods described, we will be able to retain the original values of Modified Date, Created Date, Created By, and Modified By from the source list. Make sure to specify the parameter -IncludeUserSecurity in your import command.
- Most of the steps we have shown in this article will require you to have Farm administration rights in order to run the necessary import and export commands.
- If your source list contains any custom columns, you might have to install that feature on the destination SharePoint 2010 farm before doing the migration.
See Also:
- SharePoint 2010 Cookbook: How to Migrate a SharePoint 2007 Site to SharePoint 2010 Using Database Attach [Bamboo Nation]
- Using Powershell Script to export & Import SPList [Kashish Sukhija's Blog]
- Importing SharePoint 2007 list templates (STP) into SharePoint 2010 [Tom's Random Ranting]
- Preparing PowerShell for SharePoint and MOSS 2007 [Nick Grattan's SharePoint Blog]
- Packaging and Deploying Web Parts for Microsoft Windows SharePoint Services [MSDN]
Blog Post: Always Enable Disk-Based Caching in SharePoint Server 2010
[RIA (Rich Internet Apps)] (Site Home)In March, 2009, I wrote a post that explains why I always recommend enabling disk-based caching in Microsoft Office SharePoint Server (MOSS) 2007. This morning a Microsoft PFE (Premier Field Engineer) reached out to me after he came across my blog post while investigating some issues at a customer site. He said that he was at some "government agency" but that's all he would say -- and probably all I want to know ;-) Anyway, he mentioned that his 10-minute SQL Server Profiler trace showed somet ...
In March, 2009, I wrote a post that explains why I always recommend enabling disk-based caching in Microsoft Office SharePoint Server (MOSS) 2007.
This morning a Microsoft PFE (Premier Field Engineer) reached out to me after he came across my blog post while investigating some issues at a customer site. He said that he was at some "government agency" but that's all he would say -- and probably all I want to know ;-)
Anyway, he mentioned that his 10-minute SQL Server Profiler trace showed something like 56,000 calls to the proc_FetchDocForHttpGet stored procedure (which I suspect is how he discovered my earlier post, since it appears as the 3rd search result on bing and the 7th search result on Google).
We talked for a little bit about the effects of enabling disk-based caching, since the customer's SharePoint administrator was a little reluctant to enable it without some "data" to support it. [Note that there are some cons, which is something I really should have covered in a blog post by now. I'll try to get to that soon, I promise.]
I told the PFE to do some quick analysis on his Profiler trace to see how many of those proc_FetchDocForHttpGet calls were for resource files (e.g. cascading style sheets, images, etc.) -- in other words, items ideally suited for caching (as compared to "real" documents, such as PDF or Word docs).
I also told them that I suspected they will see a dramatic drop in the load on their SQL Server after enabling disk-based caching. My guess is that their SQL environment is pretty beefy and therefore able to support the load (circa 1,000 concurrent users on the intranet site) except at peak times -- or perhaps, their load has grown significantly in recent weeks -- which prompted pulling in the PFE to troubleshoot.
This incident got me thinking about whether the story has changed for SharePoint Server 2010. For those of you that have already read more of this post than you would have liked, the short answer is "yes, it's definitely better in the newer version...but no, my recommendation hasn't changed for SharePoint Server 2010." Want to know more? Well, then read on, my friends...
Here is why I always recommend enabling disk-based caching in SharePoint Server 2010:
I fired up one of the "vanilla" SharePoint Server 2010 VMs that I built a while ago and started up a SQL Server Profiler trace using the settings detailed in the previously mentioned post.
I then browsed to the home page of one of my sites (based on the out-of-the-box Publishing Portal template). As described in my previous post, I then closed the browser, cleared the Profiler trace, and then started Internet Explorer and browsed to the site again.
The good news is that Profiler shows a nominal five database roundtrips required for SharePoint Server 2010 to render the home page for the site. Keep in mind that I was using a "vanilla" site with no customizations, so your actual number may vary, depending on your environment.
While five database calls certainly seems reasonable for rendering a page request, the bad news is that the last three database calls resemble the following:
exec proc_FetchDocForHttpGet @DocSiteId='...',@DocDirName=N'...', @DocLeafName=N'controls.css', ... exec proc_FetchDocForHttpGet @DocSiteId='...',@DocDirName=N'...', @DocLeafName=N'page-layouts-21.css', ... exec proc_FetchDocForHttpGet @DocSiteId='...',@DocDirName=N'...', @DocLeafName=N'home.jpg', ...
Seriously...what is the likelihood the CSS files or image have changed since the last request for the home page? Zero. [Okay, maybe not absolute 0 -- but probably something like 6.28 x 10-9 ;-) ]
After changing the <BlobCache> enabled attribute from "false" to "true" in my Web.config file, I confirmed that subsequent requests for the home page only require two database roundtrips. Woohoo!
So, why then should we burden SQL Server with these extraneous requests. The answer, of course, is that we shouldn't (at least not in Production and Test environments). As I noted in my previous post, you certainly don't have to enable BLOB caching in your development environments (I certainly don't).
However, this leads to an important question: What if my CSS files or images do change and I need to ensure that users get the updated versions? Well, as I hinted earlier in this post, that's a topic for a later time.
Don't worry, you won't have to wait very long.
Lastly, I should note that MOSS 2007 requires 15 database roundtrips to render the home page for a site based on the Publishing Portal template (yes, this is even after the site is "warmed" up). If you look at the calls to proc_FetchDocForHttpGet, you'll find a number of additional CSS files and images.
Launching a New Website: 18 Steps to Successful Metrics & Marketing
[Power150, SEO (Search Engine Optimization)] (SEOmoz Daily SEO Blog)Posted by randfishThe process of launching a new website is, for many entrepreneurs, bloggers and business owners, an uncertain and scary prospect. This is often due to both unanswered questions and incomplete knowledge of which questions to ask. In this post, I'll give my best recommendations for launching a new site from a marketing and metrics setup perspective. This won't just help with SEO, but on traffic generation, accessibility, and your ability to measure and improve everything about yo ...
Posted by randfish
The process of launching a new website is, for many entrepreneurs, bloggers and business owners, an uncertain and scary prospect. This is often due to both unanswered questions and incomplete knowledge of which questions to ask. In this post, I'll give my best recommendations for launching a new site from a marketing and metrics setup perspective. This won't just help with SEO, but on traffic generation, accessibility, and your ability to measure and improve everything about your site.
#1 - Install Visitor Analytics
Nothing can be improved that is not tracked. Keeping these immortal words of wisdom in mind, get your pages firing analytics code before your first visitor. Google Analytics is the obvious choice, and customization options abound (for most sites more advanced than a basic blog, I'd highly recommend at least using first-touch attribution).
Google analytics, or any other package (see some alternatives here), needs to be placed on every page of your site and verified. Do yourself a favor and install in a template file you can be sure is on every page (e.g. footer.php). GA's instructions will indicate that placing the code at the top of the page is key, but I'm generally in favor of leaving it at the bottom to help page load time for visitors (though the new asynchronous GA code is pretty fast).
#2 - Set Up Google & Bing Webmaster Tools Accounts
Both Google & Bing have webmaster tools programs that monitor data about your site and message it back to you through online interfaces. This is the heartbeat of your site from the search engines' perspective and for that reason, it's wise to stay on top of the data they share.
That said, the numbers inside these tools are not always perfect, and often have serious flaws. The referring keywords and traffic data are, in my experience, far off what analytics tools will report (and in those cases, trust your analytics, not the engines' tools). Likewise, crawl, spidering and indexation data isn't always solid, either. Nonetheless, new features and greater accuracy continue to roll out (more of the former than the latter unfortunately) and it's worth having these both set up.
#3 - Run a Crawl Simulation of Your Site
No matter how perfect you or your developers are, there's always problems at launch - broken links, improper redirects, missing titles, pages lacking rel=canonical tags (see more on why we recommend using it and the dangers of implementing improperly), files blocked by robots.txt, etc.
By running a crawl test with a free tool like Xenu or GSiteCrawler, or leveraging a paid tool like Custom Crawl from Labs or the Crawl Service in the Web App (pictured above), you can check your site's accessibility and insure that visitors and search engines can reach pages successfully in the ways you want. If you launch first, you'll often find that critical errors are left to rot because the priority list fills up so quickly with other demands on development time. Crawl tests are also a great way to verify contractor or outsourced development work.
#4 - Test Your Design with Browser Emulators
In addition to testing for search engine and visitor accessiblity, you'll want to make sure the gorgeous graphics and layout you've carefully prepared checks out in a variety of browsers. My rule is to test anything that has higher than 2% market share, which currently means (according to Royal Pingdom): Internet Explorer, Firefox, Chrome, Safari and Opera.
There's a great list of browser testing options from FreelanceFolder here, so I'll just add that in-person testing, on your own PCs & Macs, is also a highly recommended use of an hour.
#5 - Set Up RSS Feed Analytics
Virtually every site will have some form of structured data being pushed out through an RSS feed. And, just like visitor analytics, if you want to improve the reach and quality of the feed, you'll need to leverage data.
Feedburner is the de facto software of choice, and it's very solid (though, good alternatives do exist). Getting your feed and the analytics to track and measure it is typically a very easy process because there's nothing to verify - you can create and promote any feed you want with just a few button clicks.
One important recommendation - don't initially use the counter "chicklet" like:
It has a bad psychological impact to see that no one has subscribed to your new RSS feed. Instead, just provide a standard link or graphic and after you've amassed a few hundred or thousand readers, use the numeric readout to provide additional social proof.
#6 - Tag the Actions that Matter
No matter what your site is, there are actions you're hoping visitors will take - from tweeting a link to your post to leaving a comment to buying a product or subscribing to an email list. Whatever those actions might be, you need to record the visits that make them through your analytics tool. Casey Henry's post on Google Analytics' Event Tracking will provide a thorough walkthrough.
Once action tracking is in place, you can segment traffic sources and visit paths by the actions that were taken and learn more about what predicts a visitor is going to be valuable. If you're pouring hours each day into Twitter m but seeing no actions, you might try a different channel, even if the traffic volume is high.
#7 - Conduct an Online Usability/Branding Test
Before a formal launch, it can be extremely helpful to get a sense of what users see, experience and remember when they browse to your site for a few seconds or try to take an action. There's some fantastic new software to help with this, including Clue App, screenshot below:
Last week, I set up a Clue App test for SEOmoz's homepage in 30 seconds and tweeted a single link to it, which garnered 158 kind responses with words and concepts people remembered from the visit. This type of raw testing isn't perfect, but it can give you a great look into the minds of your visitors. If the messages being taken away aren't the ones you intended, tweaking may be critical.
In addition to Clue, dozens of other easy usability and user-testing apps are now on the market. Conversion Rate Experts has a good list here and Craig Tomlin's got another excellent one here.
#8 - Establish a KPI Dashboard
No matter what your website does, you live and die by some key metrics. If you're starting out as a blogger, your RSS subscribers, unique visits, pageviews and key social stats (tweets, links, Facebook shares, etc) are your lifeblood. If you're in e-commerce, it's all of the above plus # of customers, sales, sales volume, returning vs. new buyers, etc.
Whatever your particular key metrics might be, you need a single place - often just a basic spreadsheet - where these important numbers are tracked on a daily or weekly basis. Setting this up before you launch will save you a ton of pain later on and give you consistent statistics to work back from and identify trends with in the future.
#9 - Build an Email List of Friends & Business Contacts for Launch
This may seem non-obvious, but it's shocking how a friendly email blast to just a few dozen of your close contacts can help set the stage for a much more successful launch. Start by building a list of the people who owe you favors, have helped out and who you can always rely on. If you're feeling a bit more aggressive in your marketing, you can go one circle beyond that to casual business partners and acquaintences.
Once you have the list, you'll need to craft an email. I highly recommend being transparent, requesting feedback and offering to return the favor. You should also use BCC and make yourself the recipient. No one wants to be on a huge, visible email list to folks they may not know (and get the resulting reply-all messages).
#10 - Create Your Google Alerts
TheAlerts Service from Google certainly isn't perfect, but it's free, ubiquitous, and can give you the heads up on some of the sites and pages that mention your brand or link to you in a timely fashion.
Unfortunately, the service sends through a lot of false positives - spam, scraper sites and low quality junk. It also tends to miss a lot of good, relevant mentions and links, which is why the next recommendation's on the list.
#11 - Bookmark Brand Tracking Queries
In order to keep track of your progress and identify the sites and pages that mention or link to your new site, you'll want to set up a series of queries that can run on a regular basis (or automated if you've got a good system for grabbing the data and putting it into a tracking application). These include a number of searches at Google, Twitter and Backtype:
The queries should use your brand name in combination with specific searches, like the example below (using "seomoz" and "seomoz.org"):
- Google Blog Search mentions
- Google Blog Search links
- Google Web mentions (past 24 hours)
- Google News mentions
- Twitter Search
- Backtype Search
You can add more to this list if you find them valuable/worthwhile, but these basics should take you most of the way on knowing where your site has been mentioned or referenced on the web.
#12 - Make Email Signup/Subscription Available
Capturing the email addresses of your potential customers/audience can be a huge win for the influence you're able to wield later to promote new content, products or offerings. Before you launch, you'll want to carefully consider how and where you can offer something in exchange for permission to build an email list.
One of the most common ways to build good lists is to offer whitepaper, e-book, video or other exclusive content piece for download/access to those who enter an email address. You can also collect emails from comment registration (which tend to be lower overall quality), through an email newsletter subscription offering (which tend to be very high quality) or via a straight RSS subscription (but you'll need to self-manage if you want to have full access to those emails). Services like MailChimp, ExactTarget, Constant Contact and iContact are all options for this type of list building and management.
#13 - Create Your Site/Brand's Social Accounts
Social media has become popular and powerful enough that any new site should be taking advantage of it. At a minimum, I'd recommend creating accounts on the following networks:
- Google Profiles
- YouTube (if you have or will have any video content)
- Flickr (if you have any graphics or images content)
And if you have more time or energy to devote, I'd also invest in these:
- Quora
- Slideshare (if you have any presentations)
- Scribd (if you have any document content)
- StumbleUpon
- Any industry specific social portals (e.g. in software, this might include places like StackOverflow, Github and Hacker News)
Setting up these accounts diligently is important - don't just re-use the same short bio or snippet over and over. Spend the time to build fleshed out profiles that have comprehensive information and interact/network with peers and those with similar interests to help build up reputation on the site. The effort is worth the reward - empty, unloved social accounts do virtually nothing, but active ones can drive traffic, citations, awareness and value.
BTW - Depending on the size and structure of your site, you may also want to consider creating a Facebook Fan Page, a LinkedIn Company Page and profiles on company tracking sites like Crunchbase, BusinessWeek and the Google Local Business Center.
#14 - Connect Your Social Accounts
If you've just set up your social account, you've likely added your new site as a reference point already, but if not, you should take the time to visit your various social profiles and make sure they link back to the site you're launching.
Not all of these links will provide direct SEO value (as many of them are "nofollowed"), but the references and clicks you earn from those investigating your profiles based on your participation may prove invaluable. It's also a great way to leverage your existing branding and participation to help the traffic of your new site.
#15 - Form a List of Target Press, Blogger and Industry People for Outreach
Depending on your niche, you may have traditional media outlets, bloggers, industry luminaries, academics, Twitter personalities, powerful offline sources or others that could provide your new site with visibility and value. Don't just hope that these folks find you - create a targeted list of the sites, accounts and individuals you want to connect with and form a strategy to reach the low hanging fruit first.
The list should include as much contact information as you can gather about each target - including Twitter account name, email (if you can find it), and even a physical mailing address. You can leverage all of these to reach out to these folks at launch (or have your PR company do it if you have one). If you tell the right story and have a compelling site, chances are good you'll get at list a few of your targets to help promote, or, at the least visit and be aware of you.
#16 - Build a List of Keywords to Target in Search Engines
This is SEO basics 101, but every new site should keep in mind that search engines get lots of queries for virtually everything under the sun. If there are keywords and phrases you know you want to rank for, these should be in a list that you can measure and work toward. Chances are that at launch, you won't even be targeting many of these searches with specific pages, but if you build the list now, you'll have the goal to create these pages and work on ranking for those terms.
As you're doing this, don't just choose the highest traffic keywords possible - go for those that are balanced; moderate to high in volume, highly relevant in terms of what the searcher wants vs. what your page/site offers and relatively low in difficulty.
See this post for more tips - Choosing the Right Keyphrases - from Sam Crocker.
#17 - Set Targets for the Next 12 Months
WIthout goals and targets, there's no way to know whether you're meeting, beating or failing against expectations - and every endeavor, from running a marathon to cooking a meal to building a company or just launching a personal blog will fail if there aren't clear expectations set at the start. If you're relatively small and just starting out, I'd set goals for the following metrics:
- Average weekly visits (via analytics)
- Average page views (via analytics)
- Number of new posts/pages/content pieces produced per month
- Number of target contacts (from item #15) that you've reached
- Social media metrics (depending on your heaviest use platform, e.g. # of Twitter followers if you're a heavy Tweeter)
- Any of the key items from #8 on this list (your KPI dashboard)
And each of these should have 3, 6 and 12 month targets. Don't be too agressive as you'll find yourself discouraged or, worse, not taking your own targets seriously. Likewise, don't cut yourself short by setting goals that you can easily achieve - stretch at least a little.
Every 3-6 months, you should re-evaluate these and create new goals, possibly adding new metrics if you've taken new paths (RSS subscribers, views of your videos, emails collected, etc.)
#18 - Plug in the SEOmoz Web App
I know this one's a bit self-serving, but I'd like to think I'd add it here even if it wasn't my company (I recently set up my own personal blog and found the crawling, rank tracking and new GA inegration features pretty awesome for monitoring the growth of a new site).
The SEOmoz Web App has a number of cool tracking and monitoring features, as well as recommendations for optimizing pages targeting keywords, that make it valuable for new sites that are launching. The crawl system can serve to help with #3 on this list at the outset, but ongoing, it continues to crawl pages and show you your site's growth and any errors or missed opportunities. Tracking rankings can let you follow progress against item #16, even if that progress is moving from ranking in the 40s to the 20s (where very little search traffic will be coming in, even if you're making progress). And the new GA integration features show the quantity of pages, keywords and visits from search engines to track progress from an SEO standpoint.
Using this list, you should be able to set up a new site for launch and feel confident that your marketing and metrics priorities are in place. Please feel free to share other suggestions for pre and post-launch tactics to help get a new site on its feet. I'm looking forward to seeing what other recommendations you've got.
SP2010 AJAX part 3– using jQuery AJAX with a HTTP handler
[SharePoint] (Chris O'Brien)Boiling jQuery down to the essentials (technique) Using the JavaScript Client OM + jQuery to work with lists (technique) Using jQuery AJAX with a HTTP handler (technique) – this article Returning JSON from a HTTP handler (technique) Enable Intellisense for Client OM and jQuery (tip) Debugging jQuery/JavaScript (tip) Useful tools when building AJAX applications (tip) Transitioning existing applications to jQuery/AJAX So far we’ve looked at jQuery for page manipu ...
- Boiling jQuery down to the essentials (technique)
- Using the JavaScript Client OM + jQuery to work with lists (technique)
- Using jQuery AJAX with a HTTP handler (technique) – this article
- Returning JSON from a HTTP handler (technique)
- Enable Intellisense for Client OM and jQuery (tip)
- Debugging jQuery/JavaScript (tip)
- Useful tools when building AJAX applications (tip)
- Transitioning existing applications to jQuery/AJAX
So far we’ve looked at jQuery for page manipulation and using the Client OM for talking to SharePoint. Today I want to talk about something else, and I think this is possibly the most important post in this series. jQuery and the Client OM are unlikely to deal with every scenario the AJAX-minded SharePoint developer will need to deal with. To be sure, the Client OM is surprisingly extensive and deals with way more than just fetching items from lists – that said, it’s not a full mirror of the server API and in any case, when writing server code which talks to client code (e.g. JavaScript) sometimes you need full control over both sides of the fence. So, how do you build an AJAX-style SharePoint application without using the Client OM (for whatever reason)?
Let’s consider the server side first - there are a couple of options:
- Write a WCF service (or indeed, ‘classic’ .asmx web service)
- Write a HTTP handler
- Write some other form of server code which (like the others) generates a response over HTTP
- e.g. technically a good old .aspx page could work as the server-side component. However, this is a bad choice compared to the other options for a few reasons (mentioned shortly)
Arguably the purist route would be to develop a WCF service – indeed SharePoint 2010’s implementation of the Client OM is a WCF service. If you’re experienced with WCF, this is probably a good choice for you. However, my view is that a HTTP handler is a hundred times simpler, and that the features of WCF (e.g. transport flexibility, ability to pass large files, security etc.) are typically not necessary in an “internal to the farm” SP2010/AJAX app. Of course, if you’re building a public API to expose stock quotes to the world then things might be different. Alternatively, an esoteric approach could be to use something like a regular .aspx page – however an ASP.Net web forms page (i.e. not MVC) will be less efficient here as the page will go through the full ASP.Net lifecycle (Init, OnPreRender, Render etc.) despite the fact you probably don’t have any ASP.Net controls on the page. In other words we are using ASP.Net pages for something other than what they were designed.
So, a HTTP handler is a simple and effective means of building an AJAX-style app. To some folks it’s a new technique, and for others it’s old news – but it’s my view that this approach is THE key to unlocking the ability to write AJAX apps, whether that’s on top of SharePoint or plain .Net.
The great thing about this technique is that the possibilities are unlimited – you can AJAX-ify anything, since you would write the C# code in the handler, then just call it from jQuery. Clearly this cannot be said about a ‘provided’ API such as the Client OM. By the way, I’d be interested to hear opposing opinions on the WCF vs. HTTP handler point.
In terms of the client, if we are talking about JavaScript (as we are in this series), then we need a way of calling ‘HTTP resources’ like those mentioned above from a JavaScript method. This has been possible for years, but the advent of jQuery means it’s way simpler than before – jQuery provides a handful of AJAX methods to call a server resource by URL, and the response is passed back to the JavaScript. Once you have the value from the server call (more on this later), you can display feedback to your user without reloading the page by simply using the methods described in part 1 (essential jQuery methods for updating pages). The jQuery AJAX methods are:
| $.get() | Requests a URL using a HTTP GET – calls .ajax() underneath |
| $.post() | Requests a URL using a HTTP POST – calls .ajax() underneath |
| $.ajax() | Allows full control – exposes full set of parameters |
| $.getJson() | Requests a URL GET and parses response for JSON format using $.parseJSON |
Creating a HTTP handler (server)
So, we’ve hopefully established that a HTTP handler combined with jQuery’s AJAX methods is a powerful technique – now let’s look at the detail.
HTTP handler
A HTTP handler is a .Net class which implements System.Web.IHttpHandler – the ProcessRequest() method is the main method. Here, you write code which determines what is returned when the URL for the handler is requested. So where a URL to an .aspx page would return HTML, you can return whatever you want – a simple string, some more complex XML, or perhaps JSON (the next article in this series). A simple handler returning a string looks like this:
using System;
using System.Web;
namespace COB.SPSaturday.Demos.Handlers
{
public class DemoHandler : IHttpHandler
{
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("From the handler at " + DateTime.Now);
}
}
}
The .Net framework needs to know about such a handler before it can be used. Two options here – either associate an .ashx file with your .cs file above (the handler is called by the path to the .ashx in this scenario), or add a web.config entry telling .Net how to route the request to your class (the handler is called by whatever path you define in web.config in this scenario).
Using an .ashx
Create a file in your project with an .ashx extension (there is no VS item template, unless I keep missing it), and use the WebHandler directive to point to your implementation. This works because .ashx is a special extension where .Net knows to resolve the class by looking for the WebHandler directive:
<%@ Assembly Name="COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" %>
<%@ WebHandler Language="C#" Class="COB.SPSaturday.Demos.Handlers.DemoHandler" CodeBehind="DemoHandler.cs" %>
Using a web.config entry
Add an entry (e.g. via SPWebConfigModification) like this – notice you can specify a custom path/extension if that’s preferable for any reason:
<handlers>
<!-- other handlers here -->
<add name="DemoHandler" path="/_layouts/COB/DemoHandler.cob" verb="*" type="COB.SPSaturday.Demos.Handlers.DemoHandler, COB.SPSaturday.Demos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" />
</handlers>
Most folks opt for the .ashx route since it avoids defining something in web.config for every handler your application uses.
Calling a HTTP handler (client)
Earlier, we detailed jQuery’s four core methods for making a request to the server. Here’s an example (using the .get() method):
<fieldset id="fldDemo1">
<legend>Demo 1 - simple handler</legend>
<div id="demo1Row" class="demoRow">
<div class="demoControls">
<button id="btnDemo1" type="button">Call handler</button>
</div>
<div class="demoResults">
<span id="demo1Result" />
</div>
<div class="clearer" />
</div>
</fieldset>
<script type="text/javascript">
$('#btnDemo1').click(function () {
$.get('/_layouts/COB/DemoHandler.cob',
function (data) {
$('#demo1Result').html(data);
});
});
</script>
As you can see, the key is taking the data property which is passed from the server to the jQuery callback, and using jQuery’s ability to manipulate the page with methods like .html(). Hey presto, you can now do anything you like without a full postback!
Something to note is that if you’re modifying data in the handler, a good practice is to make it a .post() request rather than .get() – of course, SharePoint disallows updates on a GET request by default so you must do something to work around this anyway (the ghetto alternative being to set SPWeb.AllowUnsafeUpdates = true).
Putting it into practice
Now that we’ve learnt the mechanisms, consider how usage might pan out in the real-world:
- Return data:
- If you’re returning unstructured data (e.g. a string), things are pretty simple (N.B. another jQuery AJAX method I omitted to mention is $.load() method which simplifies things further)
- For structured data (e.g. objects), this is often returned as a JSON-serialized string – commonly preferred to XML, but there are plenty of samples out there for XML too
- Because setting the HTML of a page element is so easy, it’s tempting to have a handler return a huge string of hardcoded HTML (e.g. when we’re updating a big complex area of the page) – indeed I mentioned this as a “SP2010/jQuery/AJAX survivor’s technique” in my SharePoint Saturday talk. This works fine but a far better approach is to use the new jQuery templates capability – Jan Tielen’s Getting Started with jQuery Templates and SharePoint 2010 is a great post.
- Factoring
- A good way to structure your code is to have a handler implement multiple methods (e.g. for related functionality), and pass a querystring parameter in the AJAX request to indicate which method to execute. You certainly don’t need a handler for every method you might implement.
Next time - Returning JSON from a HTTP handler
Blog Post: Getting Started with Visual Studio 2010 Ultimate - Load and Performance Testing
[Ecommerce] (Site Home)If you are new to Visual Studio 2010 Ultimate Load Testing - this article is for you. This gives you a quick overview and shows you how you can get started. Getting Visual Studio 2010 There are a couple of ways you can get access to Visual Studio 2010 to try it out. Option 1: Download a trial Download a trial version for Visual Studio 2010 Ultimate Trial from http://www.microsoft.com/visualstudio/en-us/download . The other versions will not work for you, so select the Web Installer or th ...
If you are new to Visual Studio 2010 Ultimate Load Testing - this article is for you. This gives you a quick overview and shows you how you can get started.
Getting Visual Studio 2010
There are a couple of ways you can get access to Visual Studio 2010 to try it out.
Option 1: Download a trial
Download a trial version for Visual Studio 2010 Ultimate Trial from http://www.microsoft.com/visualstudio/en-us/download . The other versions will not work for you, so select the Web Installer or the ISO download for Visual Studio 2010 Ultimate Trial. This download gives you the ability to run 250 users of virtual user load for the trial duration.
If you need to scale up load, you simply buy the Visual Studio Virtual User Pack 2010 through your traditional channel or buy it from the store at http://store.microsoft.com/microsoft/Visual-Studio-2010-Load-Test-Virtual-User-Pack/product/7AFF7BC9 (visit click for approximate pricing).
Once you’ve downloaded your trial, simply install Visual Studio 2010 Ultimate by following the "Typical" install option on a suitable machine and you are ready to try it ou
Option2: Pre-Configured VHD/VPC
You can download a preconfigured VHD that will allow you to focus on trying the product features instead of installing and configuring. If you want to install and configure experience as well you should consider downloading the trial.
To download this VHD/VPC go to the link below and follow the download instructions on the page.
This download is almost 9GB, so you should plan to kick off the download at night/off time as it’s a sizeable download and use the best practice of using the a download manager software to minimize download interruptions. Once you download it, run the appropriate .exe file (for your platform as in instructions on the above link). I downloaded the VisualStudio2010RTM_Win7VPC and had previously configured my Windows7 machine to have the VPC feature enabled.
For the size of the download, you will not only be able to give load testing a try (needs a couple more extra steps covered below), but also all of the manual, functional automation and other testing and development capabilities available in the Visual Studio 2010 release.
For the rest of the article I will focus on how to go about setting up a working Windows 7 VPC that I have downloaded and configured.
First Time VPC Configuration
Once VPC is configured and running, you should login to the VPC as “Christine Koch (Tester)” and use the password “P2ssw0rd” (without the quotes).
Once you have logged in (only for the first time), you must configure your browser defaults by just launching the browser and following the first time configuration wizard and accepting defaults.
Figure1: VisualStudio2010RTM_Win7VPC running on my machine
Figure 2: First time configuration for Visual Studio 2010 Ultimate
Additional Setup for VPC
Before you can use the downloaded and configured VPC specifically for load testing, you need to
1. Configure the SQL server that is already installed on the machine.
2. Configure Visual Studio 2010 to use the above database.
SQL is pre-installed on the image. You must Launch SQM Management Studio from the start menu, connect to the Database engine and open and run the SQL script located at C:\Program Files \Microsoft Visual Studio 10.0\Common\IDE7\loadtestresultrepository.sql
Figure 3: Connect to SQM Management Studio
To configure Visual Studio to use the above database, from Visual Studio go to the “Test” menu and select “Manage Test Controllers…” and click on button to configure load test results store.
Figure 4: Configure Visual Studio to use SQL database (configure load test results not shown)
Ensure you use windows authentication and select “LoadTest2010” database from the list.
Figure 5: Configure Load Test Results Store
The image contains a pre-installed and configured sample application for you to use as your application under test. It is called TailSpinToys. It's an ecommerce application that allows end users to browse and buy toys. The application is running at http://Win-GS9GMUJITS8:8000/ or http://localhost:8000.
TIP: Launch the application and spend a few minutes to browse through the application and familiarize yourselves with the target application. You may want to book mark the application home page.
Web Performance Test Basics
Web performance tests enable you to generate http requests and responses, test for correctness of the responses, and measure response times and throughput. The primary scenario for web tests is to use them in a load test to generate load against a web application and measure web application performance. In order to efficiently generate load, Web performance tests work at the http layer, they do not drive the browser. For functional UI automation, you must use the Coded UI test type.
To create your first web test, from Visual Studio choose File \ New \ Project. In the dialog that follows, on the left hand side you see the installed template. Select the appropriate language of your choice on the left (Visual Basic or C#). Expand the node and choose “Test” to create a test project.
At the bottom of the dialog, choose a name for your test project. By default VS will create a new solution for you. In this walk through I have used C# for the language template and called my solution TailSpinToysLoadTestDemo
Figure 6: Create a test project with Visual Studio 2010
Note that you have an option to check in your tests into source control if you have setup Team Foundation Server. Load testing works even if you do have team foundation server installed and configured (although in the image there is a TFS instance running). Also, it is not necessary for you to check in web/load tests.
Visual studio creates a project and opens a Unit Test by default, ignore this.
TIP: If you haven’t used Visual Studio before you need to know that most of time for testing you will spend in the Solution Explorer pane. If this is not open by default, you must go to “View” menu and choose “Solution Explorer”
You must now “Add” a “Web Performance Test” by right clicking on the TailSpinToysLoadTestDemo
Figure 7: Add a Web Performance Test from Visual Studio
This opens the WebTestRecorder. Visual Studio 2010 using a browser helper object and WinINET based recorder to record your web tests. To record your web test, simply navigate to the target application URL http://Win-GS9GMUJITS8:8000/ or http://localhost:8000 and record your scenario. At each step of the way you can add a comment make your web test more readable by others.
Figure 8: Web Test Recorder opens in browser window navigate to target application to record.
In my case, I have done a complete recording of browsing for a model plane and going through a full purchase process. Once you’ve completed your recording, stop the recording and this will close the web test recorder and bring you back to the Visual Studio window with the recorded web test tab with all the recorded URLs. It will automatically re-run the web test to automatically identify and promote dynamic parameters so subsequent requests are parameterized and handled correctly.
For this specific application, and workload, the dynamic playback run fails (and this could happen depending on your application behavior) because the response URL validation test fails. This is a validation rule that is added out of the box for a web test, and the reason this fails is that in tail spin toys the application puts the receipt number as part of the response URL and this will vary each time you run the test; so you cannot compare this with earlier recorded response URLs that contains the receipt URL. For that reason, we can delete this validation for this scenario.
Figure 9: Delete Response URL validation rules
Once you have done this, you can re-run the web test to verify that it is passing.
Figure 10: Replay a web test to debug and fix any errors
TIP: In a real world scenario, there are will scenario’s where you will need to debug your web test to identify and resolve issues. Visit the content index for web and performance testing at my blog http://blogs.msdn.com/nkamkolkar/ and/or follow it to visit the post for debugging techniques http://blogs.msdn.com/b/edglas/archive/2010/03/24/web-test-authoring-and-debugging-techniques-for-visual-studio-2010.aspx
At this point you should have a web test against tail spin toys application that runs consistently and doesn’t fail. Playback allows you to view response time, error code status, and response size for a single user. For a given request/response, you see a preview of the html that was returned, and you can view the actual request and response data. The context parameters shows what values were in the virtual users context, and you can also see the results of any validation or extraction rules.
Your next step is to create a load scenario that can scale up the load, add this web test to it and run the load scenario.
Load Testing with Visual Studio 2010 Ultimate
Load tests enable to you simulate many users hitting an application at the same time. The primary scenario for load testing is performance testing. How will the server respond to so many users? Will response times be unacceptably slow? Will error rates be acceptable? Another is capacity planning, where you may want to understand how large a server you will need to support the expected user load. You start out creating a load test by walking through the load test wizard. The wizard will guide you through creating a scenario, which you can think of as a particular group of users. The load test scenario primarily contains:
1. The load pattern, which defines how users will running at any given point in the test
2. The test mix, which defines which test scripts they will be using. A web test should roughly be viewed as a single users walk through the app.
To keep this article a reasonable length, I will not go into the full details of the elements of the load test wizard since the articles primary goal is to get you started. Perhaps this is a subject of a subsequent article or blog.
When you add a load test, you will walk through a wizard. For getting started if you go through the basic wizard accepting the defaults you should be fine. Below, I will share some specific screens that are relevant to have a manageable load test for the VPC/VHD environment. For more information on load test wizard you can visit: http://msdn.microsoft.com/en-us/library/ms182572(v=VS.90).aspx
Figure 11: Load test wizard allows you to set up load scenario that scales up the load
You can select and/or define your load pattern for the scenario; given you are running this on a VPC/VHD change the default load from 25 users to say 5 virtual users as in figure 10.
Figure 12: Selecting a load pattern
Continue to choose defaults on the Test Mix Model screen. Following that, select “Add” and choose "TailSpinWebTest" or the equivalent name for the web test that you added earlier. Note that the "TestMethod1" is the default test that got created when you opened a test project, you can ignore this.
Figure 13: You can add web tests or unit tests to load scenarios using the load test wizard.
Continue to accept the defaults in the wizard until you to come to the Run Settings screen. Here you determine how your load test should run. Change the default Run duration to 1 minute again; once you deploy this to your test labs you can run 10+ hour runs easily.
Figure 14: Load test run settings
At this point you have a full load test scenario defined which you can run to generate load and then performance analysis. Note that a load test scenario in Visual Studio 2010 appears as “.loadtest” node and a web test appears as a “.webtest” node in the solution explorer under your test project. To edit either one at any time, you can simply double click the appropriate test type.
To run your load test simply hit the green “>” tool box button just below the tab title for the load test. This will start your load test and bring up the load test analyzer which shows your running load tests and performance metrics.
Figure 15: Running a load test
As you deploy the load test in your test lab and run real load tests, you will want to familiarize yourselves with the load test integration with ASP.NET profiler that allows you to not only get down to the exact method level detail for slow performing methods, but also show you the exact SQL queries and associated performance metrics. Visual Studio 2010 Ultimate also integrates with partner technologies for JAVA diagnostics. Below is an example screen shot of what ASP.NET profiler provides you insight into.
Figure 16: ASP.NET profiler integration with Tier Interaction Profiling enabled showing slow SQL queries
Working with Load Test Results Analyzer
Visual Studio 2010 Ultimate has integrated Load Test Analyzer that provides monitoring of the application and system under test in addition to providing visibility into key performance indicators, in addition to average, min, max, standard deviation, and 90th percentile data. You can view error rates, drag and drop metrics and also view innovative new visualizations such as VUSER ACTIVITY .
Figure 17: Load test analyzer monitors during the load test and provides for post run analysis
Load test analyzer also allows you to make analysis notes, generate run to run comparison as excel reports and also provides the ability to export your load test results as a load test archive which you can email to another Visual Studio 2010 Ultimate user for offline analysis.
Summary
Visual Studio 2010 Ultimate includes comprehensive load testing capabilities for your web applications. In this article I hope I have given you a way to quickly experience the basic flow and usage of the product and put you on a path to try out more advanced scenarios. There are many resources available in form of online MSDN documentation, blogs, videos, white papers, and forums to help you get to the next stage of using the product to solve real complex scenarios. You can find more information and resources at my recently started blog http://blogs.msdn.com/b/nkamkolkar
Blog Post: Deploying a VSTO Excel 2007 add-in to All Users (Visual Studio 2008 SP1)
[Geography] (Site Home)_____________________________________________ !!!! [PERFORM THIS STEP FIRST] !!!! To be able to deploy an Add-in to all users (without manually installation for each user account on a machine) you must download and install http://support.microsoft.com/kb/976811 (A 2007 Office system application does not load an add-in that is developed by using VSTO); To enable the hotfix package, follow these steps: Go to Start menu; Type regedit, and then press ENTER. Locate and then click the f ...
_____________________________________________
!!!! [PERFORM THIS STEP FIRST] !!!!
To be able to deploy an Add-in to all users (without manually installation for each user account on a machine) you must download and install http://support.microsoft.com/kb/976811 (A 2007 Office system application does not load an add-in that is developed by using VSTO);
To enable the hotfix package, follow these steps:
- Go to Start menu;
- Type regedit, and then press ENTER.
- Locate and then click the following registry subkey:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office2.0\Common\General\
- On the Edit menu, point to New, and then click DWORD Value.
- Type EnableLocalMachineVSTO, and then press ENTER.
- Right-click EnableLocalMachineVSTO, and then click Modify.
In the Value data box, type 1, and then click OK.
Exit Registry Editor.
>>> You do not need this HotFix when creating VSTO 2010 add-in deployment projects from Visual Studio 2008 SP1. <<<
_____________________________________________
- For deploying an Add-in you have a couple of choices http://msdn.microsoft.com/en-us/library/wtzawcsz.aspx (Deploying Applications and Components):
– ClickOnce Deployment (http://msdn.microsoft.com/en-us/library/t71a733d.aspx) or
– Windows Installer Deployment (http://msdn.microsoft.com/en-us/library/2kt85ked.aspx);
_____________________________________________
You cannot use the ClickOnce approach to deploy an Add-in to all users because this method’s setup actions install the registry keys inside the active user account location (HKEY_CURRENT_USER).
- To be able to achieve the functionality that you desire, you must deploy your Add-in by using a MSI approach: http://msdn.microsoft.com/en-us/library/cc616991.aspx (Deploying a Visual Studio Tools for the Office System 3.0 Solution for the 2007 Microsoft Office System Using Windows Installer).
_____________________________________________
1. Open 'Visual Studio 2008';
2. Go to 'File' > 'New' > 'Project';
3. In 'Project Types' > go to 'Visual C#' > 'Office' > '2007' and select 'Word2007 Add-in';
4. Choose a name for the project and click 'OK';
Add a ribbon and some test buttons to your Add-in (you can skip to step 5 if you do not want to perform these steps)
4.1 Right-click the Add-in project and from the context menu choose ‘Add’ then select ‘New Item…’;
4.2 In the ‘Add New Item’ window, under the ‘Templates’ section, select the ‘Ribbon (Visual Designer)’ template;
4.3 Type the desired name for the new ribbon component (for example ‘MyRibbon’), and click ‘Add’;
4.4 A visual editor appears, allowing you to customize your newly added component. Notice the ‘Toolbox’ on the right-hand side of the image below.
4.5 From the ‘Toolbox’, using your mouse, drag 3 buttons, a split-button and a separator into the Ribbon tab;
4.6 You can set the properties for each visual items added at the previous step using the ‘Properties’ window. Use the ‘Label’ property to set the name of each item.
Use the ‘OfficeImageId’ to attach icons next to these elements.
You have the choice of adding your custom images (on the ‘Image’ property, click ‘(none)’ and browse to a supported picture format) or using the Office built-in resources: go to http://www.microsoft.com/downloads/details.aspx?FamilyID=12b99325-93e8-4ed4-8385-74d0f7661318&displaylang=en and download the ‘Office 2007 Icons Gallery’.
(To install this download: 1.Download the file by clicking the Download link (above) and saving the file to your hard disk. 2. Double-click the Office2007IconsGallery.exe program file on your hard disk to start the installation. Follow the instructions to install the file.
Instructions for use: Once you have installed this download, use Excel as you normally would. The Office Icon Gallery appears on the Developer Tab when this document is open. Note: To enable the Developer tab, click the Microsoft Office button and then click Excel Options. In the Popular section, under Top options for working with Excel, select the Show Developer tab in the Ribbon.)
Select an icon from one of the 9 the Built-in galleries and click it. A pop-up dialog box will be shown. Copy the ‘imageMso’ code into the ‘OfficeImageId’ property belonging to your object. After you build and run the Add-in project your visual elements will have that image displayed as an icon.
To add information into the drop-down visual elements (a ‘gallery’ control in our example), just click it to access its property window, and go to ‘Items’ from the list;
Click ‘(Collection)’ and the ‘DropDownItem Collection Editor’ will appear. Use the ‘Add’ button to insert new elements. You can also modify their icons just as you did at the previous steps (using ‘OfficeImageId’ property).
You can set actions for the buttons you just added.
To make your tab appear first and therefore replace ‘Home’ tab making it the first one seen by your user, click the tab from the Ribbon visual designer and go to ‘Position Type’ property. Set its value to ‘BeforeOfficeId’.
After you change ‘Position Type’ from default value, a new property will be available: ‘OfficeId’.
Select the ‘OfficeId’ property and set it to ‘TabHome’.
Caution: the built-in tab names are key sensitive. Setting the OfficeId property to TabHome value will correctly position your tab before the Home tab. If you set the OfficeId as tabHome, your new tab will be placed at the end of the built-in tabs list.
Next, select the ‘ControlIdType’ property and choose ‘Custom’ from the drop-down list.
This action will make your custom tab to be displayed before ’Home’ tab.
4.6 Run the Add-in. Set it to ‘Release’ configuration, and press ‘Start Debugging’
4.7 Running the Add-in for the first time:
Make sure you set the build configuration to release
5. In the ‘Solution Explorer’, right-click the Add-in Solution and click 'Configuration Manager' to show the build configuration manager for the Visual Studio solution.
6. In the 'Configuration Manager' dialog box, for 'Active Solution Configuration', select 'Release'.
-- Adding a deployment project –
7. On the 'Visual Studio 2008'>'File' menu, expand 'Add' and click 'New Project' to add a new project.
8. In the 'Add New Project' dialog box, in the 'Project types' pane, expand Other 'Project Types' and then select 'Setup and Deployment'.
9. In the 'Templates' pane, select 'Setup Project' from the 'Visual Studio installed templates' group.
10. Type a name and click 'OK' to finish this step and create the new setup project.
-- To add the Add-in project output to the setup –
11. In the 'Solution Explorer', right-click the setup project, click 'Add' and then 'Project Output'.
12. In the 'Add Project Output Group' dialog box, confirm that the AddIn project is selected, and the 'Primary Output' option is selected.
13. Click 'OK' to add the project output to the setup project.
-- To add the deployment and application manifests –
14. In the Solution Explorer, right-click the setup project, click 'Add', and click 'File'.
15. In the 'Add Files' dialog box, navigate to the Word AddIn output directory. Usually the output directory is the 'bin\release' subfolder of the project root directory, depending on the selected build configuration.
16. Select the '[WordAddinName].vsto' and '[WordAddinName].manifest' files and click 'Open' to add these two files to the setup project.
-- Excluding Dependencies –
Referencing the components that Word AddIn requires: These components must be excluded and deployed using prerequisite packages to allow them to be registered correctly.
17. To exclude the Word AddIn project dependencies, in the 'Solution Explorer', in the Word AddIn setup node, select all dependency items beneath the 'Detected Dependencies' item except for Microsoft .NET Framework.
18. Right-click the group and select ‘Properties’, change the 'Exclude' property to 'True' to exclude the dependent assemblies from the setup project. .
-- To configure dependent components as prerequisites –
19. In the 'Solution Explorer', right-click the Setup project and select 'Properties'.
20. Click 'Prerequisites'.
21. In the ‘Prerequisites’ dialog box, perform the following tasks.
22. Select 'Create setup program to install prerequisite components'. This creates the 'setup.exe' bootstrapper together with the MSI file.
23. In the 'Choose prerequisites to install' list, select the following: 'Windows Installer 3.1', '.NET Framework 3.5', '2007 Microsoft Office Primary Interop Assemblies', 'Visual Studio Tools for the Office system 3.0 Runtime'.
24. For 'Specify the install location for prerequisites', select 'Download prerequisites from the component vendor's web site’.
25. Click 'OK' to close the 'Prerequisites' dialog box.
-- Configuring the required registry keys –
26. In the 'Solution Explorer', right-click the Setup project.
27. Expand 'View', click 'Registry'.
28. In the Registry editor, expand HKEY_LOCAL_MACHINE and then Software.
29. Create the key hierarchy required for the add-in registration: HKEY_LOCAL_MACHINE\Software\Microsoft\Office\Word\Addins\SampleCompany.WordAddInName
30. Right-click the 'SampleCompany.WordAddInName' key, select 'New' and click 'String value'.
Use these steps to add three more values. Use the following names and data type:
'FriendlyName' of type 'String',
'LoadBehavior' of type 'DWORD',
'Manifest' of type 'String'.
31. Right-click the 'Description' value in the registry editor and click 'Properties' Window. Enter a text that will be displayed as a description in 'Control Panel'.
32. Select the 'FriendlyName' key in the registry editor. Enter a text that will define the name of the Addin in 'Control Panel'.
33. Select the 'LoadBehavior' key in the registry editor. In the 'Properties' Window, change the 'Value property' to 3. The value 3 for the LoadBehavior value indicates that the add-in should be loaded at startup of the host application.
34. Select the 'Manifest' key in the registry editor. In the Properties Window, change the Value property to [TARGETDIR]WordAddInName.vsto|vstolocal
35. In the Registry editor, right-click the 'SampleCompany.WordAddInName' key and click 'Properties'. Set the value of 'DeleteAtUninstall' to 'True' to ensure that the registry keys are deleted when the Visual Studio Tools for Office add-in is uninstalled.
36. Right click the Word Add-in project > select 'Build' and then perform the same step for its setup project.
37. Go to the directory where the Visual Studio editor has saved your Add-in solution, then navigate to [Setup Project] folder, then go to 'Release' and you will find the '.MSI' installer and the 'setup.exe' deployment files.
For more information, please visit:
- http://msdn.microsoft.com/en-us/library/cc563937.aspx#VSTO3SolutionPart1_KnownIssues (Deploying a Visual Studio Tools for the Office System 3.0 Solution for the 2007 Microsoft Office System Using Windows Installer (Part 1 of 2))
- http://msdn.microsoft.com/en-us/library/cc616991.aspx (Dploying a Visual Studio Tools for the Office System 3.0 Solution for the 2007 Microsoft Office System Using Windows Installer (Part 2 of 2))
Database-Backed Refreshable Beans with Groovy and Spring 3
[Programming] (No Fluff Just Stuff)In 2009 I published a two-part series of articles on IBM developerWorks entitled Groovier Spring. The articles showed how Spring supports implementing beans in Groovy whose behavior can be changed at runtime via the "refreshable beans" feature. This feature essentially detects when a Spring bean backed by a Groovy script has changed, recompiles it, and replaces the old bean with the new one. This feature is pretty powerful in certain scenarios, for example in PDF generation; mail or any kind of ...
In 2009 I published a two-part series of articles on IBM developerWorks entitled Groovier Spring. The articles showed how Spring supports implementing beans in Groovy whose behavior can be changed at runtime via the "refreshable beans" feature. This feature essentially detects when a Spring bean backed by a Groovy script has changed, recompiles it, and replaces the old bean with the new one. This feature is pretty powerful in certain scenarios, for example in PDF generation; mail or any kind of template generation; and as a way to implement runtime modifiable business rules. One specific use case I showed was how to implement PDF generation where the Groovy scripts reside in a database, allowing you to change how PDFs are generated by simply updating Groovy scripts in your database.
In order to load Groovy scripts from a database, I showed how to implement custom ScriptFactoryPostProcessor and ScriptSource classes. The CustomScriptFactoryPostProcessor extends the default Spring ScriptFactoryPostProcessor and overrides the convertToScriptSource method to recognize a database-based script, e.g. you could specify a script source of database:com/nearinfinity/demo/GroovyPdfGenerator.groovy. There is also DatabaseScriptSource that implements the ScriptSource interface and which knows how to load Groovy scripts from a database.
In order to put these pieces together, you need to do a bit of configuration. In the articles I used Spring 2.5.x which was current at the time in early 2009. The configuration looked like this:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!-- set data source props, e.g. driverClassName, url, username, password... --> </bean> <bean id="scriptFactoryPostProcessor" class="com.nearinfinity.spring.scripting.support.CustomScriptFactoryPostProcessor"> <property name="dataSource" ref="dataSource"/> </bean> <lang:groovy id="pdfGenerator" script-source="database:com/nearinfinity/demo/DemoGroovyPdfGenerator.groovy"> <lang:property name="companyName" value="Database Groovy Bookstore"/> </lang:groovy>
In Spring 2.5.x this works because the <lang:groovy> tag looks for a Spring bean with id "scriptFactoryPostProcessor" and if one exists it uses it, if not it creates it. In the above configuration we created our own "scriptFactoryPostProcessor" bean for <lang:groovy> tags to utilize. So all's well...until you move to Spring 3.x at which point the above configuration no longer works. This was pointed out to me by João from Brazil who tried the sample code in the articles with Spring 3.x, and it did not work. After trying a bunch of things, we eventually determined that in Spring 3.x the <lang:groovy> tag looks for a ScriptFactoryPostProcessor bean whose id is "org.springframework.scripting.config.scriptFactoryPostProcessor" not just "scriptFactoryPostProcessor." So once you figure this out, it is easy to change the above configuration to:
<bean id="org.springframework.scripting.config.scriptFactoryPostProcessor"
class="com.nearinfinity.spring.scripting.support.CustomScriptFactoryPostProcessor">
<property name="dataSource" ref="dataSource"/>
</bean>
<lang:groovy id="pdfGenerator"
script-source="database:com/nearinfinity/demo/DemoGroovyPdfGenerator.groovy">
<lang:property name="companyName" value="Database Groovy Bookstore"/>
</lang:groovy>
Then, everything works as expected and the Groovy scripts can reside in your database and be automatically reloaded when you change them. So if you download the article sample code as-is, it will work since the bundled Spring version is 2.5.4, but if you update to Spring 3.x then you'll need to modify the configuration in applicationContext.xml for example #7 (EX #7) as shown above to change the "scriptFactoryPostProcessor" bean to be "org.springframework.scripting.config.scriptFactoryPostProcessor." Note there is a scheduled JIRA issue SPR-5106 that will make the ScriptFactoryPostProcessor mechanism pluggable, so that you won't need to extend the default ScriptFactoryPostProcessor and replace the default bean, etc. But until then, this hack continues to work pretty well.
Part-time data entry person needed (Bensenville)
[Jobs, Jobs (not Steve)] (craigslist | all jobs in chicago)We are looking for a part-time data entry person who is familiar with Word and Excel. This person will need to copy and paste information into a coding template at the correct spot. Each template will have different sections, which require different information. High accuracy required. Work load varies, ranging from 8 - 24 hours per week. Work hours are 9:00 - 5:30 during weekdays. Requirements: - Very detail oriented - Accurate - Able to work with flexible work schedule - ...
This person will need to copy and paste information into a coding template at the correct spot. Each template will have different sections, which require different information. High accuracy required. Work load varies, ranging from 8 - 24 hours per week. Work hours are 9:00 - 5:30 during weekdays.
Requirements:
- Very detail oriented
- Accurate
- Able to work with flexible work schedule
- Very familiar with Microsoft Office Suite
This is a part-time position with a hourly pay. Please email your resume for application.
Blog Post: WP7 Code: Managing Application State
[SAP] (Site Home)Visual Studio and the Windows Phone Developer Tools make building software for the Windows Phone similar to building desktop or browser applications. However, beyond these similarities crafting a phone application differs fundamentally from building an application aimed at a device with a keyboard, large screen, running on AC power, and with reliable network connectivity (to name just a few differences). Mobile device users are less likely to tolerate applications that demand too much of their a ...
Visual Studio and the Windows Phone Developer Tools make building software for the Windows Phone similar to building desktop or browser applications. However, beyond these similarities crafting a phone application differs fundamentally from building an application aimed at a device with a keyboard, large screen, running on AC power, and with reliable network connectivity (to name just a few differences). Mobile device users are less likely to tolerate applications that demand too much of their attention, such as taking too much time to display information or to respond to actions. Consequently developers rely on techniques that make their applications provide a good experience.
This post shows how to extend the GeoFencing application to save and restore application state. Doing so in applications that need time to initialize can lower their launch time, one of the first thing their users will notice. The application state for the GeoFencing application comprises the perimeter’s center and radius. Preserving them across invocations allows the application to display information before acquiring a location fix (which, depending on the environment, could take a while). In addition to improving the perceived responsiveness storing the information about the perimeter also offers a better experience, allowing users to leave the application and then restart it without dropping the data.
Start by adding to the project a class representing the saved perimeter data (make sure you reference the System.Runtime.Serialization.dll which holds the namespace with the same name):
using System.Device.Location;
using System.Runtime.Serialization;namespace GeoFencing
{
[DataContract]
public class PersistentRecord
{
[DataMember]
public GeoCoordinate PerimeterCenter { get; set; }
[DataMember]
public double PerimeterRadius { get; set; }
}
}
For this example I chose simple XML serialization—probably not a good choice if you’re dealing with lots of data and/or blobs.
Add a static property to the App class for holding the perimeter data. Next extend the Application_Activated, Application_Deactivated, and Application_Closing default implementations (template method) to load from/save to the PhoneApplicationService’s dictionary (to be picked up by a tombstoned application instance), or save it to the application’s isolated storage (to be picked up by a new application instance). The SavePersistentData method does all the work involving isolated storage.
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey("GeoFencing"))
{
State = (PersistentRecord)PhoneApplicationService.Current.State["GeoFencing"];
}
}// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
if (State != null)
{
PhoneApplicationService.Current.State["GeoFencing"] = State;
SavePersistentData();
}
}// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
if (State != null)
{
SavePersistentData();
}
}public static PersistentRecord State { get; set; }
private static void SavePersistentData()
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = store.CreateFile("center.dat"))
{
var serializer = new DataContractSerializer(typeof(PersistentRecord));
serializer.WriteObject(stream, State);
}
}
}
Note that the default implementation of Application_Launching stays the same (do nothing). Application_Launching is on the critical path for startup. Consequently file I/O +deserialization (or anything else that has the potential of slowing down launch times) doesn’t belong there. An asynchronous invocation (hint: Rx) from the MainPage ctor is a better alternative. First here’s the method that reads the saved information from isolated storage (App class). While it won’t win any robustness awards (that’s left as an exercise for the reader) it does illustrate the point.
public static bool TryReadState(out PersistentRecord savedState)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
IsolatedStorageFileStream stream = null;
savedState = default(PersistentRecord);
try
{
if (store.FileExists("center.dat"))
{
using (stream = store.OpenFile("center.dat", System.IO.FileMode.Open))
{
var serializer = new DataContractSerializer(typeof(PersistentRecord));
savedState = (PersistentRecord)serializer.ReadObject(stream);
}
return (true);
}}
catch (System.IO.IOException)
{
// swallow
}
catch (System.IO.IsolatedStorage.IsolatedStorageException)
{
// swallow
}
catch (SerializationException)
{
store.DeleteFile("center.dat");
}
return false;
}
}
The final updates to the MainPage.xaml.cs enable it to read and use the saved perimeter information. The following Rx query (placed after InitializeComponent) surfaces the saved state as an IObservable<PersistentRecord>. The Start method invokes TryReadState asynchronously ,and ObserveOnDispatcher brings the events into the main (UI) thread. As there is at most one persistent record Take(1) drops everything else after that.
var savedStates = Observable.Start(() =>
{
if (App.State != null)
{
return App.State;
}
else
{
PersistentRecord savedState;
var isOk = App.TryReadState(out savedState);
return isOk ? savedState : default(PersistentRecord);
}
}).Where(e => e != null).ObserveOnDispatcher().Take(1);
The next change entails merging the stream corresponding to the radius position with the restored value provided by the App instance (either from the PhoneApplicationService or isolated storage):
var radii = this.slider1.GetValueChangedEventStream().Select(e => e.NewValue).StartWith(50)
.Merge(savedStates.Select(s => s.PerimeterRadius));
When either the perimeter’s center or radius change (via user input) a new PersistentRecord instance is set on the App object. The CombineLatest combinator provides an elegant solution for this:
centers.CombineLatest(radii, (l, r) => new { Center = l, Radius = r }).Subscribe(crs =>
{
App.State = new PersistentRecord { PerimeterCenter = crs.Center
, PerimeterRadius = crs.Radius
};
});
The subsequent UI updates and distance computations (Rx queries) must use either the saved value or, upon the arrival of the first reading, the values from the geolocation subsystem. The TakeUntil combinator passes through the saved information until the arrival of the first center setting, extracted via the Select combinator :
var savedOrAcquiredCenters = centers.Publish(cs => cs.Merge(savedStates.Select(s => s.PerimeterCenter).TakeUntil(cs)));
The code for MainPage.xaml.cs follows. Building and running the application requires the XAML for the UI, as well as the App. xaml.cs and the PersistentRecord class.
public partial class MainPage : PhoneApplicationPage
{
private GeoCoordinateWatcher gcw;
public GeoCoordinateWatcher Gcw
{
get
{
if (gcw == null)
gcw = new GeoCoordinateWatcher { MovementThreshold = 0.0 };
return gcw;
}
}// Constructor
public MainPage()
{
InitializeComponent();geoFenceButton.IsEnabled = false;
slider1.IsEnabled = false;var savedStates = Observable.Start(() =>
{
if (App.State != null)
{
return App.State;
}
else
{
PersistentRecord savedState;
var isOk = App.TryReadState(out savedState);
return isOk ? savedState : default(PersistentRecord);
}
}).Where(e => e != null).ObserveOnDispatcher().Take(1);var positionUpdates = from sc in Gcw.GetStatusChangedEventStream()
where sc.Status == GeoPositionStatus.Ready
from pos in Gcw.GetPositionChangedEventStream()
let location = pos.Position.Location
where (!Double.IsNaN(location.Latitude) && !Double.IsNaN(location.Longitude) && location.HorizontalAccuracy <= 100.0)
select location;positionUpdates.Take(1).Subscribe(_ => geoFenceButton.IsEnabled = true);
positionUpdates.Subscribe(l =>
{
this.hereTextBlock.Text = string.Format( "{0:###.00000000N;###.00000000S;0}, {1:###.00000000E;###.00000000W;0}"
, l.Latitude
, l.Longitude
);
});var clicks = this.geoFenceButton.GetClickEventStream();
clicks.Take(1).Subscribe(_ =>
{
this.slider1.IsEnabled = true;
});var centers = positionUpdates.Publish(updates => from position in updates
from click in clicks.Take(1).TakeUntil(updates)
select position);var radii = this.slider1.GetValueChangedEventStream().Select(e => e.NewValue).StartWith(50)
.Merge(savedStates.Select(s => s.PerimeterRadius));centers.CombineLatest(radii, (l, r) => new { Center = l, Radius = r }).Subscribe(crs =>
{
App.State = new PersistentRecord { PerimeterCenter = crs.Center
, PerimeterRadius = crs.Radius
};
});var savedOrAcquiredCenters = centers.Publish(cs => cs.Merge(savedStates.Select(s => s.PerimeterCenter).TakeUntil(cs)));
savedOrAcquiredCenters.Subscribe(center =>
{
centerTextBlock.Text = string.Format("{0:###.00000000N;###.00000000S;0}, {1:###.00000000E;###.00000000W;0}", center.Latitude, center.Longitude);
}
);radii.Take(2).Subscribe(r => this.slider1.Value = r);
radii.Subscribe(radius =>
{
this.fenceTextBlock.Text = string.Format("Perimeter radius {0:####.00}m",radius);
});var distancesFromCenter = positionUpdates.CombineLatest(savedOrAcquiredCenters, (p, c) => p.GetDistanceTo(c)).DistinctUntilChanged();
var distancesToFence = distancesFromCenter.CombineLatest(this.slider1.GetValueChangedEventStream(), (d, radius) => radius.NewValue - d);
distancesToFence.Subscribe(distanceToFence =>
{
var msg = string.Empty;
if (distanceToFence >= 0)
{
msg = string.Format("{0:####.00}m from the fence", this.slider1.Value - distanceToFence);
distanceTextBlock.Foreground = new SolidColorBrush(Colors.Green);
}
else
{
msg = string.Format("{0:####.00}m outside the fenced perimeter", Math.Abs(distanceToFence));
distanceTextBlock.Foreground = new SolidColorBrush(Colors.Red);
}
this.distanceTextBlock.Text = msg;
});
}
In summary, this post has shown:
- How to tap into the application’s lifecycle via the methods exposed by the App class
- How to use Observable.Start to asynchronously execute an Action, and bring the results to the UI thread with ObserveOnDispatcher
- How to leverage Rx to combine events from several event streams
Blog Post: Hello, Lync!
[SharePoint] (Site Home)Getting started with Lync controls is actually quite easy. In this post, we will explore the basic steps required to create a sample application using Lync controls, and then we will run a simple “Hello, Lync” application which shows a presence indicator with photo. Creating your first Lync Controls project We’ll use the WPF application libraries in this sample. 1. To begin, you must first install and sign in to Microsoft Lync. All Lync controls require a running instance o ...
Getting started with Lync controls is actually quite easy. In this post, we will explore the basic steps required to create a sample application using Lync controls, and then we will run a simple “Hello, Lync” application which shows a presence indicator with photo.
Creating your first Lync Controls project
We’ll use the WPF application libraries in this sample.
1. To begin, you must first install and sign in to Microsoft Lync. All Lync controls require a running instance of Microsoft Lync to function properly.
2. Next, be sure you have installed the Lync Client Platform SDK.
3. Open Visual Studio, and create a new project using the template Lync WPF Application
4. Open the file Window1.xaml
The template already includes some very basic XAML which instantiates a PresenceIndicator control and a TextBlock:
<!--
Show the presence indicator. Hover over the icon to see the contact card.
Set Source to a valid SIP URI in your organization.
-->
<controls:PresenceIndicator
x:Name="Presence"
Source="sip:john@contoso.com"
PhotoDisplayMode="Large"
/>
<!-- Use the DisplayName property from PresenceIndicator to show the user's name -->
<TextBlock
Text="{Binding DisplayName, ElementName=Presence}"
Margin="4,0,0,0"
VerticalAlignment="Center"
/>
If you run the application at this point, you are very likely to see the result “Unknown Contact” in the text block, along with the default silhouette image and grey presence bar. This is because we have not yet selected a valid contact.
Connecting your control to a source of data
All Lync Controls which operate on a single contact support a property called Source which is used to tell the control which contact should be displayed. The Source property accepts any string, and the control will search for a contact matching that string. While this behavior is very robust, it tends to yield inconsistent results. A search for a string such as “Joe” or “Mary” in an organization of any non-trivial size is likely to return more than one result, and the one which gets returned first is the one which your control will display.
You can make the control behave in a more deterministic fashion by giving it more specific source criteria. One option is to use the Lync API to find a Contact object which meets your needs, and pass that object to the Source of your control in the code-behind; but using that API adds some complexity to your code which isn’t necessary for most applications. A simpler option, which works very well, is to specify the SIP URI value for the contact you wish to use. In fact, if you provide a SIP URI and be sure to prefix it with the string sip:, the Lync Control will load your contact much more quickly, because it does not perform a search. Instead, it simply asks the server for the specific contact you are requesting, based on an exact match of the specified SIP URI.
You can easily view a user’s SIP URI in Lync by simply unchecking the option “Show Friendly Name” in the contact list options menu which appears on the right hand side of the contact list toolbar. Once you know the SIP URI of the contact you wish to display, you can wire up your application:
6. Edit the XAML for your new window, and edit the Source property on your presence indicator. For this example, use your own SIP URI. The XAML will look something like this:
<controls:PresenceIndicator
x:Name="Presence"
Source="sip:schuddle@microsoft.com"
PhotoDisplayMode="Large"
/>
Despite having assigned a valid SIP URI, the TextBlock still shows the value “Unknown Contact” in the design-time preview. Not to worry, we can leave this value alone, and let the magic of binding take care of that problem. As you can see, the Text property of our TextBlock is bound to the DisplayName of the presence control. This setting will automatically update at runtime when the PresenceIndicator control resolves to our selected contact.
Try running your application now. You should see something like this:
And, you’re done! Congratulations, you have just created your first application using the new Lync Client Platform SDK.
As you have seen, it is really very simple to add Unified Communications functionality into your applications. But there is much more you can do with these controls once you are familiar with the basic interface they support. In our next post, we will explore the contact search controls, and how they can be used to integrate Lync’s powerful search feature into your application.
Consuming web services with the Dojo Toolkit
[Java] (java.blogs Recent Entries)rajneesh's posterous This article is about consuming web services—both simple services and RESTful web services— using the Dojo Toolkit. To get the most out of this article, you need to have the following installed and configured on your system: A text editor or integrated development environment (IDE) (This article uses the Eclipse JavaScript IDE.) A web server Dojo Toolkit overview As the focus on building better Rich Internet Applications (RIAs) increases, JavaScript fr ...
This article is about consuming web services—both simple services and RESTful web services— using the Dojo Toolkit. To get the most out of this article, you need to have the following installed and configured on your system:
- A text editor or integrated development environment (IDE)
(This article uses the Eclipse JavaScript IDE.)- A web server
As the focus on building better Rich Internet Applications (RIAs) increases, JavaScript frameworks emerge to enable web developers to make their applications more engaging.
Prepackaged JavaScript libraries offer many advantages. First, using code that is already verified in different browsers and different platforms can significantly reduce the amount of testing required to verify functions on many different browsers, thereby reducing the amount of defects in your code. Second, using code that is already written and tested saves a substantial amount of time and enables you complete your application faster.
Give Rational Application Developer a try
Download a free trial version of IBM Rational® Application Developer for WebSphere® Software, which helps developers quickly design, develop, test, analyze, and deploy high-quality Java™, Java Platform, Enterprise Edition (Java EE), Web 2.0, service-oriented architecture (SOA), and portal applications. Rational Application Developer includes features to quickly build skills on emerging Java EE and web technologies, automate code verification, build and test, and enables agile software development for today's fast-paced software delivery needs.
The Dojo Toolkit (see Resources) is a collection of JavaScript code that offers significant functions. The Dojo provides JavaScript methods that you can use to animate elements, fade them in and out, and make Ajax calls.
This article is about using the Dojo Toolkit along with Ajax and RESTful web services to offer your users better functions and a better user experience. The article provides an auto-completion example that fills in a text box to match results as a user types. This user interaction pattern for searching has emerged as a commonplace—albeit smooth—way to make searches better for your users.
Ajax is a term used to describe a combination of technologies to make calls (asynchronously) to a server while the user's web page stays loaded. This technique has become fairly ubiquitous in today's web pages and plays an important role in making the web experience richer.
In the example in this article, every time the user changes the text in a text box the JavaScript code makes a call to a server to get suggested values. On one hand, this additional function can cause a lot of traffic. On the other hand, if the user is searching repeatedly by posting the entire form, this technique can actually save some amount of traffic.
This example uses the latest version of Eclipse with the JavaScript tooling installed. The tooling offers improved ability to edit JavaScript and HTML pages.
Follow these steps to create a project you can use to create a few HTML files so you can follow the example:
- Create a new static web project by using File > New > Project.
- Select Web\Static Web Project and click Next.
- Type the name of your project (for example, MyDojoExample).
- Click New Runtime.
- Select New Server from the list and select the Create a new local server check box.
- Type the name of the server as it will display in the server list, and type the name of the directory in which you want to publish your files. Typically, this should be a directory location in which you can publish user web files.
- Click Next on the Static Web Project wizard.
- Leave the context root the same as the name of the project, and leave the name of the web content folder name WebContent.
- Click Finish. Eclipse creates the new project for you.
When you add files to your project, Eclipse automatically publishes the files to the directory that you configured for your server. You can see this in action by creating your first HTML file in this project, which is an index.html file. You will modify this file to include the search box used for the example.
Follow these steps to create the new HTML file:
- Select the WebContent folder in your new project and use your alternate mouse button to open the context menu.
- From the menu, select New > HTML file.
- Type the name of the file in the File name field, and click Next to see the HTML template chooser.
- For this example, select the New XHTML File (1.0 strict) option and click the Finish button.
The new file will look like Listing 1.
Listing 1. The new index.html file
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Insert title here</title> </head> <body> </body> </html>
Now that you have a new static web project and an HTML page, it's time to add an input control to the HTML page. The HTML file contains a few
divtags that make positioning the input control and the suggestions a little easier. The result is the HTML file shown in Listing 2.
Listing 2. The index.html file with controls
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Test web page</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="styles/main.css" /> </head> <body> <div id="wrapper"> <form> <div id="search"><input id="searchBox" type="text" maxlength="2048" name="criteria" title="Search" onkeyup="update()" /></div> <div id="suggestions"></div> </form> </div> </body> </html>
At this point, it isn't much different from the blank version. The bulk of the work is done by using JavaScript code in the web page.
You can include the Dojo Toolkit in one of two ways:
- Use one of the publicly hosted Dojo files locations.
- Download the Dojo Toolkit JavaScript files yourself and include them alongside your own code.
Which method is better depends on your needs.
There are a couple of good reasons to use a publicly hosted file:
- Using a content distribution network (CDN) to host the file simply eliminates the need for you to keep track of the file locally.
- It's one less file that you have to deploy.
The main disadvantage of using a publicly hosted file is that you don't have any control if the CDN is unavailable (a situation that is unlikely, but possible). If, for some reason, the CDN is down, your site might not function well. If you have internal web applications that require high uptime, consider maintaining the Dojo script files locally to decrease your application's points of failure.
To link in the Dojo file from one of the CDNs, modify your index.html file to look like the one in Listing 3.
Listing 3. Including the link to the Dojo CDN location
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Test web page</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="styles/main.css" /> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js" type="text/javascript"></script> </head> <!-- snipped... --> </html>
Downloading and including the Dojo Toolkit base file
If you want to download and include the Dojo Toolkit base file instead of linking to it, you can download the Dojo JavaScript code from the site linked in Resources. After you download this file, you can import it into your project in Eclipse using File > Import. By convention, it's a good idea to create a folder called js or scripts in which you can put not only your JavaScript code files but also the files that you download.
To include the files that you downloaded into your web page, modify index.html to look like Listing 4.
Listing 4. Including a local reference to downloaded dojo.js
<script src="js/dojo.js" type="text/javascript"></script>
Now that you have a basic web page, you are ready to add more functions by adding the JavaScript code that calls the service.
Now that you have the index.html file created and the Dojo JavaScript file is linked into your index.html file, it's time to call a simple service to make suggestions to the user.
This article provides two different examples of calling a service and obtaining a result. This first example is a simple service that doesn't necessarily conform to the typical RESTful web service URL conventions. REST is not a standard, but there are ways of crafting the URLs that allow better interoperability between clients and RESTful web services. The second example is an introduction into using the dojox.rpc.Rest method.
Because the response from the service changes based on the query string, the service does require that you write it in a dynamic web application technology (see Resources to learn more about dynamic web applications). The example shown in Listing 5 is a simple PHP script that has an array of names. Based on what the user provides, it filters out the list of names and adds them to the Extensible Markup Language (XML) response.
Listing 5. A simple service written in PHP
<?php header("Content-type: text/xml"); $data = array( "Bilbo Baggins", "Frodo Baggins", "Samwise (Sam) Gamgee", "Meriadoc (Merry) Brandybuck", "Peregrin (Pippin) Took"); $resultXML = ''; $resultsXML .= '<suggestions>'; foreach ($data as $d) { $pattern = '/'.$_GET['s'].'/'; if (preg_match($pattern, $d)) { $resultsXML .= '<item>'.$d.'</item>'; } } $resultsXML .= '</suggestions>'; print($resultsXML);
You can write a simple script in any other language that returns a similar response to test the JavaScript code.
Making the call to the URL from the Dojo Toolkit could not be easier. Simply set up the arguments for the call, as shown in Listing 6, and pass them to the service.
Listing 6. Setting up the arguments for dojo.xhrGet
var args = { url:"mockService.php?s=" + dojo.byId("searchBox").value, handleAs:"xml", preventCache:true, load:function(data) { // handle the data... }, error:function(error) { target.innerHTML = "Error:" + error; }
The arguments are explained in further detail in Table 1.
Table 1. Arguments for dojo.xhrGet
Argument Description url The arguments include the URL (remember, this URL is not a RESTful URL). handleAs One of json, text, or xml. The PHP script responds with XML, so that is used here. preventCache Use true if you don't want to cache the data. Caching data leads to faster execution, but it's not desirable if the results are completely dynamic. load The callback function to execute when the data is returned by the service. error The callback function to execute if an error occurs. When you have the arguments set up correctly, pass them to the dojo.xhrGet method. The complete code is shown in Listing 7.
Listing 7. The complete JavaScript code
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Test web page</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="styles/main.css" /> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ // You could move all this code to a JavaScript file and include it... dojo.require("dojox.xml.parser"); function update() { if (dojo.byId("searchBox").value.length < 3 ) return; var target = dojo.byId("suggestions"); var args = { url:"mockService.php?s=" + dojo.byId("searchBox").value, handleAs:"xml", preventCache:true, load:function(data) { // handle the data... }, error:function(error) { target.innerHTML = "Error:" + error; } }; var ajaxCall = dojo.xhrGet(args); } //]]> </script> </head> <body> <div id="wrapper"> <form> <div id="search"><input id="searchBox" type="text" maxlength="2048" name="criteria" title="Search" onkeyup="update()" /></div> <div id="suggestions"></div> </form> </div> </body> </html>
In addition to calling standard Ajax-style services, you can also use the Dojo Toolkit to call RESTful web services.
Most RESTful web services follow conventions for the way a URL is constructed for a specific type of request, in addition to the HTTP method used for the request. If you build a RESTful service that fits these conventions, you can use the dojo.rpc.Rest object to make the calls for you. The dojo.rpc.Rest object simplifies the traditional service calls even further.
Listing 8 is an example of calling a RESTful web service using the Dojo Toolkit.
Listing 8. Calling a RESTful web service using dojo.rpc.Rest
// Calling this access the URL hobbits/1 (see Table 2) var service = dojox.rpc.Rest("hobbits"); service("1");
Table 2 lists examples of proper RESTful URLs and HTTP actions that should go with each type of service. Note that not all browsers support all the HTTP methods, so you should test and verify for your needs. For a massive audience, using GET and POST may be the best option.
Table 2. RESTful conventional URLs
Action HTTP method Example URL Find an object GET http://www.example.com/hobbits/1 Find all objects GET http://www.example.com/hobbits/ Delete an object DELETE http://www.example.com/hobbits/1 Create an object POST http://www.example.com/hobbits/ Update a new object PUT http://www.example.com/hobbits/1 If you want to create a full implementation of a RESTful web service, frameworks can help guide you to build URLs that conform to the RESTful URL conventions. See Resources to find more information about various frameworks for different languages.
Displaying the results elegantly
Now that you are calling the service with the Dojo code, you can update the contents of the
divelement to include the query results. This allows the users to see the suggestions as they type.To update the value of the
divelement that contains the suggestions, use the dojo.byId method to get a reference for thedivby the ID and set theinnerHTMLproperty as shown in Listing 9. The dojo.byId method is an alias for the traditional JavaScript document.getElementById method.
Listing 9. Updating the contents of the element
// the full load function... load:function(data) { var rootEl = data.documentElement; var resultHTML = "<ul>"; for (var i = 0; i < rootEl.childNodes.length;i++) { resultHTML += "<li>" + dojox.xml.parser.textContent(rootEl.childNodes[i]) + "</li>"; } resultHTML+="</ul>"; target.innerHTML = resultHTML; },
Now that you have the code working, you can view your index.html page in a browser. When you type a value—for example,
Bag—the suggestions automatically appear in thedivelement. Although not covered in this article, you should use Cascading Style Sheets (CSS) to make thedivelements flow together nicely in a drop-down box with suggestions based on the user's input.
Handling long-running services
There are two major concerns when handling long-running services from a web browser:
- User experience
- Reliability
User experience is affected by long-running services if nothing apparent is changed in the browser while the service is executing. If your web page contains a button that makes an Ajax or RESTful service call and displays the results, you should do something with the browser to show the user that something is happening. Otherwise, you run the risk of a user submitting a form multiple times or becoming impatient with your site and leaving.
To combat the user experience issue, implement an animation (the Dojo Toolkit has methods for those) or disable the submit button so the user cannot submit multiple times. With animated visual (for example, a spinning clock), the user has a sense that something is happening and is much more willing to wait for the process to complete.
Services that are expected to run for a long time can be problematic to call using Ajax, but there can be valid cases for using them. Examples of long-running services are those that aggregate data, generate documents, or archive files.
In situations such as these, it's not a good practice to simply call the service and wait—especially if the service could process for more than a few seconds. If your connection is disrupted or if your browser is closed, you cannot necessarily rely on the callback mechanism to fire when the service is complete.
If you have control over the service, consider returning a unique identifier to your callers that provides them with a way of later asking a different service method about the status of the request. Your browser can either put this number into storage in a cookie locally, or the browser and services can work together to persist the number for the user.
This method of calling services allows you to begin the long-running process on the server side. In the browser, you can implement polling to ask the service tier for the status of the request. (You can the Dojo Toolkit's Timer object for this.) This method provides the user with both flexibility and stability.
Among other things, the Dojo Toolkit enables you to make Ajax calls in your web application to provide RIA functions. Using either one of the CDNs or downloading the Dojo Toolkit files yourself, you can take advantage of prewritten and tested functions.
The Dojo Toolkit provides methods for calling both plain web services using Ajax and RESTful web services. The toolkit allows you to process JSON, XML, and text responses from Ajax services.
How to Create a Safari Extension from Scratch
[Web Design] (Nettuts+)Safari 5, the latest version of Apple’s web browser, introduces extensions. Safari extensions are small add-ons that you can use to expand Safari’s capabilities, built using simple HTML, CSS and JavaScript. In this tutorial, you will learn the basics of extension development by creating a simple extension using Safari 5′s Extension Builder. Introduction In this tutorial we will build a simple extension that adds a button to the main Safari toolbar, and opens up Nettuts+ in a ...
Safari 5, the latest version of Apple’s web browser, introduces extensions. Safari extensions are small add-ons that you can use to expand Safari’s capabilities, built using simple HTML, CSS and JavaScript. In this tutorial, you will learn the basics of extension development by creating a simple extension using Safari 5′s Extension Builder.
Introduction
In this tutorial we will build a simple extension that adds a button to the main Safari toolbar, and opens up Nettuts+ in a new tab when clicked. In the process, you will learn the basics of extension development and Extension Builder in Safari 5.
Developing an extension is very different from regular web development; it allows you to break out of the confines of a normal browser window, and change the browser itself for a unique experience. Fortunately, Safari 5 lets us do this using simple web technologies such as HTML, CSS and JavaScript; and it fully supports the powerful new features of HTML5 and CSS3.
Safari extensions can do five main things:
- Add toolbar items: An extension can add a button to the default Safar toolbar. In addition to performing some action when clicked, a numbered badge can be added to this button (to show, for example, a number of unread messages.)
- Add extension bars: Extension bars are just simple html pages in the form of a toolbar which are added above the tab bar in Safari. They are extremely versatile and can be used for many things. The downside is that they take a lot of space and can become annoying.
- Add tabs & windows: Tabs and windows can be easily modified and added from within an extension. You can change a URL or load custom HTML content to a tab.
- Modify web content with script and style sheet injection: Easily change the appearance or behavior of a single webpage or the whole web using scripts and CSS files.
- Add context menu items: An extension can add a custom item to the contextual (right-click) menu that performs a certain command when selected.
In addition, Safari Extensions provide a very simple way to create a user-defined settings panel inside the built-in Safari Preferences window.
Step 0: Sign Up as a Safari Developer
The first thing to do is sign up as a Safari developer on apple.com. This will allow you to make a Safari Developer Certificate which is needed in order to use Extension Builder. Register over here and then go to the Safari Extension Certificate Utility and follow the instructions.
Once you have your certificate properly installed, it’s time to get started!
Step 1: Meet Extension Builder
Extension Builder is the main tool used to create extensions.
If you’ve never developed for Safari before, you need to turn on the Develop menu. To do this, go to the Advanced tab in Safari Preferences and check the box next to “Show Develop in the menu bar”. The menu should now appear; it contains many useful development tools, but for now, just select “Show Extension Builder.” Make sure you have the latest version of Safari installed.
Extension Builder is made up of a sidebar on the left that lists the extensions you currently have in development and a panel on the right that lets you inspect an extension, edit its properties and settings, install it, and more.
To create a new extension, click the ‘+’ icon in the lower left corner and select “New Extension.” Name the extension whatever you like and save it somewhere. This will create a folder with a name extension “.safariextension”. Go ahead and open this folder.
The extension folder contains all the files and resources for the extension. This is where we put the HTML, CSS, images and scripts that will make up our extension. By default, the folder will contain a file called Info.plist, which is essentially where all the info set in Extension Builder is saved to, in XML form. There is no reason to change this file manually.
Step 2: Set Up the Extension Settings
Many settings are self-explanatory, such as Name, Author and Description. There is no need to change anything for now, just scroll down to the “Extension Chrome” category. These are the user interfaces of the extension that will actually be shown. As mentioned before, you can add extension bars (“Bars”), context menu items and toolbar button. Only a button is needed for our extension, so click “New Toolbar Item”. Toolbar Item 1 should appear with the following properties:
- “Label” is the name of the button that will be shown if the toolbar overflows. “Palette Label” will be shown under the button in the “customize toolbar” dialog, and “Tool Tip” is the text that will appear after hovering on the button for a while. We will set the label to “Nettuts”, and, by leaving the other two properties empty, they will take their values from the Label and be the same.
- The “Image” property is the icon that will appear on the button itself. The image should just be an alpha channel, or a black image with a transparent background, in .png format. The size of the icon ideally should be 16×16 pixels, but can be as large as 18×18; images that are too large will be cropped to fit. We will use a small, 16×16 “plus” icon (icon.png) to indicate Nettuts+.
To apply the image you want to use, first put it in the extension folder, then select it from the dropdown menu in Extension Builder. - “Identifier” can be used to identify the button from a script; we will set it simply to “nettuts”.
- “Command” is the name of the command that will be sent when clicking the button. Set it to “open-nettuts”. We will later use this to detect when the button is clicked.
- “Include by Default” Indicates whether or not the button appears on the user’s toolbar immediately after installing the extension. Since the button is our extensions only feature, keep the setting checked.
Step 3: Create the Global Page
To control our button, we need to have a script, and for that, we need to have an HTML file to load the script. That file is the global HTML page.
The global page is a place to put scripts, data and resources that require no user interface. It is simply a HTML page that is not displayed anywhere. The global page is loaded once per Safari session, and that makes it good for controlling our toolbar button.
Using your preferred text editor, make a new empty HTML file and call it “global.html”. Save this file to the extension folder. In Extension Builder, find the “Global Page File” setting and select the file you created from the dropdown menu.
Open the global page. First, make a script tag in the file. You can also use an external .js file, but since our page contains nothing else but the script, there is no need.
When our button is clicked, safari sends a “command” event, which is called “open-nettuts”, as we set in Extension Builder. In order to respond to this event, we need to install a listener in our global HTML page, like this:
safari.application.addEventListener("command", performCommand, false);
The first parameter is “type” – the type of event the target should listen to. In our case, it’s a command. The second parameter is the function to call when the event occurs; we will simply call it “performCommand”. The last parameter is Boolean – “useCapture”, but it is not important for our needs; set it to false.
Our global page is now listening to commands that Safari sends. Now we need to implement the function “performCommand” to respond appropriately when our button is clicked:
// Function to perform when event is received
Function performCommand(event) {
// Make sure event comes from the button
if (event.command == "open-nettuts") {
// This is where we finally respond to the click
}
}
Our function will receive any command event sent from Safari, so we need to make sure which one it is and respond accordingly. The command property of the event object we receive should be the same as what we set in Extension Builder, so we check that it is indeed “open-nettuts”.
Finally, when we know our button was clicked by the user, we want to perform two things: open a new tab, and set its URL to http://net.tutsplus.com.
var newTab = safari.application.activeBrowserWindow.openTab(); newTab.url = "http://net.tutsplus.com/";
The first line is rather self-explanatory: it finds the current active browser window and opens a new tab in it. This tab is returned and saved in a variable, which is used in the second line to change the URL to nettuts.
Your global page should look like this now:
<script>
// Set up the Listener
safari.application.addEventListener("command", performCommand, false);
// Function to perform when event is received
function performCommand(event) {
// Make sure event comes from the button
if (event.command == "open-nettuts") {
var newTab = safari.application.activeBrowserWindow.openTab(); // Open a new tab
newTab.url = "http://net.tutsplus.com/"; // Set tab URL to nettuts
}
}
</script>
That’s it! our global page now recognizes a click, opens a new tab and directs it to Nettuts. Switch back to Extension Builder, and click “Install” in the top right corner. A button should appear on the toolbar. Click it to make sure it works!
Step 4: Add the Extension Icon
As a final touch, you might want to add an icon to your extension. To do that, all you need to do is add three .png images of an icon to the extension folder: Icon-32.png, Icon-48.png and Icon-64.png. Each file needs to be the size that its name suggests, so Icon-32.png will be 32×32 pixels, and so forth.
As a bonus, the source files for this tutorial include a Photoshop template for creating your own extension icons!
Step 5: Build the Package
Our extension is now finished! To export it, click the “Build Package…” button in Extension Builder and save it somewhere. The extension can be installed by opening the file or dragging it to Safari. You can now send this file, publish it on the internet or try submitting to Apple’s Safari extension gallery.
Conclusion
This tutorial should have provided you with a basic idea of how to create a Safari Extension. Now you can start making you own – browse the Safari extension Gallery for inspiration, or check out my own Widgets Bar extension. For more information about developing extensions, Apple’s Safari Dev Center has a development guide, sample code and documentation. Have fun, and let us know if you’d like more tutorials on this topic!
Blog Post: Silverlight for Windows Phone 7: ListBox Scroll Performance
[SharePoint] (Site Home)Having a basic list scoll is a key scenario for many applications. The Silverlight Windows Phone 7 list box control makes it easy to bind data and get the performance benefits of UI container virtualization. However, in order to get these free performance benefits you need to be careful about how you use it. Here are some tips on how to tweek your list box scroll performance. Simplify ListBox Item Listbox's VirtualizingStackPanel (VSP), calculates the height of items currently in the view an ...
Having a basic list scoll is a key scenario for many applications. The Silverlight Windows Phone 7 list box control makes it easy to bind data and get the performance benefits of UI container virtualization. However, in order to get these free performance benefits you need to be careful about how you use it.
Here are some tips on how to tweek your list box scroll performance.
Simplify ListBox Item
Listbox's VirtualizingStackPanel (VSP), calculates the height of items currently in the view and buffers the UI containers for a screens worth of items above and below what is currently in the view. This works great if the items are of fixed size. If you change the size of the items, the UI virtualization breaks and you don't get any performance benefits.
- Ensure you have the item data template in a fixed sized container (grid).
- Avoid/remove using complex converters, when the same information can be easily provided by the data object.
- Avoid/remove nested structures, example listbox in a listbox item.
- Strongly recommended to not use user control inside the data template.
- Avoid/remove custom controls from the data template
Load the images in background
If you have refered to the performance document, you will know the importance of keeping the UI thread free for better responsiveness to handle input. This means that if you load the images on the UI thread, an input like flick might be lost. David talks about how to move the image loading to a single background thread in his blog LowProfileImageLoader.
Use Data Virtualization
When using ListBox, UI is virtualized, the data is not -- so although a 1,000 item list only has a fraction of the UI elements created, it needs all 1,000 of the data objects created and loaded, which can be a resource issue (long load times / high memory usage). This is because in desktop Silverlight the ListBox and other controls databind to IEnumerables, where the only option the control has is to enumerate the entire list to find out how large it is. Peter has a solution to this in his post on Data Virtualization.
Do not use ListBox**
If your application demands variable height items, but the number of items is managable enough to keep in memory. You should consider a solution without list box, example: David Anson talks about how to use StackPanel instead of a ListBox in his post on DefferedLoadListBox from Delay in order to get good performance from a scenario that's pretty common for social media applications: a scrolling list of entries with a picture and a brief bit of text. Its easy to try it out for your application.
Do not use Nested ListBox
If you have a need for nested list boxes, you should consider a solution similar to ListBoxGroupie
Other tips/approaches
- HttpWebRequest must be used instead of WebClient. The reason for this is, there is a current platform issue where a request created via WebClient always returns on the UI thread, and again, we know that keeping the UI thread free is important.
- When your list(in pivot/pano) is being populated, if you want to display a progress bar use the high performance progress bar
- Sometimes you bind the list to an observable collection. In that case, Add items to the UI thread in batches every some miliseconds (example add 2 items every 20ms). This keeps the UI thread free for input from the user, avoiding stutters and delays, giving the perception of performance. This is much better than adding all 20 items in one go, drawing 20 items straight on the UI thread will keep it busy and wont take in the input from user, example the flick gesture for scroll. BingImageSearch is an example of one such application.
- Peter has a good design example, where he stops loading the items if the list is scrolling. The link will be posted here when his blog is ready.
Finally, we are incorporating all the feedback into the platform and your comments/feedback/questions are appreciated.
Thanks
Silverlight Windows Phone 7 Perfomance Team
Blog Post: Integrating Prism v4 Region Navigation with Silverlight Frame Navigation
[Microsoft Office] (Site Home)Introduction This article covers integrating Prism v4 Region Navigation with Silverlight 4 Frame Navigation. Integration is not directly supported by the Prism v4 Library. The included download provides the required classes to integrate the two navigation API's. The below image pictures the demo application; notice the user friendly, deep link unmapped Uri in the address bar. The right ListBox lists an Item view was opened and subsequently navigated away from. This included application dem ...
Introduction
This article covers integrating Prism v4 Region Navigation with Silverlight 4 Frame Navigation. Integration is not directly supported by the Prism v4 Library. The included download provides the required classes to integrate the two navigation API's.
The below image pictures the demo application; notice the user friendly, deep link unmapped Uri in the address bar. The right ListBox lists an Item view was opened and subsequently navigated away from.
This included application demonstrates how to integrate Prism Region Navigation with Silverlight Frame Navigation. Additionally, we'll examine implementing Non-Linear Navigation in this configuration.
Prerequisites
A general knowledge of Prism regions, modules, MEF, Silverlight Frame Navigation and the Silverlight UriMapper is required to understand this article and demo application.
Before proceeding, please read the Prism v4 Region Navigation Pipeline article. Information presented in that article will not be repeated here.
Integrating Navigation API's
The demo application includes the Prismv4FrameRegionNavigation assembly. This assembly provides the required classes to integrate the two navigation API's such as a content loader, region adapter, journal, navigation service and region behavior. For the remainder of the article, I'll refer to the Prismv4FrameRegionNavigation assembly as, "the assembly."
<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/HomeView" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed" prism:RegionManager.RegionName="MainContentRegion" > <navigation:Frame.ContentLoader> <prism_Regions:FrameContentLoader RegionName="MainContentRegion"/> </navigation:Frame.ContentLoader> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <!--Default applicaiton mapper--> <uriMapper:UriMapping Uri="" MappedUri="/ThePhoneCompany.Views.HomeView"/> <!--Used to add a new record--> <uriMapper:UriMapping Uri="/{moduleName}/{pageName}/add" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}?key=0"/> <!--Used to edit a record--> <uriMapper:UriMapping Uri="/{moduleName}/{pageName}/{key}" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}?key={key}"/> <!--Used to view a page--> <uriMapper:UriMapping Uri="/{moduleName}/{pageName}" MappedUri="ThePhoneCompany.{moduleName}.Views.{pageName}"/> <!--Used to navigate to a page in the Shell--> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/ThePhoneCompany.Views.{pageName}"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame>
The RegionManager.RegionName attached property is attached to the Frame control. In your past Prism projects you probably added this attached property to a ContentControl, ItemsControl or TabControl. The assembly provides the region adapter and behavior which enables the Prism Region to be attached to the Frame control.
The included FrameContentLoader is used in the above XAML snippet. This content loader is a replacement for the default Silverlight ContentLoader. Notice the required RegionName property on the FrameContentLoader that matches the RegionName in the Frame RegionManager.RegionName attached property.
The assembly classes route all Region Navigation API requests to the Frame. This was done so that all navigation requests, regardless of origin, are processed uniformly by the Frame. This also ensures that the Frame's UriMapper can handle mapping user friendly Uri's to application MappedUri's.
A glance at the above UriMappings shows that this scheme supports multiple modules that can use the same UriMappings.
By default, the Region Navigation API's use an object's short type name to look it up in the container for creation and when iterating a region's contents to determine if the object can handle the navigation request.
I have elected to use full type names to identify objects in the container for navigation purposes. I also did this in the Prism v4 Region Navigation Pipeline demo application.
The ThePhoneCompany.Infrastructure.FullTypeNameRegionNavigationContentLoader class is used to change the default strategy from short type names to full type names, for determining which objects in the region are candidates to be checked if they are the navigation target. This the same class used in the Prism v4 Region Navigation Pipeline demo application.
User friendly Uri's are not only used in the address bar, but also by the application RequestNavigate method calls. The below code snippet shows the CategoryViewModel CloseExecute method, calling RequestNavigate from code. The InventoryHomeView string constant keeps the magic strings out of the code.
public const String InventoryHomeView = "/Inventory/InventoryView"; void CloseExecute() { _keepAlive = false; _regionManager.RequestNavigate(RegionNames.MainContentRegion, Constants.InventoryHomeView); }
Using friendly Uri’s also allows HyperlinkButtons to initiate navigation without additional code other than setting the NavigateUri property in XAML. In the below XAML snippet from the InventoryView.xaml ListBox DataTemplate. You can see how simple it is to data bind the ItemID property to set the NavigateUri property.
<DataTemplate> <Grid Loaded="lbDataItemsGridItemTemplate_Loaded"> ... <HyperlinkButton Grid.Column="3" Margin="7,0,0,0" Content="edit" NavigateUri="{Binding Path=ItemID, StringFormat=/Inventory/ItemView/\{0\}}" /> </Grid> </DataTemplate>
In-fact all but two navigation requests are initiated in XAML and not code. The two RequestNavigate method calls are in the ItemViewModel and CategoryViewModel CloseExecute methods.
Errors that are thrown during navigation requests will be bubbled up to the Frame and the Frame will raise the NavigationFailed event.
In addition to address bar deep linking and the Frame handling navigation errors, navigating against a Frame from XAML is a very good benefit of this implementation.
Developers can modify the Prismv4FrameRegionNavigation assembly's classes to meet the specific needs of their applications.
Lessons Learned
Assembly Loading on Application Start Up
In Silverlight, Prism Modules can be created using one of the Visual Studio Silverlight Applications project templates or a Silverlight Class Library project template.
The reason you use one of the Silverlight Application project templates is for packing. When the Silverlight Application is compiled, it will be packed in its own XAP. Packaging the assembly in its own XAP provides the developer a number of options for when the XAP is actually downloaded and loaded into the Silverlight application domain. If your Silverlight application consists of several XAPs, your initial load time can be decreased, since you will only have to get the XAP that contains the shell download and display the shell.
The reason you use the Silverlight Class Library project template is to provide code separation and promote reuse across solutions. Another reason to use this project template type is to package the assembly with the main Silverlight application; in other words, this assembly will be part of the Silverlight application's XAP.
So, what does this have to do with start up?
The Navigation API's require that the container can create the target object. Meaning, if you are using MEF or an IOC container, the Navigation API will call into the container requesting the target object by name. If the container can't create the object, the container will throw and the navigation request will stop.
If your solution is packaged in several XAPs, and the target of the navigation is in a XAP that has not been downloaded or is in the process of downloading, the container will throw an exception when the target object is requested.
Now let's look at two real-world scenarios that could cause a problem.
Please note, these issues are not caused by using the Navigation API, but are a side-effect of the asynchronous downloading of assemblies and the timing of accessing those assemblies before they are loaded and ready for use.
- Deep linking – the user opens their browser; enters a deep link Url and presses enter. If your application is broken down into multiple XAPs and the target of the deep link is not in the Silverlight application XAP it is possible that the user will get an error because the target XAP has not yet been downloaded.
- Main application attempts to navigate to a target that has not yet been loaded – this can happen even if your application is not navigating using a deep link. The main application has loaded and contains a button that navigates to class in module that is still loading. If the user clicks that button, an error will occur because the target is still loading.
To solve these problems you can:
- Package your assemblies in the main Silverlight application XAP.
-
Intercept the navigation request, verify the assembly is loaded then proceed with the navigation request. If the assembly is not loaded or is in the process of loading, you can wait until the assembly loads, and then proceed with the request.
- MEF Catalogs have a Changed event that you can hook to determine the status of assembly loading.
Silverlight Applications and MEF
There is a known issue when referencing the Prism MefExtensions assembly from multiple Silverlight assemblies. Your main Silverlight application should reference the assembly and set Copy Local to True.
All other assemblies that reference the Prism MefExtensions assembly must set Copy Local to False as pictured below.
If you forget to change the Copy Local property value to False, an exception will be thrown during the bootstrapping process. However, the message will indicate that you need to set Copy Local to False.
Non-Linear Navigation Implementation
In addition to Region Navigation integration, this application implements Non-Linear Navigation features similar to the Prism v4 Region Navigation Pipeline demo application.
This application demonstrates using the metadata by surfacing it in four locations as pictured below.
The Application metadata property is used to aggregate the count of the views in the region from the Inventory module. See the top inventory-(3) button text. Each time the Frame is navigated the Navigated event is raised and this event handler is invoked.
void ContentFrame_Navigated(Object sender, System.Windows.Navigation.NavigationEventArgs e) { ... //Posting to the dispatcher because the remove item from region does not get executed before this gets called. //Delaying the execution by posting this, enables the Inventory Hyperlink button to have the correct count displayed. this.Dispatcher.BeginInvoke((Action)delegate { this.hlbInventory.Content = MakeLabelWithCountForApplicationSuite(Constants.Inventory, Constants.Inventory.ToLower()); }); }
The below method is called from the above code and returns a formatted string that will be used as the Hyperlink’s content.
String MakeLabelWithCountForApplicationSuite(String applicaitonName, String labelText) { Int32 count = 0; foreach (var item in this.RegionManager.Regions[RegionNames.MainContentRegion].Views) { var fwe = item as FrameworkElement; if (fwe != null) { var nonLinearNavigationObject = fwe.DataContext as INonLinearNavigationObject; if (nonLinearNavigationObject != null && nonLinearNavigationObject.Application == applicaitonName) { count += 1; } } } if (count == 0) return labelText; else return String.Format("{0}-({1})", labelText, count); }
The InventoryViewModel creates the text for two inventory function buttons, Item-(1) and Category-(2), see in the above screen shot. This is accomplished by matching the types in the region with the type that each of the button provides selection for.
String MakeLabelWithCountForApplicationView(Type viewType, String labelText) { Int32 count = 0; foreach(var item in _regionManager.Regions[RegionNames.MainContentRegion].Views) { if(item.GetType() == viewType) { count += 1; } } if(count == 0) return labelText; else return String.Format("{0}-({1})", labelText, count); }
The open items for the selected inventory function are listed in the right side ListBox. This information comes from the region views metadata. The below code snippet checks which inventory function is active, then iterates over the views in the region looking for matching types; when found adds the metadata to the list for display in the right side ListBox.
void UpdateActiveDataItems() { try { if(ItemIsChecked) ActiveDataItems.Source = this.GetActiveDataItems(typeof(ItemView)); else if(CategoryIsChecked) ActiveDataItems.Source = this.GetActiveDataItems(typeof(CategoryView)); } catch(Exception) { } } IList<NonLinearNavigationMetadata> GetActiveDataItems(Type viewType) { var list = new List<NonLinearNavigationMetadata>(); foreach(var item in _regionManager.Regions[RegionNames.MainContentRegion].Views) { if(item.GetType() == viewType) { var fwe = item as FrameworkElement; if(fwe != null) { var nonLinearNavigationObject = fwe.DataContext as INonLinearNavigationObject; if(nonLinearNavigationObject != null) { list.Add(new NonLinearNavigationMetadata(nonLinearNavigationObject)); } } } } return list; }
As you can see, metadata is at the heart of implementing Non-Linear Navigation. Surfacing the metadata in creative ways makes it very easy for end users to use your software applications.
Video
The demo application video can be viewed here.
Note: You can view the high resolution version of the .wmv video file by:
- Clicking on the video link
- Log into Vimeo
After logging in, you'll be given access to the high resolution video download. There is no cost associated with this.
Download and Video
The link to download the demo application is located at the bottom of this article.
Requirements: you must download Prism v4 Drop 9 or later and run the RegisterPrismBinaries.bat batch file. The Prism v4 Readme covers this file in detail. If you do not want to run this batch file, you'll need to remove and re-add the references to the Prism assemblies.
Comments
Microsoft values your opinion about our products, guidance, documentation and samples.
Thank you for your feedback and have a great day,
Karl Shifflett
Patterns & Practices Prism Team
Getting Started with jQuery Templates and SharePoint 2010
[SharePoint] (Jan Tielens' Bloggings)Yesterday evening Scott Guthrie announced that Microsoft’s contributions to the jQuery Javascript library were accepted as Official jQuery plugins. One of those contributions is the jQuery Template plugin that allows you to do (up to a certain level) something like data binding similar to the approach we know from Silverlight. The idea is to create a template (think HTML snippet with elements bound to data properties) and data bind that template with an array of objects. You can find the API d ...
Yesterday evening Scott Guthrie announced that Microsoft’s contributions to the jQuery Javascript library were accepted as Official jQuery plugins. One of those contributions is the jQuery Template plugin that allows you to do (up to a certain level) something like data binding similar to the approach we know from Silverlight. The idea is to create a template (think HTML snippet with elements bound to data properties) and data bind that template with an array of objects. You can find the API documentation over here, or you can check out Boris Moore’s excellent Getting Started guide.
So how can we leverage this in SharePoint 2010? Well jQuery Templates are a great match for either the SharePoint 2010 ECMAScript/Javascript Client Object Model, or the SharePoint 2010 REST API. The following example shows how to quickly display a list of Tasks on a page, by only using jQuery and the REST API in SharePoint 2010. Let’s start with a very basic Site Page that puts an empty ul element in the PlaceHolderMain Content control (notice the ul element gets a specific id set):
<%@ Page MasterPageFile="~masterurl/default.master" %>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
<ul id="tasksUL"></ul>
</asp:Content>
Next, we need to put some script references in the PlaceHolderAdditionalPageHead Content control to load both the jQuery library and the jQuery Templates plugin. For now this plugin is a separate .js file, in the next release of jQuery this plugin becomes a part of jQuery itself. (The demo code assumes that both .js files are deployed to the same location as the Site Page.)
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script src="jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="jquery.tmpl.js" type="text/javascript"></script>
</asp:Content>
After the script references (also in the PlaceHolderAdditionalPageHead Content control), we need to define our template, also using a script element that has the type attribute set to text/x-jquery-xml. This template is just an HTML snippet that gets created for every object of the array that gets “data bound”. In the template it’s possible to use references to those object’s properties by using template tags. For example ${Title} will get the value of the Title property of the object that gets “data bound”. In the sample below, I’m defining a template that’s basically an li element which displays some properties of a SharePoint Task item. Notice that to display the Description of the Task, I’m using the {{html}} Template Tag, because Description is a Rich Text Field thus it contains HTML itself.
<script id="tasktemplate" type="text/x-jquery-tmpl">
<li>
<b class="ms-rteFontSize-4">${Title}</b> - ${StatusValue}
<div>{{html Description}}</div>
</li>
</script>
Finally, in a new normal script element (once again in the PlaceHolderAdditionalPageHead Content control), we can use jQuery’s getJSON function to make an async call to the REST API. The getJSON function takes as a parameter a URL to call, which will be the URL of the listdata.svc (= REST API end point) that fetches all Tasks that are not yet completed. The last parameter of the getJSON function is a callback function that will use the jQuery Templates mechanism to build a new li element for every retrieved Task item, based on the template we defined above. This is accomplished by using the new tmpl function, called on the template. The return value is an array of DOM elements, which are added to the empty ul element, defined above.
<script type="text/javascript">
$(document).ready(function () {
$.getJSON(
"../_vti_bin/listdata.svc/Tasks?$filter=StatusValue ne 'Completed'", null,
function (data) {
$("#tasktemplate").tmpl(data.d.results).appendTo("#tasksUL");
});
});
</script>
Once the Site Page is deployed, the result will look as follows:
If you’re interested, I’ve created a (slightly enhanced) sample Visual Studio project that’s a Sandboxed Solution (download here, including source code). This project provisions the required .js files, and the Site Page. On the demo Site Page I’ve used the following template:
<script id="tasktemplate" type="text/x-jquery-tmpl">
<li>
<b class="ms-rteFontSize-4" style="cursor:pointer;">
${Title}</b> - ${StatusValue}
<div style="display:none">{{html Description}}</div>
</li>
</script>
Notice that the div element that shows the Description value, is using the style display:none; so initially the Description value is not being displayed. In the callback function, after the li items are built, I’m using jQuery’s toggle function to show and hide the Description div when the corresponding Title is clicked.
$("#tasktemplate").tmpl(data.d.results).appendTo("#tasksUL");
$("#tasksUL li").toggle(
function () {
$(">div", $(this)).show("fast");
},
function () {
$(">div", $(this)).hide("fast");
}
);
When the Sandboxed Solution is deployed and activated, just navigate to the demo page using the URL http://yoursite/TemplateDemoAssets/demopage.aspx. You’ll see a nicely animated list of Tasks that are not yet completed!
Javascript Libraries and ASP.NET: A Guide to jQuery, AJAX and Microsoft
[Programming] (Articles, Opinions & Lab - MIX Online)When Microsoft announced they would begin providing official support for jQuery, few of us realized how profoundly that announcement would eventually impact client-side development on the ASP.NET platform. Since that announcement, using jQuery with ASP.NET has moved from the obscure, to a central role in ASP.NET MVC’s client-side story, and now to the point of potentially superseding ASP.NET AJAX itself. The journey hasn’t been all smooth. With Microsoft’s move toward jQuery, the ASP.NET ...
When Microsoft announced they would begin providing official support for jQuery, few of us realized how profoundly that announcement would eventually impact client-side development on the ASP.NET platform. Since that announcement, using jQuery with ASP.NET has moved from the obscure, to a central role in ASP.NET MVC’s client-side story, and now to the point of potentially superseding ASP.NET AJAX itself.
The journey hasn’t been all smooth. With Microsoft’s move toward jQuery, the ASP.NET AJAX, Microsoft Ajax Library, ASP.NET Ajax Library and Ajax Control Toolkit roadmaps have been uncertain at times. This has made it difficult to keep track of which projects are still relevant, and especially which you should choose going forward.
In my last article for Mix Online, I discussed what ASP.NET needed to know about jQuery from development perspective. In this article, I want to provide clarity on the events that led us to this point, talk about what portions of the current AJAX framework are and aren’t affected by recent changes and show you where we’re headed next. In addition, I’ll dive into the implications of the recent announcement about the adoption of Microsoft’s template library by the jQuery core.
How We Got Here
To fully grasp the current state of affairs, it helps to understand how we got here—especially in light of the recent ambiguity surrounding several of Microsoft’s similarly-named libraries. That story begins in 2005-2007, as Microsoft previewed and then released their first AJAX framework for the ASP.NET platform.
ASP.NET AJAX
Originally developed under the codename Atlas, ASP.NET AJAX was Microsoft’s first bid to provide ASP.NET developers with officially-supported AJAX functionality. The ASP.NET AJAX extensions initially shipped as an out-of-band extension to ASP.NET 2.0, but its popularity led to its inclusion as a first-class component of ASP.NET 3.5 and subsequent releases.
ASP.NET AJAX’s popularity stemmed from its server-side controls such as the ScriptManager, UpdatePanel and Timer, which made it unnecessary to learn or use JavaScript. Unfortunately, the convenience those server-side controls offered came at the expense of performance and efficiency. Though a server-driven approach remains in use even in ASP.NET 4, its popularity has waned.
One of ASP.NET AJAX’s more enduring features was that it augmented ASMX services with the ability to communicate in raw JSON format. Many of the jQuery-centric techniques to avoid ASP.NET AJAX actually still leverage ASP.NET AJAX’s System.Web.Extensions features on the server-side, using features such as ASMX ScriptServices, Page Methods and the JavaScriptSerializer class.
ASP.NET AJAX is also notable because it gave us Microsoft’s first full client-side framework— MicrosoftAjax.js. MicrosoftAjax.js not only underpinned ASP.NET AJAX’s server-side abstractions, but also provided a cross-browser JavaScript API for tasks including event handling and basic DOM manipulation. Even when burdened by the code necessary to support the UpdatePanel, MicrosoftAjax.js weighed in at only 24kb when minified and zipped.
The Ajax Control Toolkit
Shortly after the first version of ASP.NET AJAX shipped, a companion product named the Ajax Control Toolkit was released on CodePlex as an open source project. The Ajax Control Toolkit (ACT) is a collection of special server controls built on top of ASP.NET AJAX’s client-side MicrosoftAjax.js framework.
At the toolkit’s inception, its popular controls included the CascadingDropdown, AutoComplete and ModalPopup extenders. These controls were primarily abstractions around embedded JavaScript to achieve rich DHTML functionality without client-side programming. In fact, some of the controls, such as TextBoxWatermark and MaskedEdit, were purely client-side effects, wrapped in a server control for ease of use.
Unfortunately, what this server-centric approach offered in ease of use was often negated by inefficiency. It was easy to end up with dozens of HTTP requests for embedded JavaScript, image and CSS resources. Worse, it wasn’t entirely obvious where the culprit was, since those embedded resources were silently injected on the control’s behalf, not explicitly added to the page by the developer.
Though the Ajax Control Toolkit still enjoys significant popularity (as I’m writing this, it remains one of the top three most downloaded CodePlex projects in the last week), its uptake in new projects is beginning to decline in favor of a more client-side approach that uses jQuery and its plugins.
The Microsoft Ajax Library
By early 2008, the ASP.NET team was already at work on a successor to ASP.NET AJAX’s client-side component: The Microsoft Ajax Library. It was an upgraded replacement for MicrosoftAjax.js that provided the previous framework’s baseline features, as well as a range of expanded functionality.
Over the course of six preview releases and a beta, The Microsoft Ajax Library rolled out advanced features including:
- Script Management – Handling interdependencies between scripts, asynchronously loading them on demand and reliably raising events when they’ve been loaded is a tedious problem to solve. It’s worth the effort though. Non-blocking, asynchronous script loading can result in a marked improvement in how quickly pages load.
- Templating – The DataView component provided a robust client-side templating solution, similar to what a Repeater or ListView could accomplish on the server-side. As development transitions to the client-side, the problem of transforming JSON data sources in to HTML markup becomes a common thorn in our sides. The DataView was a solid solution to that problem.
- Data Integration – One of the library’s most powerful ASP.NET-specific features was the DataContext. Similar to its namesake on the server-side, the DataContext made it extremely easy to work with data and included support for Data endpoints and client-side change tracking and submission.
- Componentized – The Microsoft Ajax Library was eventually refactored into about a dozen loosely coupled scripts, divided roughly to a component (e.g. DataView or DataContext) per script. The script dependency management abstracted this away so you could pick and choose features to use and rely on the framework to asynchronously load the smallest subset of scripts necessary to support those features.
One notable thing about those new features is that they didn’t overlap with what jQuery core provided at the time. Rather than reinventing a selector engine, DOM manipulation library or animation framework to compete with jQuery, this library’s focus was truly on complementing jQuery.
That shift in approach was an important one. ASP.NET developers using jQuery as the foundation for their client-side development could continue doing that, while selectively taking advantage of the Microsoft Ajax Library’s improvements. The library’s componentized approach and script dependency management made it easy and straightforward to load the minimal set of script to provide exactly the functionality you desired.
The CodePlex Foundation
Since JavaScript frameworks must be distributed as source code anyway, they’re natural candidates for open source projects. In that spirit, the ASP.NET team decided to embrace open source and contributed the Microsoft Ajax Library to the recently-formed CodePlex Foundation in early 2010.
To better fit the ethos of open source and avoid any misinterpretation of Microsoft’s role in the CodePlex Foundation, the decision was made to remove “Microsoft” from the library’s name. Instead, it was renamed more neutrally as the ASP.NET Ajax Library.
At that point, the library still had no dependency on jQuery. However, many of the library’s components were refactored so they could be instantiated as jQuery plugins instead of requiring the older create or Sys.create syntaxes. Additionally, the library’s Sys.get method supported passing advanced selector queries through to jQuery if it was present.
New Directions – Microsoft <3 jQuery
During the second day’s keynote at MIX10 this year, Microsoft put an abrupt shift in thinking on display. Instead of demonstrating the ASP.NET Ajax Library, Microsoft gave us the first public glimpse of an entirely new approach: a jQuery plugin called jQuery Templating.
Even for those of us who follow Microsoft’s client-side development story, this year’s events came as quite a surprise. Particularly, the lack of an officially sanctioned new AJAX technology to accompany ASP.NET 4’s release left most of us scratching our heads. For nearly two years, we generally expected that the ASP.NET Ajax Library would ship with ASP.NET 4, but nothing new shipped at all!
Even more surprising, the nascent ASP.NET Ajax Library not only remained unreleased, but was abandoned completely. If anyone had still wondered whether or not Microsoft was truly committed to jQuery, it was clear at that point that they were “all in.”
Microsoft gives back to jQuery
The jQuery Templating plugin showcased at MIX10 was only the beginning. Since then, Microsoft has committed development, testing, documentation and other resources toward helping improve jQuery. These efforts include both contributing patches to jQuery core and developing new plugins.
Additionally, Microsoft continues to offer its technical support services to developers using jQuery. While the expansive jQuery community provides all the support most of us will ever need via blogs, message boards, and tutorial sites, having SLA-backed phone support is an important factor in the decision process at some organizations.
Even though the collaboration between Microsoft and the jQuery project has taken several months to hit its stride, the new relationship has begun producing tangible results. Not only has Microsoft refined the jQuery Templates plugin to the point that it’s a great client-side templating solution in its own right, but their implementation of jQuery Templates is now slated for inclusion into jQuery 1.5.
Where we stand now and a look at the future
Microsoft’s changes in direction this year are potentially great ones, and we’ve only seen the first glimpse of what they have in store for client-side development on the ASP.NET platform. However, it hasn’t been easy to keep track of the shifting roadmap, exactly which technologies are impacted by the changes, and which approaches to bet on going forward.
The status of existing Microsoft projects
First, this is what you need to know about the status of Microsoft’s existing client-side development products:
- ASP.NET AJAX – Both the server-side and client-side portions of ASP.NET AJAX ship with ASP.NET itself and are fully supported. Though the UpdatePanel’s server-side flavor of AJAX has largely fallen out of favor, it will continue to be a viable approach and be supported for the foreseeable future. There has been some confusion about the fate of ASP.NET AJAX, due to the similarity of its name and the ASP.NET Ajax Library’s, but their fates are in no way intertwined at this point.
- The Ajax Control Toolkit – The venerable Ajax Control Toolkit is still available on CodePlex and continues to receive occasional bug fixes, but activity on the project has been sparse in recent month. As was quietly showcased toward the end of the ASP.NET Ajax Library’s lifespan, it’s possible that most of the Ajax Control Toolkit’s DHTML controls could be re-implemented as jQuery plugins instead of their current assembly-based packaging. With so much of its functionality already present in jQuery and jQuery UI, the only certainty is that its future is uncertain.
- Microsoft Ajax Library / ASP.NET Ajax Library – Though this library was stealthily “released” within the latest versions of the AJAX Control Toolkit distributions, it is deprecated, obsolete, is not supported by Microsoft in any form. It will receive no further development or bug fixes, and should not be used going forward.
The most important takeaway here is to differentiate between ASP.NET AJAX and the ASP.NET Ajax Library. They are named so similarly that it’s easy to be confused about their unrelated roadmaps. If you’re using the ScriptManager or UpdatePanel, you’re in the clear in terms of future support. If you’re using a DataView or Sys.require, you’re using the now-obsolete ASP.NET Ajax Library and should consider a jQuery-based alternative.
jQuery development to watch
Though it’s clear that jQuery is the future of client-side development on the ASP.NET platform, there hasn’t been much guidance about where to start and what to learn. This story is still unfolding, but these are a few of Microsoft’s new jQuery efforts that you can watch to stay abreast of what’s to come:
- Templating – Previously referred to as jQuery.tmpl or jQuery-tmpl, the jQuery Templating feature was Microsoft’s first foray into working with the jQuery team and community. Though its inclusion in jQuery isn’t planned until jQuery 1.5 is released, you can begin using it in plugin form immediately. That plugin is currently available on GitHub: http://github.com/jquery/jquery-tmpl
- Data Linking – The next feature that may be a precursor to more official things to come is the jQuery-datalink plugin, which is also available on GitHub: http://github.com/nje/jquery-datalink. With this plugin, you can “link” JavaScript objects together so that they remain synchronized when changes are made to one or both of them. The canonical example of this is linking a JavaScript object’s properties to corresponding fields in a form, to eventually automate tasks such as change tracking and submission.
- Globalization – The third jQuery plugin that Microsoft is working on is one called jQuery-glob. Though it will almost certainly not find its way into jQuery core, I believe it will become one of many “official” ASP.NET jQuery plugins in the future.
While these three plugins are the only ones that Microsoft is developing in public right now, they are just the tip of the iceberg. It’s likely that we’ll soon see Microsoft develop jQuery plugins to re-implement the Ajax Library’s DataContext, provide easier access to ASP.NET-specific data endpoints, and replicate the functionality of the Ajax Control Tookit.
To keep up with Microsoft’s work on these official jQuery plugins and any new ones that may emerge, I suggest keeping an eye on .NET jQuery Extensions at GitHub. In addition, several members of the Microsoft team have blogs with great tutorials and announcements regarding the team’s ongoing work with jQuery:
Finally, you can also watch my blog (http://encosia.com) or follow my Twitter updates (@Encosia) for more information like this article and hands-on examples of using these new jQuery features with ASP.NET.
Blog Post: Pivoting ASP.NET event log error messages
[Microsoft Office] (Site Home)Unless you’ve been hiding under the proverbial rock, you’ve probably seen the recent Pivot hoopla. If you’re not familiar with it, it’s a way to visualize a large amount of data in a nice filterable format. The nice thing about it is that it’s really easy to put together a pivot collection and there are a ton of tools available for just this purpose. Just do a search on CodePlex for Pivot and you’ll get about 40’ish good results for tools you can use to create a Pivot Coll ...
Unless you’ve been hiding under the proverbial rock, you’ve probably seen the recent Pivot hoopla. If you’re not familiar with it, it’s a way to visualize a large amount of data in a nice filterable format. The nice thing about it is that it’s really easy to put together a pivot collection and there are a ton of tools available for just this purpose. Just do a search on CodePlex for Pivot and you’ll get about 40’ish good results for tools you can use to create a Pivot Collection.
So, I was putting together a proof-of-concept for an internal project and thought I would continue on with my series of blog posts on ASP.NET Error Message event logs with a post on how to visualize this data using a pivot. You may wish to read parts 1 and 2 here:
- Part 1: Parsing ASP.NET event log error messages for fun and profit
- Part 2: Don’t guess when it comes to performance…
So, when I put together my pivot, I worked out a 3 step process:
- Figure out what you want to Pivot
- Find an API and convert the data
- Generate and Test the collection
Let’s begin, shall we.
Figure out what you want to Pivot
The structure for the Pivot Collection is deceptively simple -
<?xml version="1.0"?>
<Collection Name="Hello World Collection" …>
<FacetCategories>
<FacetCategory Name="Hello World Facet Category One" Type="String"/>
</FacetCategories>
<Items ImgBase="helloworld.dzc">
<Item Img="#0" Id="0" Href="http://www.getpivot.com" Name="Hello World!">
<Description> This is the only item in the collection.</Description>
<Facets>
<Facet Name="Hello World Facet Category One">
<String Value="Hello World Facet Value"/>
</Facet>
</Facets>
</Item>
</Items>
</Collection>
The way that I think about the Items in the Collection are in the same way that you might think about an object. For example, a Car object might have the following properties:
- Advertising blurb
- Car and Driver Reviews
- Color
- Make
- Model
- Engine
- 0-60mph time
- Max Speed
The common values like the Color, Make, Model, 0-60mph time and max speed become the facets or attributes that describe your object in relation to other instances of objects. Things like the advertising blurbs and car and driver reviews or descriptions belong instead as properties of your Item directly in the Description element.
For our data, namely ASP.NET exceptions, we’re going to define an exception as the following:
- Item
- Name = Exception Type
- Description = Exception Message
- Facets
- Request Path
- Stack Trace
- Event Time
- Top Method of Stack Trace
- Top My Code Method of Stack Trace
This should allow us to group and drill through the common properties that might link exceptions together and still provide detailed error information when necessary.
Find an API and code it
The second step here is to find some code/API/tool that we can enhance for our purposes. There are some great tools published by the Live Labs team – for example:
While both tools could be used in this instance, in part 2 we found that some of our Event Logs we were parsing contained more than 10,000 items and I wanted a bit more control over how I converted the data. “No touching” is the phrase of the day. Fortunately, the command line tool was published on CodePlex with an API we can use. Once you download the product you see that it contains 3 assemblies:
The last item there is the PauthorLib.dll which encapsulates many of the extension points within this great tool. In-fact, it exposes about 7 different namespaces for our purposes:
For our purposes, we are going to focus on the Streaming set of namespaces. Why? Well, this is namely because we are going to be dealing with a lot of data and I didn’t want to load everything into memory before writing it to disk. If you look at the contents of the Streaming namespace, you’ll see a great class called “AbstractCollectionSource”. This looks fairly promising because it exposes two main methods:
class EventLogExceptionCollectionSource : AbstractCollectionSource
{
protected override void LoadHeaderData()
{
throw new NotImplementedException();
}
protected override IEnumerable<PivotItem> LoadItems()
{
throw new NotImplementedException();
}
}
Before we do anything, though, we need a constructor. The constructor will be responsible for taking a string representing the path to our data and passing it to our base class’s constructor.
public EventLogExceptionCollectionSource(string filePath)Then, the first method, LoadHeaderData, is where we define our facets – Request Path, Stack Trace, etc. - as well as the data types that each facet will be. So, our code will be fairly simple and straight-forward:
: base(filePath)
{
// Do nothing else.
}
protected override void LoadHeaderData()
{
this.CachedCollectionData.FacetCategories.Add(
new PivotFacetCategory(STACKTRACE,
PivotFacetType.LongString));
this.CachedCollectionData.FacetCategories.Add(
new PivotFacetCategory(REQUESTPATH,
PivotFacetType.String));
this.CachedCollectionData.FacetCategories.Add(
new PivotFacetCategory(EVENTTIME,
PivotFacetType.DateTime));
this.CachedCollectionData.FacetCategories.Add(
new PivotFacetCategory(TOPMETHOD,
PivotFacetType.String));
this.CachedCollectionData.FacetCategories.Add(
new PivotFacetCategory(TOPAPPMETHOD,
PivotFacetType.String));
this.CachedCollectionData.Name = "Event Log Error Messages";
}
The second method, LoadItems(), is responsible for doing exactly what it suggests – this is where we load the data from whichever source we care about and then convert it into our PivotItem collection. For our purposes, we’re going to load the XML file we defined in Part 1 of this series into a list of EventLogMessage objects and then convert those EventLogMessage objects into PivotItem objects:
protected override IEnumerable<PivotItem> LoadItems()
{
// Load XML file
XDocument document = XDocument.Load(this.BasePath);
// Populate collection of EventLogMessage objects
var messages = from message in document.Descendants("Message")
select EventLogMessage.Load(message.Value);
int index = 0;
foreach (EventLogMessage message in messages)
{
PivotItem item = new PivotItem(index.ToString(), this);
item.Name = message.Exceptiontype;
item.Description = message.Exceptionmessage;
item.AddFacetValues(REQUESTPATH, message.Requestpath);
item.AddFacetValues(STACKTRACE, message.Stacktrace);
item.AddFacetValues(EVENTTIME, message.Eventtime);
item.AddFacetValues(TOPMETHOD, message.StackTraceFrames[0].Method);
item.AddFacetValues(TOPAPPMETHOD, GetFirstNonMicrosoftMethod(message.StackTraceFrames));
index++;
yield return item;
}
}
The key method calls from above are the AddFacetValues(…) method calls. This method essentially sets the attributes we wish to have our data pivot upon. This, by itself, isn’t enough to generate our great pivot calls – we need to call our code from somewhere. Since this is a simple app, we’re going to make it a console app. For our Collection to get generated we need to use a few other objects included in this API:
- EventLogExceptionCollectionSource – The class we created above.
- HtmlImageCreationSourceFilter – This class will generate the tile in the Pivot based upon some HTML template we specify.
- LocalCxmlCollectionTarget – Generates the Collection XML file at the path we specify.
- DeepZoomTargetFilter – Generates the deep zoom files to support our collection XML file and also enables all of our fancy transitions.
In practice, the code is pretty simple and straight forward and I applaud the people who wrote this library:
private static void GenerateExceptionPivot(string inputFile, string outputFolder)
{
string collectionName = Path.Combine(outputFolder, "MyExceptions.cxml");
EventLogExceptionCollectionSource source =
new EventLogExceptionCollectionSource(inputFile);
HtmlImageCreationSourceFilter sourceFilter1 =
new HtmlImageCreationSourceFilter(source);
sourceFilter1.HtmlTemplate =
"<html><body><h1>{name}</h1>{description}</body></html>";
sourceFilter1.Width = 600;
sourceFilter1.Height = 600;
LocalCxmlCollectionTarget target =
new LocalCxmlCollectionTarget(collectionName);
DeepZoomTargetFilter targetFilter1 =
new DeepZoomTargetFilter(target);
targetFilter1.Write(sourceFilter1);
}
That last statement, targetFilter1.Write(…) is what will actually execute everything and write our resultant files to disk.
Generate and Test the collection
So, now if we run our console application and call that GenerateExceptionPivot(…) method, we’ll get some great output.
What’s nice about the Library is that it provides progress as it iterates through your data (in the red rectangle) and also in the blue rectangle, we need that it’s multi-threaded by default. This is primarily for the most intensive part of the operation – the creation of the deep zoom artifacts. If you have one of those new fangled machines with 2+ cores, you can tweak the number of threads that it will spawn for this operation by setting the ThreadCount property of the DeepZoomTargetFilter object. This may or may not improve your performance but it’s nice that the option is available.
...
DeepZoomTargetFilter targetFilter1 =
new DeepZoomTargetFilter(target);
targetFilter1.ThreadCount = 100;
targetFilter1.Write(sourceFilter1);
...
Once our collection has been generated, we can browse it in an explorer.exe window just to get an idea of what our code has wrought:
And then to test it, you can just point the Live Labs Pivot application at our “MyExceptions.cxml” file and view the wonderful data. For example, you can look at the Event Time in the histogram view to see how your exceptions broke down over time. You can also filter your data by things like the RequestPath (the page that threw the exception) or the Method that was at the top of the callstack.
Then, you can zoom in on a specific time slice you care about:
Then, if you want to view the details for a specific instance, just click the corresponding tile. Then, a new side bar will appear on the right hand side with all of the details we stored in this record:
We generated a single collection in this blog post. One thing to keep in mind is that each collection should have no more than 3,000 items. For collections in which you want to have more than 3,000 items, you should look at potentially creating a Linked Collection. That will be the subject of an upcoming blog post.
Until next time!
something like crystal report or active reports for php?
[IT] (DaniWeb IT Discussion Community)hi, i looking for a design report like crystal report or active reports but for php the objective is creat a report using mysql data and save the template and load that report in a pdf file for print or save any one know something like that? i already use classes like class.ezpdf.php but i ...
VooSky Business & Portfolio- 8 in 1 WordpressTheme (Business)
[WordPress] (ThemeForest new items)VooSky Theme VooSky comes with awesome featured post slider, Two home page layouts, 8 colors, fully working contact form, Advance theme /post and page option panels, portfolio page(multi page or single page) , Dropdown navigation with infinite dropdown levels and many other MAIN FEATURES Easy to Customize. 8 colors styles to choose with default color style. Two home page Layouts. Advance theme option panel. Page setting panel. Post settin ...
VooSky Theme
VooSky comes with awesome featured post slider, Two home page layouts, 8 colors, fully working contact form, Advance theme /post and page option panels, portfolio page(multi page or single page) , Dropdown navigation with infinite dropdown levels and many other…
MAIN FEATURES
- Easy to Customize.
- 8 colors styles to choose with default color style.
- Two home page Layouts.
- Advance theme option panel.
- Page setting panel.
- Post setting panel.
- JQuery improved, degrades gracefully when JavaScript is turned off.
- Drop down menus enabled.
- Three dynamic sidebars enabled.
- Home page sidebar
- Normal page sidebar and
- Contact page sidebar
- Ready for many useful plug-ins. (Recommended plug-ins list…)
- Styled for all widgets comes with wordpress.
- Advance portfolio gallery page.
- Single page portfolio or
- Multi page portfolio
- Possible to having any post or page in the portfolio page with little efforts.
- Featured Posts Slider in home page.
- Possible to having any post or page in Featured Posts Slider.
- More control over slides
- Slider almost dynamic
- Functional Ajax contact form
- Designed for further reduce page load times.
- Lightweight JavaScript files and CSS and optimized images.
- Cufon font replacement technology for better Visibility (no effects on SEO ).
- Free fonts used & included.
- PSD source Files included & Fully editable
- Tested with various different test data for possible occurrences.
- Styled for deep threaded comments.
- Gravatar ready
- TimThumb image resizing ( Thanks to darrenhoyt )
- RSS feeds ready.
- Unique page template design.
- extensive Documentation File included
- Styled:
- 404 page
- Archive page
- Archive Complete page
- Author page
- Blog page
Credits
I would like to say a little thank you for people.
- John Resig and Company for JQuery
- Darren Hoyt for TimThumb
- Arro for Vegur font
- Rafael Lima for CSS Browser Selector
- Ivan Lazarevic for Coin Slider
- Janis Skarnelis for FancyBox
- Stefan Benicke jquery-foxiblob




