At first we tried the solution mentioned, involving a
SqlServerProviderHelper (not sure if I got that class name right). This actually worked fine for most tables,
but for any tables with XML columns, those would no longer come back correctly
from the DB. They needed to be encoded in NVARCHAR and no longer were.
I then began investigating whether an IAdapterProvider
solution was the way to go. After
fiddling with this, and tracing through DevForce code in Reflector, I realized
that this was only mostly dealing with the object schema and not really
involved with the actual parameters involved in a fetch operation.
The solution I ended up with seems to be good. It hinged
on the fact that we subclassed the PersistenceManager with a company-specific
version long ago, and have faithfully used our own class. This is critical because we created
"new" versions of the GetEntity()/GetEntities()-style methods. (These
really should have been virtual to begin with; then our coverage would have been
even more complete, but we think it's good enough for this case.)
Within our GetEntity()/GetEntities() methods, we
"promote" the IEntityQuery into a company-specific class derived from
RdbQuery. (We do this for EntityQuery and RdbQuery but not pass-thru
queries.) And in our RdbQuery subclass,
we override the Fetch() method of RdbQuery to do everything the base class
does, but also tweak the query parameter types:
///
<summary>
/// This
method is not intended to be called directly from your code.
///
</summary>
///
<param name="pDataSet">The dataset holding fetched
data</param>
///
<param name="pDataSourceKey">The data source key containing
connection information for the datasource.</param>
///
<remarks>
/// This
method executes on the server side of the Persistence divide to
///
retrieve data from the backend datasource.
///
</remarks>
public
override void Fetch(DataSet pDataSet, IDataSourceKey pDataSourceKey)
{
AdoHelper adoHelper = ((RdbKey)pDataSourceKey).AdoHelper;
RdbQuerySqlFormatter formatter = new RdbQuerySqlFormatter(adoHelper,
(base.QueryStrategy == null) ? null : base.QueryStrategy.TransactionSettings);
CanonicalSqlQuery pQuery = formatter.BuildCanonicalQuery(this);
ParameterizedSql pParamSql = formatter.BuildSqlSelect(pQuery);
// SQL
has an issue with sending varchar columns as nvarchar; see
// A
global type mapping change can be done,
// but
it fails because XML columns need to be sent in Unicode instead of using the DB
collation, and conversion to ANSI breaks compatibility with such columns.
// So
the solution is to instead, at fetch time, replace parameters marked as
NVarChar with VarChar
// It's
unlikely we actually query by those XML parameters so this should be relatively
safe. This only affects
// the
parameters sent out in the query, not the return type mappings.
// One
other thing we do is that in our own PersistenceManager subclass's GetEntity()/GetEntities()
calls, we "promote" all
//
EntityQuery and RdbQuery objects to our derived objects, to guarantee invoking
this class.
foreach
(var param in pParamSql.Parameters)
{
if
(param.DbType == DbType.String)
param.DbType = DbType.AnsiString;
}
using
(formatter)
{
formatter.Fetch(base.EntityType, pDataSet, pParamSql,
this.CommandTimeout);
this.FetchSpans(pQuery, pDataSet, adoHelper, formatter);
if
(base.ContainsSubquery && !this.SuppressQueryInversion)
{
this.FetchInverted(pDataSet, formatter, this);
}
}
}
You may also note that in here we could put extra
intelligence in if we needed to apply this logic conditionally, such as by
parameter name, or we could add to the query class to control the behavior.
The resulting solution has zero effect on the data type
transfer in either direction; it only affects the query parameters, which is
exactly what we want.