Request a topic or
contact an Arke consultant
404-812-3123
ASP.NET

Arke Systems Blog

Useful technical and business information straight from Arke.

About the author

Author Name is someone.
E-mail me Send mail

Recent comments

Archive

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012

Passing query to Custom Google Search on your webpage

Stackoverflow got me the answer to this.

If you're using Custom Google Search on your website and you have a small textbox on each page that is supposed to 'pass' that query to your search page (ultimately passed into your Google search post action), you should do the following: 

Instead of the code Google provided you with to embed, take your unique key provided by Google and place it in the '***my key***') setting of the first code snippet below.  What this is doing is creating a new instance of your custom google search with your unique key and drawing out the results (*cse*) and forcing it to submit the action (execute) with your parameters ($q). 

 

    google.load('search', '1', { language: 'en' });

    function OnLoad() {
        var customSearchControl = new google.search.CustomSearchControl('009045124056933145342:5wmgo53sugc');
        customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
        customSearchControl.draw('cse');
        customSearchControl.execute(search_value);
    }
    google.setOnLoadCallback(OnLoad);

 

On my click event for my search text box on every page, I sanitized the query (this is a very basic sanitize, you should alter as necessary to protect your site!) and appended those parameters to the query string being sent to my search results page.

private String SanitizeUserInput(String text)
        {
            if (String.IsNullOrEmpty(text))
                return String.Empty;

            String rxPattern = "<(?>\"[^\"]*\"|'[^']*'|[^'\">])*>";
            Regex rx = new Regex(rxPattern);
            String output = rx.Replace(text, String.Empty);

            return output;
        }

        protected void Button1_Click(object sender, ClickEventArgs e)
        {

            Response.Redirect(
                String.Format(
                Page.ResolveUrl("~/{MySearchPage}.aspx?q={0}"),
                    HttpUtility.UrlEncode(SanitizeUserInput({mysearchtextbox}.Text.Trim()))
                    ),
                false
                );
        }



On my search results page, I remove those parameters with the below function and pass them into my $q variable.

function getQuerystring(key, default_) {
if (default_ == null) default_ = "";
key = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regex = new RegExp("[\\?&]" + key + "=([^&#]*)");
var qs = regex.exec(window.location.href);
if (qs == null)
return default_;
else
return qs[1];
}


Posted by Nicole Rodriguez on Wednesday, August 24, 2011 9:36 PM
Permalink | Comments (0) | Post RSSRSS comment feed

DotNetNuke PurgeScheduleHistory

DotNetNuke runs a task to purge it’s schedule history; however, the stored procedure that does this has performance problems that will cause deadlocks on a high traffic website.

The query that the PurgeScheduleHistory stored procedure ships with is:

DELETE FROM dbo.ScheduleHistory
FROM dbo.Schedule s
WHERE (
  SELECT COUNT(*)
  FROM dbo.ScheduleHistory sh with (nolock)
  WHERE sh.ScheduleID = ScheduleHistory.ScheduleID
  AND sh.StartDate >= ScheduleHistory.StartDate
) > s.RetainHistoryNum
AND s.RetainHistoryNum <> -1
AND s.ScheduleID = ScheduleHistory.ScheduleID

Anytime you write a query that does a delete from a select you run the risk of deadlock.  Also, whenever you do a sweeping delete on a table you can escalate to a page lock, have trouble with locking indexes, and end up in a deadlock scenario again.  On the surface this doesn’t sound like a deadlock scenario, but you have to understand the way locks affect indexes and how locks escalate in SQL Server—you might be surprised to learn a simple SELECT query can deadlock under the right conditions also (http://stackoverflow.com/questions/661908/sql-server-deadlocks-between-select-update-or-multiple-selects).

The fix to this stored procedure follows the same approach Microsoft used to fix the DeleteExpiredSessions stored procedure that ships with ASP.NET (http://support.microsoft.com/kb/973849).

The following query should replace the PurgeScheduleHistory stored procedure:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[PurgeScheduleHistory]

AS

SET NOCOUNT ON
SET DEADLOCK_PRIORITY LOW

create table #T (ID int not null primary key)

insert into #T
select schedulehistoryid from (
select s.ScheduleID, sh.schedulehistoryid, rank() over (partition by s.scheduleid order by sh.startdate) rn, RetainHistoryNum
from ScheduleHistory sh WITH (READUNCOMMITTED)
join Schedule s WITH (READUNCOMMITTED) on s.ScheduleID = sh.ScheduleID
where s.RetainHistoryNum <> -1) a
where rn > RetainHistoryNum

DECLARE ESC CURSOR LOCAL FORWARD_ONLY READ_ONLY
FOR SELECT ID FROM #T

declare @ID int

open ESC

fetch next from ESC into @ID

WHILE @@FETCH_STATUS = 0
    BEGIN
        DELETE FROM ScheduleHistory WHERE ScheduleHistoryID = @ID
        FETCH NEXT FROM ESC INTO @ID
    END

CLOSE ESC

DEALLOCATE ESC

drop table #T

Thanks to David Eison for finding this solution.


Categories: SQL Server | ASP.NET | DotNetNuke
Posted by Eric Stoll on Wednesday, February 16, 2011 1:13 AM
Permalink | Comments (0) | Post RSSRSS comment feed

CRM and ViewState

Microsoft Dynamics CRM 4.0 doesn’t use ViewState, or Sessions.  Indeed, they are disabled in the web.config file.  This can be a bit of a surprise to an ASP.NET developer working on a custom page in the /ISV directory. 

Three possible solutions on ViewState:

1) Don’t use ViewState. 

This requires re-initializing any data with every postback.  One thing you can do is minimize postbacks.  You’ll see CRM does this in many places by encouraging the use of Javascript and popping extra windows to handle immediate-response things like filling in or validating a lookup field.  If you are displaying a grid, it will be empty after postback because it wasn’t re-populated from viewstate, so you need to repopulate it on every request – which means you also need to deal with the possibility that values may have changed due to another user editing records in the meantime.  Some simple strategies to start with are to refer to records by guids instead of by row numbers and to minimize updates to only the fields your user actually changed, and to code defensively.

2) You can enable viewstate for a particular page by adding it to the page directive at the top of the page:

