CacheManger Using Redis Multiplexer with Web.Config Configuration - c#

I need to implement Michael solution using two Cache Instances like he explain in WhatIfRedisStopsWorkingHowDoIkeepMyAppRunning but using configuration in web.config.
Finally i only have this line of code
var defaultConfig = ConfigurationBuilder.LoadConfiguration("defaultCache");
I don`t find how to access to the ConnectionMultiplexer to hook me in the events or do it by config...
Is posible?

There are two ways to configure Redis via app/web.config in CacheManager,
via ConnectionString
<connectionStrings>
<add name="redisFromConnectionStrings" connectionString="127.0.0.1:6379,allowAdmin=True,connectTimeout=11,ssl=False,abortConnect=False,connectRetry=10" />
</connectionStrings>
or Redis configuration section
<cacheManager.Redis xmlns="http://cachemanager.michaco.net/schemas/RedisCfg.xsd">
<connections>
<connection id="redisAppConfig" allowAdmin="true" password="" ssl="false" sslHost="" connectionTimeout="11" database="3">
<endpoints>
<endpoint host="127.0.0.1" port="6379" />
</endpoints>
</connection>
</connections>
</cacheManager.Redis>
:UPDATE:
There is currently no option to access the connection multiplexer used by CacheManager.
But you can pass in an existing multiplexer to the configuration.
var defaultConfig = ConfigurationBuilder.LoadConfiguration("defaultCache");
var multiplexer = ConnectionMultiplexer.Connect(...);
defaultConfig = defaultConfig
.Builder
.WithRedisConfiguration("redisConfig", multiplexer )
.Build();
Of course you have to instantiate the multiplexer yourself and cannot use the web/app config anymore to configure the Redis part. You'd have to handle that yourself...

Related

How modify loaded configuration by CacheManager in runtime

I'm using #Victor P solution to manage the cache in my application.
The configuration is loaded from the application settings, but we have a policy of not add sensitive information in the code, and on the other hand, the production instance of Redis requires authentication. This password is loaded from the environment variables, but I can't find the way to modify the Redis configuration in runtime.
Here is how we are doing it now
// Locad configuration of cache type: MemoryCache or RedisCache
string cacheManagerName = ConfigurationManager.AppSettings["CacheManagerName"];
// Build cache configuration from configuration section
var config = ConfigurationBuilder.LoadConfiguration(cacheManagerName);
//TODO: Modify config if the variable environment for the password is set
// This will only necessary if the cache type is Redis
//Create cachemanager instance
_kernel.Bind(typeof(ICacheManager<>)).ToMethod((ctx) => CacheFactory.FromConfiguration(ctx.GenericArguments[0], config)).InSingletonScope();
Configuration example:
<add key="CacheManagerName" value="RedisCache" />
<cacheManager xmlns="http://cachemanager.michaco.net/schemas/CacheManagerCfg.xsd">
<managers>
<cache name="MemoryCache" updateMode="None" enableStatistics="false" enablePerformanceCounters="true">
<handle name="default" ref="MemoryCacheHandle" />
</cache>
<cache name="RedisCache" updateMode="Up" enablePerformanceCounters="true"
enableStatistics="false" backplaneName="RedisConfigurationId"
backplaneType="CacheManager.Redis.RedisCacheBackplane, CacheManager.StackExchange.Redis"
serializerType="CacheManager.Serialization.Json.JsonCacheSerializer, CacheManager.Serialization.Json">
<handle name="RedisConfigurationId" ref="RedisCacheHandle" isBackplaneSource="true"/>
</cache>
</managers>
<cacheHandles>
<handleDef id="MemoryCacheHandle" type="CacheManager.SystemRuntimeCaching.MemoryCacheHandle`1, CacheManager.SystemRuntimeCaching"
defaultExpirationMode="Sliding" defaultTimeout="30m" />
<handleDef id="RedisCacheHandle" type="CacheManager.Redis.RedisCacheHandle`1, CacheManager.StackExchange.Redis"
defaultExpirationMode="Sliding" defaultTimeout="30m" />
</cacheHandles>
</cacheManager>
<cacheManager.Redis xmlns="http://cachemanager.michaco.net/schemas/RedisCfg.xsd">
<connections>
<connection id="RedisConfigurationId"
allowAdmin="true"
password=""
ssl="false"
sslHost="">
<endpoints>
<endpoint host="127.0.0.1" port="6379" />
</endpoints>
</connection>
</connections>
</cacheManager.Redis>
Removing secrets from an app/web.config was always an issue by itself I guess.
There is a documentation post which explains some options.
Regarding CacheManager. You can use the <connectionStrings> section for configuring Redis, instead of the cacheManager.Redis section, and then store that connection string in a separated "secret" file
<connectionStrings configSource="ConnectionStrings.config">
</connectionStrings>
That's still pretty stupid though in my opinion. So the best way is to configure that part entirely by code and read secrets from some secure store. Btw, environment variables are not secure at all either.
You can "trick" cachemanager and add just the redis configuration by code via RedisConfigurations. And reference the config key as usual.

