Changes to Custom IndexProviders Implementation in Sitefinity 3.6 SP1

In case you are using some of the Custom Index Providers I blogged about previously, you may encounter some errors when upgrading to the new Sitefinity 3.6 SP1 that was released this week.

The problem is that the IIndexerInfo interface how requires a new method, ResolveIndexPath(). Since the Sitefinity team is still catching up on documentation, I didn’t know what exactly this is used for. Fortunately, with the use of Reflector, it turns out that this simply returns the Path property.

public string ResolveIndexPath()
{
    return Path;
}

Actually, this method is also supposed to take into account culture information, modifying the link if necessary. However, if you do not require this, and I don’t, then you are safe with just returning the path.

Now with this change, your index providers should compile correctly, and successfully index your site. I hope this was helpful! As always, comments are welcome and appreciated.

Ajax Conflicts Between .net 2.0 and 3.5

This post is for anyone who is encountering the following errors in their asp.net applications:

  • The server tag ‘asp:ScriptManager’ is ambiguous. Please modify the associated registration that is causing ambiguity and pick a new tag prefix.
  • The base class includes the field ‘ScriptManager1’, but its type (System.Web.UI.ScriptManager) is not compatible with the type of control (System.Web.UI.ScriptManager).

There are many instances where you might exprience this problem. For me, the situation was that we had a virtual directory running asp.net 2.0 with AJAX extensions, while the root site is running asp.net 3.5. The subdirectory was attempting to reference the old 1.0.61025.0 version of the Web.Extensions assembly. Since we upgraded our webserver to asp.net 3.5, this assembly should no longer be used.

So the solution was to upgrade all the Web.Extensions stuff in web.config from the 1.0 to the asp.net 3.5 version. First, upgrade the configSections from this:

<configuration>
<
configSections>
<
sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35
">
<
sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35
">
<
section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection,
System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
" requirePermission="false" allowDefinition="MachineToApplication"/>
<
sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup,
System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
">
<
section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection,
System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
" requirePermission="false" allowDefinition="Everywhere" />
<
section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection,
System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
" requirePermission="false" allowDefinition="MachineToApplication" />
<
section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection,
System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
" requirePermission="false" allowDefinition="MachineToApplication" />
</
sectionGroup>
</
sectionGroup>
</
sectionGroup>
</
configSections>
</
configuration>

to this:

<configSections>
<
sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<
sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<
section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<
sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<
section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
<
section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<
section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<
section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
</
sectionGroup>
</
sectionGroup>
</
sectionGroup>
</
configSections>

Next, in system.web update the controls, compilation, httpHandlers, and httpModules references:

<system.web>
<
pages>
<
controls>
<
add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</
controls>
</
pages>
<
compilation>
<
assemblies>
<
add assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</
assemblies>
</
compilation>
<
httpHandlers>
<
remove verb="*" path="*.asmx"/>
<
add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<
add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<
add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</
httpHandlers>
<
httpModules>
<
add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</
httpModules>
</
system.web>

to the new 3.5 versions

<pages>
<
controls>
<
add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<
add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</
controls>
</
pages>
<
assemblies>
<
add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</
assemblies>
<
httpHandlers>
<
remove verb="*" path="*.asmx"/>
<
add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<
add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<
add verb="GET,HEAD" path="ScriptResource.axd" validate="false" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</
httpHandlers>
<
httpModules>
<
add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</
httpModules>

Finally, there may be some scattered tag registrations on your pages that are still directly referencing the old version. Add a bindingRedirect to your web.config so that these references are updated to point to the correct assembly. You can add this section before or after (but not inside!) system.web

<runtime>
<
assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<
dependentAssembly>
<
assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<
bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</
dependentAssembly>
<
dependentAssembly>
<
assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<
bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</
dependentAssembly>
</
assemblyBinding>
</
runtime>

And that should do it! Our virtual directory now has no conflicts, and completely references the new asp.net 3.5 assemblies. I hope that this post was helpful, and if I left anything out, please let me know in the comments!

Inserting a Duplicate DataRow to a DataSet

While developing an online form, I came upon the need to insert two copies the results to a database, one for each of two options selected on the form. The only thing different between these entries would be this option, and of course, the autogenerated ID for the row’s primary key.

While I could simply call the Insert function of the TableAdapter twice, this method requires that each field be explicitly submitted as a parameter, and since the data is coming from textboxes, and there are about a dozen boxes on the page, that’s a whole lot of textbox.Text.Trim() arguments!

One alternative was to introduce a local variable for each textbox, assigning it the respective value, and only passing those locals as parameters, but being a HUGE fan of strong-typing, I would much rather use a class that maps these fields to a row, especially since the dataset I made for this Table already has one!

It was easy enough to instantiate the dataset, create the strongly-typed row and populate it with data and add it to the dataset:

var ds = new MyDataTable();

var app = ds.NewMyRow();
app.Option = OptionMenu1.SelectedValue;
app.foo = Foo.Text.Trim();
app.bar = Bar.Text.Trim();

Now I can add the first copy of the row to the datatable just fine. Unfortunately, since app is a reference variable, I can’t add it again, because it already belongs to the table. And even though the Add method will take an overload of the ItemArray, since it’s passing duplicate values, I’ll still get an error because the primary key (automatically assigned as -1) is being duplicated!

ds.Rows.Add(app);
ds.Rows.Add(app); // Throws Error, already in table!
ds.Rows.Add(app.ItemArray); // Throws Error, duplicate Primary Key! 

For whatever reason, DataRow does not include a Clone() method or any way to easily make a duplicate of the item. So the solution I worked out is to instead make a new instance of an object array from the DataRow.ItemArray property, setting the primary key (at index 0) to null before adding this new instance using the Add method. This forces the dataset to generate a new auto key so that the DataRow is unique. Then I can retrieve the reference to this new row (so I can have strongly-typed access to its properties) and change the option for this new row.

if (OptionMenu2.SelectedValue != "none")
{
    // duplicate row
var arr = app.ItemArray; arr[0] = null; ds.Rows.Add(arr); // update the option for the second row var app2 = ds.Rows[1] as MyRow; app2.Option = OptionMenu2.SelectedValue; } new MyTableAdapter().Update(ds);

Now all I have to do is call the update method on my table adapter and both rows are added from just one copy of the data. Not bad!