<%@ Page . . . EnableViewState="true" . . .

Note that if you have a server cluster and use viewstate, you’ll either need to set a machine key in web.config or else disable viewstate validation with another Page directive:

<%@ Page . . . EnableViewState="true" EnableViewStateMac="false" %>

Disabling ViewStateMac means your users will be able to tamper with the viewstate, so just keep that concern in mind if you have custom permissioning rules in your app beyond CRM’s built in permissioning.

3) You can enable viewstate for all of your ISV pages by setting it up as its own app. 

Create a virtual directory under /ISV, point it to your pages, and give yourself a web.config that sets enableviewstate for your pages (and a machine key).  See, for example, this guide at xrmlinq.

Remember that if you’re going to use viewstate, you should keep an eye on viewstate size ; perhaps you don’t want to transfer a megabyte of serialized grid data with every page load.  You can see how big viewstate is by viewing source on your page, or by adding some code to your pages (If Request.IsLocal is true and DEBUG is defined, I tack on a label with the size from LosFormatter; see example code at scottonwriting ); but in general, if you use viewstate on a grid or listbox, you’re going to be storing a lot of data.  If you’d rather repopulate your grid or listbox with every request instead of serializing their data, simply databind them before viewstate begins being tracked - during init instead of during load.  See ASP.NET Page Lifecycle.

As for Sessions, I would recommend avoiding them.  You’ll probably have some real headaches if you need to support the offline client and rely on sessions, and it can be hard to anticipate (and test for) how sessions will be affected by one user popping several windows open.  In general, if data is ephemeral it can be handled well by viewstate.  If data is not ephemeral, you probably want to be storing it in a database.  So pass on session.


Posted by David Eison on Tuesday, October 19, 2010 3:04 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Silverlight Issues

Silverlight often feels a bit much like a work in progress, other times just like parts weren’t thought through all the way.  I thought I’d post a few examples of the sorts of things we’ve run into while developing with it.  Main lesson is, somehow you will need to plan for the unplanned.

For example, in the global application error handler:

Exception tmp = e.ExceptionObject;
if (tmp != null)
{
    while (tmp.InnerException != null)
    {
        tmp = tmp.InnerException;
    }

    // Silverlight defect where File stream can error out if file open failed due 
    // to an open file error, then later the garbage collector tries to clean up
    // the stream.  Can't be reliably handled at regular code level because we don't
    // get back a stream to operate on due to the file method erroring out.
    if (tmp is InvalidOperationException)
    {
        // string parse is bad and fragile, but I can't find any way to get the error code
        // this should be error code -2146233079 per Reflector, but, well.
        if (tmp.Message.Contains("UI Thread") && tmp.Message.Contains("System.Windows.SaveFileStream.Dispose"))
        {
            // ignore
            return;
        }
    }
. . .

Forums suggest this one may have been fixed in 4.

My favorite issue is a layout issue: If you want a label next to a textbox, you put it in a horizontal stackpanel.  But horizontal stackpanels don’t constrain themselves horizontally.  Bottom line?  Your labels won’t wrap and will clip instead.   Solution?  Specify fixed widths on your labels.  I wonder if nobody did a form with labels next to textboxes and a dynamic layout all during the testing and design phase.

We also have these gems:

                // NOTE: A "Dialogs must be user initiated" error can crop up if running under the debugger.
                // It is a spurious error and will not happen when not using the debugger.
                // See http://forums.silverlight.net/forums/t/82454.aspx

 

// firefox doesn't render our silverlight right in an iframe.
function isfirefox() {
    if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
        return true;
    } else {
        return false;
    }
}

 