Read connection string from web.config file when deploy to IIS7

It seems to be pretty simple question but not for me. I am trying to read connection string from web.config file.
I have WCF service application which have web.config file. In web.config file I defined connection strings.
Then I deployed wcf application on IIS7 under default web site(One template which comes when you install IIS7).
Now when we read connection string then it is not giving me connection strings which define in wcf web.config file. Somehow I am not able to access it. And while debugging when I found a connection string which is actually not connection string which I defined in wcf web.config but it is default web site connection string which I don't want.
I want to access connection string which I defined WCF web.config file only. I am stuck in it. I tried all but with no luck. For reference I am putting code which I tried and web.config code also.
Web.Config code.
<configuration>
<system.diagnostics>
<trace>
<listeners>
<add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="AzureDiagnostics">
<filter type="" />
</add>
</listeners>
</trace>
</system.diagnostics>
<appSettings>
<add key="ConnString" value=""/>
<!--<add key="ConnString" value=""/>-->
</appSettings>
<connectionStrings/>
<system.web>
<httpRuntime maxRequestLength="2097151"
useFullyQualifiedRedirectUrl="true"
executionTimeout="14400" />
<compilation debug="true" />
</system.web>
Code to read connstring
string connString = string.Empty;
string svcDir = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
DirectoryInfo rootDir = Directory.GetParent(svcDir);
string root = rootDir.FullName;
string webConfigFilePath = root + "\\Web.config";
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = webConfigFilePath;
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var test = configuration.ConnectionStrings.ConnectionStrings;
string connectionString = "";
if (configuration.ConnectionStrings.ConnectionStrings["ConnString"].ConnectionString.Length > 0)
{
connectionString = configuration.ConnectionStrings.ConnectionStrings["ConnString"].ConnectionString;
}
//var connectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnString"];
//if (connectionString != null)
//{
//}
return connString;
I am using .net framework 4.0, IIS 7, apppool is .net framework 4.0 and windows 7 machine.
Thanks,
Awadhendra
The web.config has a dedicated section for connection strings, and the code you are using is using the configuration manager to access the connection strings section in the web.config. The problem you have is that you haven't put the connection string in the connection strings section, you have put it in the app settings. Move the connection string out of app settings and put into the connection strings section, or change your code to read it from app settings. Now storing a connection string in app settings was the way it was done in .NET 2.0, but since then (.NET 3.5) there has been a dedicated section made for connection strings.
please refer here
e.g.
<connectionStrings>
<add name="ConnString" connectionString="xxxwhateveritisxxx" />
</connectionStrings>
or code to read it from app settings (if you must have it in app settings, although I wouldn't recommend):
string connectionString = ConfigurationManager.AppSettings["ConnString"];
Can u give a try to this :-
string connString = ConfigurationManager.AppSettings["ConnString"].ToString()
return connString;
It will read the app setting section of your web.config.

webservices trace / log

I have set of web services and I want to add a trace layer.
I don't want to modify each web service since I have many.
I would like to write log every entering to a web service: name of web service and parameters.
What is the best way to do so?
P.S. I am using asp.net and C#.
EDIT:
I only want to wrap the web services as each one will have log(..) at the beginning.
A common way to achieve this is to inject a SOAP extension. From there you can intercept every request/response packet in raw SOAP. The sample shows how to implement one, and the explanation describes how it works and how to configure it.
Sample:
http://msdn.microsoft.com/en-us/library/system.web.services.protocols.soapextension.aspx
Explanation:
http://msdn.microsoft.com/en-us/library/esw638yk(vs.71).aspx
Configuration:
http://msdn.microsoft.com/en-us/library/b5e8e7kk(v=vs.71).aspx
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="{Type name}, {Assembly}" priority="1" group="0" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
Add a Global Application Class Global.asax file to your project and add the logging logic to the Application_BeginRequest() method. The sender object will contain the HTTP Request and parameters. You can filter for just .asmx requests and log those.
protected void Application_BeginRequest(object sender, EventArgs e)
{
}
EDIT--
Give PostSharp a try. It's the easiest way to get this functionality. For posterity I will leave my posting below but just ignore it and use PostSharp.
If your web services are WCF then you should check out http://msdn.microsoft.com/en-us/magazine/cc163302.aspx.
At each step along the way they provide extensibility points that you can plug into. You can use these extensibility points to implement a wide variety of custom behaviors including message or parameter validation, message logging, message transformations.
No doubt this is the way to go for WCF services. Otherwise, if they are just web services then you can use the Unity framework and hookup and Interceptor to do the same thing.
If the progamming language is not important, you may put Apache Synapse as proxy in front of your services. Your clients will then send the requests to Synapse, which will delegate the requests to your original services. The proxy can be configured to do something with the requests in between, such as logging.
Please see the following links for more information:
http://synapse.apache.org/Synapse_Configuration_Language.html#proxy,
http://synapse.apache.org/Synapse_Configuration_Language.html#send,
http://synapse.apache.org/Synapse_Configuration_Language.html#log
A combination of the following examples could work for you:
http://synapse.apache.org/Synapse_Samples.html#Sample0
http://synapse.apache.org/Synapse_Samples.html#ProxyServices
e.g.:
<definitions xmlns="http://ws.apache.org/ns/synapse"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ws.apache.org/ns/synapse http://synapse.apache.org/ns/2010/04/configuration/synapse_config.xsd">
<proxy name="StockQuoteProxy">
<target>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
</endpoint>
<outSequence>
<!-- log all attributes of messages passing through -->
<log level="full"/>
<!-- Send the message to implicit destination -->
<send/>
</outSequence>
</target>
<publishWSDL uri="file:repository/conf/sample/resources/proxy/sample_proxy_1.wsdl"/>
</proxy>
How about writing your own HttpModule? That would negate the need to touch the existing web service code. You would just need to add your module to each web.config file.
I maintain an Open source web services framework that lets you simply achieve this by having all web services inherit from a base class and do your own logging.
Here is an example of a base-class where I maintain a distributed rolling log for all exceptions in redis - a very fast NoSQL data store:
public object Execute(TRequest request)
{
try
{
//Run the request in a managed scope serializing all
return Run(request);
}
catch (Exception ex)
{
return HandleException(request, ex);
}
}
protected object HandleException(TRequest request, Exception ex)
{
var responseStatus = ResponseStatusTranslator.Instance.Parse(ex);
if (EndpointHost.UserConfig.DebugMode)
{
// View stack trace in tests and on the client
responseStatus.StackTrace = GetRequestErrorBody() + ex;
}
Log.Error("ServiceBase<TRequest>::Service Exception", ex);
//If Redis is configured, maintain rolling service error logs in Redis (an in-memory datastore)
var redisManager = TryResolve<IRedisClientsManager>();
if (redisManager != null)
{
try
{
//Get a thread-safe redis client from the client manager pool
using (var client = redisManager.GetClient())
{
//Get a client with a native interface for storing 'ResponseStatus' objects
var redis = client.GetTypedClient<ResponseStatus>();
//Store the errors in predictable Redis-named lists i.e.
//'urn:ServiceErrors:{ServiceName}' and 'urn:ServiceErrors:All'
var redisSeriviceErrorList = redis.Lists[UrnId.Create(UrnServiceErrorType, ServiceName)];
var redisCombinedErrorList = redis.Lists[UrnId.Create(UrnServiceErrorType, CombinedServiceLogId)];
//Append the error at the start of the service-specific and combined error logs.
redisSeriviceErrorList.Prepend(responseStatus);
redisCombinedErrorList.Prepend(responseStatus);
//Clip old error logs from the managed logs
const int rollingErrorCount = 1000;
redisSeriviceErrorList.Trim(0, rollingErrorCount);
redisCombinedErrorList.Trim(0, rollingErrorCount);
}
}
catch (Exception suppressRedisException)
{
Log.Error("Could not append exception to redis service error logs", suppressRedisException);
}
}
var responseDto = CreateResponseDto(request, responseStatus);
if (responseDto == null)
{
throw ex;
}
return new HttpResult(responseDto, null, HttpStatusCode.InternalServerError);
}
Otherwise for normal ASP.NET web services frameworks I would look at the Global.asax events, specifically the 'Application_BeginRequest' event which Fires each time a new request comes in.
I don't know if this is what you are looking for ,just add this to you WCF config file after the ""
It will create very extensive logging that you will be able to read using the Microsoft Service Trace Viewer
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" switchValue="Warning, ActivityTracing">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="ServiceModelMessageLoggingListener">
<filter type="" />
</add>
</listeners>
</source>
<source name="System.ServiceModel" switchValue="Warning, ActivityTracing"
propagateActivity="true">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="ServiceModelTraceListener">
<filter type="" />
</add>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="C:\ServiceLog.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
<filter type="" />
</add>
<add initializeData="C:\Tracelog.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelTraceListener" traceOutputOptions="Timestamp">
<filter type="" />
</add>
</sharedListeners>
</system.diagnostics>

How to use Application Data in an (App.config) connectionString

I've got an SQL Server CE database in a project that I wan't to store somewhere in the %AppData% directory. However I can't find a way to make a reference to the Application Data path in the connection string (in the App.Config)
<?xml version="1.0"?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="EntityConnectionString" connectionString="metadata=res://*/EntityModel.csdl|res://*/EntityModel.ssdl|res://*/EntityModel.msl;provider=System.Data.SqlServerCe.3.5;provider connection string="Data Source=|ApplicationData|\Entities.sdf"" providerName="System.Data.EntityClient"/>
</connectionStrings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
</configuration>
So far I learned that: %APPDATA% is not supported and using the settings class (like suggested) won't work either (the settings class isn't constructed at the time the exception is already thrown).
Is it possible to use the application data folder (or another special folder) in the connectionString property (in the App.Config)?
Note: it seems like I'm searching for an solution to modify the connection string (in code) as early as possible rather than an native App.Config solution.
Use your custom build environment variable support:
Let you have:
<connectionStrings>
<add name="My" connectionString="..;Data Source=|%AppData%|\Entities.sdf;.." />
</connectionStrings>
The you can use:
using System.Configuration; // requires reference to System.Configuration.dll
ConfigurationManager.ConnectionStrings["EntityConnectionString"].ConnectionString.Replace("%AppData%", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
Next way you can support several environment variables:
var vars = new Dictionary<string, string>
{
{ "%AppData%", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
{ "%Temp%", Environment.GetFolderPath(SpecialFolder.Temp) },
// etc..
{ "%YourNonStandardVar", "YourNonStandartPath" }
};
var result = ConfigurationManager.ConnectionStrings["YourString"].ConnectionString
foreach (var v in vars)
result = result.Replace(v.Key, v.Value);

Castle Active Record Multiple Database Connections (Oracle and SQL)

Scenario: I have an application that pulls data from a SQL database as well as an Oracle database. I have NHibernate implemented for the SQL side and a co-worker already has a working implementation of the Oracle side (same object different project). I am currently defining a connection string in App.Config and calling this function in Program.cs
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Foo"].ToString();
var configuration = InPlaceConfigurationSource.Build(DatabaseType.MsSqlServer2000, connectionString);
ActiveRecordStarter.Initialize(System.Reflection.Assembly.GetExecutingAssembly(), configuration);
Note the project is in C# .Net 3.5
I have read about using DifferentDatabaseScope but when I try that the queries don't return any results nor can I see anything in the NHib Profiler. No errors pop up just 0 count.
Question: How do I implement multiple connections?
For future users who come across this problem.
This article helps http://www.darkside.co.za/archive/2008/01/21/castle-activerecord-connecting-to-multiple-databases.aspx
To get this to work I had to add these code snippets to my App.config
<activerecord>
<config>
<add key="connection.driver_class"
value="NHibernate.Driver.OracleClientDriver" />
<add key="dialect"
value="NHibernate.Dialect.Oracle10gDialect" />
<add key="connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
<add key="connection.connection_string"
value="Data Source =
(DESCRIPTION =
(ADDRESS =
(PROTOCOL = TCP)
(HOST = SERVERNAME)
(PORT = 1521)
)
(ADDRESS =
(PROTOCOL = TCP)
(HOST = SERVERNAME)
(PORT = 1521)
)
(LOAD_BALANCE = yes)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = NAME)
)
);User Id = ID; Password = PASS;" />
</config>
And this
<config type="Sens.SensClass`1, Sens">
<add key="connection.driver_class"
value="NHibernate.Driver.SqlClientDriver" />
<add key="dialect"
value="NHibernate.Dialect.MsSql2000Dialect" />
<add key="connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
<add key="connection.connection_string"
value="Data Source=mntcon016\;Initial Catalog=TEST;Trusted_Connection=True;" />
</config>
</activerecord>
<configSections>
<section name="activerecord"
type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" />
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/>
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="proxyfactory.factory_class"> NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle </property>
</session-factory>
</hibernate-configuration>
Then in Program.cs before Application.Run I have
ActiveRecordStarter.Initialize(
ActiveRecordSectionHandler.Instance, types.ToArray());
Where types is a list(function requires an array) of type[]. This list needs to contain every class that will be used with Nhibernate. In my case it contains both the SQL and Oracle classes. As well as this class that is inherited by all my SQL classes
public abstract class TestClass<T> : ActiveRecordBase<T>
{
}
To generate my SQL classes I had used a generator and it made them all Serializable which had to be taken off. Also note that you cannot have classes with the same name or you will get an error.

Categories

Resources