I have done some more on extending the T4 templates to auto-generate fully typed AutoQuery DTOs for each table exposed. Like this:
[UseNamedConnectionAttribute("SqlConnection.CommonDB")]
public partial class PackageTypeQuery: QueryBase<PackageType>
{
public string Id { get; set;}
public string IdStartsWith { get; set;}
public string IdEndsWith { get; set;}
public string IdContains { get; set;}
public string IdLike { get; set;}
public string[] IdBetween { get; set;}
public string[] IdIn { get; set;}
public bool? ActiveFlag { get; set;}
public DateTime? CreatedDateGreaterThanOrEqualTo { get; set;}
public DateTime? CreatedDateGreaterThan { get; set;}
public DateTime? CreatedDateLessThan { get; set;}
public DateTime? CreatedDateLessThanEqualTo { get; set;}
public DateTime? CreatedDateNotEqualTo { get; set;}
public DateTime[] CreatedDateBetween { get; set;}
public DateTime[] CreatedDateIn { get; set;}
}
Its worked out really well, and now have a few thousand tables fully exposed to HTTP querying with no real coding beyond setting up the AppHost. But we needed to handle multiple Dbs so the following plug-in was required.
/// <summary>
/// Will cause IDbConnectionFactory to resolve to named connection
/// </summary>
public class UseNamedConnectionAttribute : RequestFilterAttribute
{
public string NamedConnection { get; set; }
public UseNamedConnectionAttribute(string namedConnection)
{
NamedConnection = namedConnection;
}
public override void Execute(ServiceStack.Web.IRequest req, ServiceStack.Web.IResponse res, object requestDto)
{
RequestContext.Instance.Items.Add(MultiDbConnectionFactory.RequestContextKey, NamedConnection);
}
}
//Will pull injected context named connection to open
public class MultiDbConnectionFactory : IDbConnectionFactory
{
public MultiDbConnectionFactory(IDbConnectionFactory failbackDbFactory)
{
FailbackDbFactory = failbackDbFactory;
}
private IDbConnectionFactory FailbackDbFactory;
public const string RequestContextKey = "_UseNamedConnection";
public IDbConnection CreateDbConnection()
{
try
{
return OrmLiteConnectionFactory.NamedConnections[(string)RequestContext.Instance.Items[RequestContextKey]].CreateDbConnection();
}
catch{}
return FailbackDbFactory.CreateDbConnection();
}
public IDbConnection OpenDbConnection()
{
var conn = CreateDbConnection();
conn.Open();
return conn;
}
}
/// <summary>
/// Registers a multi-db aware request filter to help handle T4 generated *Query
/// requests. It will inject the proper named Db, as well as register them from
/// the AppSettings.
/// </summary>
public class AutoQueryMultiDbConnectionFeature : IPlugin
{
private readonly ILog Log = LogManager.GetLogger(typeof(MultiDbConnectionFactory));
public bool AutoLoadFromAppSettings { get; set; }
public string AppKeyPrefix { get; set; }
public AutoQueryMultiDbConnectionFeature(bool autoLoadFromAppSettings = true, string appKeyPrefix = "SqlConnection.")
{
AutoLoadFromAppSettings = autoLoadFromAppSettings;
AppKeyPrefix = appKeyPrefix;
}
public void Register(IAppHost appHost)
{
OrmLiteConfig.DialectProvider = SqlServer2012Dialect.Provider;
appHost.Register<IDbConnectionFactory>(new MultiDbConnectionFactory(new OrmLiteConnectionFactory() { DialectProvider = SqlServer2012OrmLiteDialectProvider.Instance}));
if (AutoLoadFromAppSettings)
{
foreach (var key in HostContext.AppSettings.GetAllKeys().Where(t => t.StartsWith(AppKeyPrefix))) //This is our internal convention for AppSetting storage of connection strings (also is nice with Tier feature)
{
var nameKey = key;
var factory = new OrmLiteConnectionFactory()
{
DialectProvider = SqlServer2012Dialect.Provider ,
ConnectionString = HostContext.AppSettings.Get(key,string.Empty),
};
OrmLiteConnectionFactory.NamedConnections.Add(nameKey, factory);
}
}
}
}
I was planning on resubmitting the changes so others could use it, but was wondering if I should include the multi-db support, or how it could be refactored (beyond removing Sql2012 specifics) to better fit in to the framework.I’m also reluctant to include that aspect because I haven’t figured out a clean way to run unit tests. Suggestions are welcomed, because I imagine there is a cleaner way (I am just manually setting the named connection since filters don’t run):
[Test]
public void CountryCodeQueryTest()
{
var betweenRange = new string[] { "C", "G" };
var req = new CountryCodeQuery() { IdBetween = betweenRange, SortOrderGreaterThan = 52, Take = 40, Skip = 3, };
var aq = req.GetType().FirstAttribute<UseNamedConnectionAttribute>();
RequestContext.Instance.Items.Add(MultiDbConnectionFactory.RequestContextKey, aq.NamedConnection);
HostContext.AppHost.ExecuteService(req).PrintDump();
}