/*
    This service has to be activated using basicHttp because Silverlight cannot 
    function against wsHttp.  
*/

 

        //sizechanged doesn't fire properly when column widths are changed, tabs need to
        // be hidden and refreshed for it to update... also, this can be fired
        // too early, before the column width has really changed. Layoutupdated seems to mostly work, though.

 

// assigning width directly is sub-optimal because
// it won't handle a situation where this column should be bigger
// than the main column... but the grid doesn't redraw properly
// if we don't.  Tried forcing just minwidth to work 
// with invalidatemeasure, invalidatearrange,
// measure, arrange, and updatelayout, all of which redraw the column
// header right but not the column data.

Categories: ASP.NET | Silverlight
Posted by David Eison on Sunday, September 26, 2010 5:59 PM
Permalink | Comments (0) | Post RSSRSS comment feed

CRM and prepended org names

Customers with on-premise or installations of CRM will be familiar with seeing /ORGNAME/ as the root directory in their CRM URLs. 

Microsoft added this feature so that one CRM installation could support multiple different organizations (Internet Facing Deployments and CRM online have something similar but use different server names instead of the virtual directory so that cookies can be kept separate).

However, it can lead to some confusion when wanting to link to CRM files.  Say you put a .html file in an iframe – do you use orgname or not?

The /ORGNAME/ handling is done via an ASP.NET virtual path provider – which means that it only kicks in if the ASP.NET engine is processing the request.  By default, IIS is normally configured to process requests for URLs ending in .aspx via a “Handler Mapping” .aspx to the ASP.NET engine, but resources like .html or .js or .gif or .jpg usually get processed by the default Static File handler.

So a .html file under http://[crm server]/ORGNAME/isv/myapp/myapp.html will 404, but the same file as a .aspx would happily show up.  To get to the static file, you need to leave out the /ORGNAME/: http://[crm server]/isv/myapp/myapp.html

Note that if you do want to link to a .aspx, the global JavaScript function prependOrgName is the best way to add the org name:

prependOrgName(‘/isv/myapp/myapp.aspx’)


Posted by David Eison on Wednesday, September 01, 2010 3:32 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Elmah and SoapException

A web app I’m working on which uses web service calls a lot is using Elmah for error logging.

Unfortunately web service calls consistently result in the useless ‘Server was unable to process request.’ message.

Originally I thought I could patch Elmah to log session variables then log the detail in the session in a Global.asax application_error handler.  Unfortunately, the Elmah error handler appears to consistently run before the application_error handler, so that doesn’t work (although it’s still nice to have session variables logged).

So I did some quick hacking on Elmah to get it to pull out the useful SoapException.Detail message. 

In Elmah’s Error.cs file, after:

if (httpException != null)
{
    _statusCode = httpException.GetHttpCode();
    _webHostHtmlMessage = Mask.NullString(httpException.GetHtmlErrorMessage());
}

I added:

// If this is a SOAP exception, then replace the message information with more useful info
System.Web.Services.Protocols.SoapException soapException = baseException as System.Web.Services.Protocols.SoapException;
if (soapException != null && soapException.Detail != null)
{
    _detail = baseException.Message 
        + System.Environment.NewLine + System.Environment.NewLine 
        + "Soap Detail: " + Mask.NullString(soapException.Detail.InnerText) 
        + System.Environment.NewLine + System.Environment.NewLine 
        + "Regular detail: " + System.Environment.NewLine + _detail;
    _message = Mask.NullString(soapException.Detail.InnerText);
    // not sure how big these detail strings get, so arbitrarily cap the size on the short message
    if (_message.Length > 100)
    {
        _message = _message.Substring(0, 100);
    }
}

And now I get Elmah error logs like:

elmah_log_new

Instead of:

elmah_log_old

And detail messages like:

elmah_log_newdetail

Instead of:

elmah_log_olddetail


Posted by David Eison on Thursday, August 19, 2010 2:33 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Obscure linq to sql issue - don't put 'using' at the of your partial class

Just got burned by this: http://connect.microsoft.com/VisualStudio/feedback/details/361577/mslinqtosqlgenerator-fails-with-partial-class-having-a-using-at-the-top-of-the-file

Symptom:

Linq breaks and exiting/restarting doesn’t fix it.  Your database project is now missing a ‘.designer.cs’ file under your .dbml file.

Cause:

If you put a ‘using’ statement at the top of a partial class for a linq to sql database file (we routinely recommend making these partial classes to replace the default constructor, e.g. http://blog.arkesystems.com/post/2008/03/Using-the-connection-strings-in-your-webconfig-for-LINQ.aspx ) then sometimes Visual Studio deletes your .designer.cs file and so your linq just quits compiling.

Workaround is to move the using statement inside the namespace declaration, then restore your now-deleted .designer.cs file from version control or backups or else right click on the .dbml file and select "Run Custom Tool" to rebuild the file.

Timebomb, one day might kaboom:

using System.Configuration;
namespace Symmedian.SST.Data
{
    partial class MyLinqDataContext {

Perfectly happy and fine:

namespace Symmedian.SST.Data
{
    using System.Configuration;
    partial class MyLinqDataContext {


Categories: ASP.NET | LINQ
Posted by David Eison on Sunday, August 08, 2010 9:46 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Microsoft Dynamics CRM 4.0 – Guids ToString – Even simple things can be complicated

One common need when working with CRM is to convert GUIDs to String.  Say, you want to build up some fetchxml.  No problem, .ToString(), right?

Well, Guids can actually be converted to strings in many different ways – even just the hexadecimal string can be with brackets, without brackets, uppercase, lowercase, with or without hyphens, etc. 

To keep your code as reliable and readable as possible, you’ll want to do this consistently – CRM uses uppercase hexadecimal, with brackets.  Guid has a ToString that takes a format string that you can pass it several different options.  “B” is closest to what we want, but not quite right, because it returns lowercase.  So you should convert to uppercase… but you’ll want to be careful, because converting to uppercase behavior can vary depending on the locale of the box running the code.  Usually your code will run on a server and it won’t matter, but what if you are writing a plugin which is running offline on an outlook client?  I don’t actually know if there is a locale where the hexadecimal characters A-F don’t convert right, but I know that in Turkey the uppercase of i is not I, so I don’t want to push my luck that no culture does something similar with A-F (update: Writing culture-safe managed code says Turkish and Azeri are the only languages with single character case differences, but this points out there may be multiple character case differences, such as Ff in welsh, and then there are some languages that don’t have some letters, such as Cryllic has no C for example…).  So, to be as safe as possible, specify the ‘InvariantCulture’ for any string comparisons or conversions that should deal with strings in a program-readable consistent manner.

Finally, to make this easily accessible to all of your code, you can add a utility method directly to the Guid class as an Extension Method

using System;
using System.Globalization;

namespace arkesystems.crm
{
    public static class CRMUtilExtensionmethods
    {
        public static string ToStringForCRM(this Guid guid)
        {
            return guid.ToString("B", CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture);
        }
    }
}

 

Now whenever I want to turn a guid into a string, I just have to have

using arkesystems.crm;

at the top of the class, and my extension method is available directly on the Guid itself (handily also in the autocomplete dropdown):

Guid taskGuid = crmservice.Create(t);
string strTaskGuid = taskGuid.ToStringForCRM();

Posted by David Eison on Friday, August 06, 2010 6:44 PM
Permalink | Comments (0) | Post RSSRSS comment feed

The new CRM 4 SDK is out

Just an FYI to anyone not aware, the new CRM 4 SDK has been released.

This one includes full support for LINQ, more support for development in CRM Online, and other goodies. There's also a whole new microsoft.xrm folder with samples and walkthroughs!

The Microsoft Dynamics SDK documentation website hasn't been updated yet, but I'm sure it will soon.


Posted by Wayne Walton on Thursday, May 06, 2010 4:40 PM
Permalink | Comments (0) | Post RSSRSS comment feed

CRM Custom Workflows and “This workflow includes an invalid reference”

When using a custom workflow dll in CRM, make sure to register it with the same GUIDs on production as you do on your dev CRM server.  You can do this easily by using export/import from the plugin registration tool.

When you make a workflow, anything that uses a step from the custom workflow will end up referring to it by GUID.  So if the guids don’t match for the dll, you can’t import/export workflows between the environments. And it’s not an easy fix by editing guids in the customization export file, because workflows are serialized inside this file.

If you find out about the problem early, you can unregister and reregister the dll with the right GUID.  If you find out about the problem late, after someone has already developed workflows separately in both environments, you may need to delete anything that uses data from the custom dll and recreate those workflow steps.

It shows up as an error message of “This workflow contains errors and cannot be published”, and “This workflow includes an invalid reference”.  (The invalid reference message is a generic there-is-a-guid-that-doesnt-match error, it could also be referring to a record like a system user.)


Posted by David Eison on Saturday, April 17, 2010 1:37 AM
Permalink | Comments (0) | Post RSSRSS comment feed