I am using ConfigurationManager.AppSettings.Get() to get a string
value from a web.config file.
I must pass it as a constant string value to an attribute.
How can I do that? How can I pass the string value as constant string
to the attribute?
This is going to be a long post, but the problem has
some interesting aspects (no pun intended).
I will assume that the scenario is:
* you have a third party library that contains:
- a definition of an attribute with a value
- a service that process objects of classes
with that attribute
* you have your own code that contains:
- one or more classes with that attribute
- calls to that service
* you want to make the value of the attribute
configurable instead of hardcoded
Simple example.
External library:
using System;
namespace F
{
public class SomeAttribute : Attribute
{
private String sv;
public string SomeValue
{
get { return sv; }
set { sv = value; }
}
}
public class SomeService
{
public void Process(object o)
{
object[] attrs =
o.GetType().GetCustomAttributes(typeof(SomeAttribute), true);
if(attrs.Length > 0)
{
string val = ((SomeAttribute)attrs[0]).SomeValue;
Console.WriteLine("processing with SomeAttribute
SomeValue=" + val);
}
else
{
Console.WriteLine("processing without SomeAttribute");
}
}
}
}
Your code:
using System;
using F;
namespace E
{
[SomeAttribute(SomeValue="ABC")]
public class SomeData
{
}
public class Program
{
public static void Main(string[] args)
{
SomeData o = new SomeData();
SomeService srv = new SomeService();
srv.Process(o);
}
}
}
This works fine, but the value for the attribute is hardcoded
to "ABC".
What you want to do is:
using System;
using F;
namespace E
{
[SomeAttribute(SomeValue="cfg:some")]
public class SomeData
{
}
public class Program
{
public static void Main(string[] args)
{
SomeData o = new SomeData();
SomeService srv = new SomeService();
srv.Process(o);
}
}
}
with an app.config (you are actually using web.config, but
I am testing with a Console app and it really does not change
anything):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="some" value="ABC"/>
</appSettings>
</configuration>
That does obviously not work out of the box.
You can not change the behavior of the library without changing
the library.
If you could change the source code of the library, then it
would be easy.
using System;
using System.Configuration;
namespace F
{
public class SomeAttribute : Attribute
{
private String sv;
public string SomeValue
{
get { return sv; }
set { sv = value; }
}
}
public class SomeService
{
public void Process(object o)
{
object[] attrs =
o.GetType().GetCustomAttributes(typeof(SomeAttribute), true);
if(attrs.Length > 0)
{
string val = ((SomeAttribute)attrs[0]).SomeValue;
if(val.StartsWith("cfg:"))
{
val =
ConfigurationManager.AppSettings[val.Substring(4)];
}
Console.WriteLine("processing with SomeAttribute
SomeValue=" + val);
}
else
{
Console.WriteLine("processing without SomeAttribute");
}
}
}
}
works fine.
But it assumes that:
- you have access to the source code of the library
- the license of the library allows modification
- you are willing to take on the burden of maintaining those
modifications when you need to update the library
If those assumptions were met, then you would not be
asking this question here.
So let us look at alternatives.
Decompiling the binaries, modifying the generated
souce code and rebuilding!?!?
It does get rid of the requirement for source code.
But it assumes that:
- the license of the library allows modification
- you are willing to take on the burden of maintaining those
modifications when you need to update the library
[and that burden is a lot bigger than when using the real
source code]
Not recommendable.
Using an AOP static weaver to write aspect that does
what you want and weave them into the binaries!?!?
It does get rid of the maintenance problem.
But it assumes that:
- the license of the library allows modification
- you can find a good AOP static weaver for .NET
The license is something you will need to deal with
no matter what.
Surprisingly it can be difficult to find a good
AOP static weaver for .NET.
AspectDNG were my favorite for many years. It worked
great with .NET 1.1 and I could get it to work with
2.0, but I have not been able to get it to work
properly with 4.0. The author dropped the project
in February 2008. And a quick attempt to update
the code by getting a newer version of Cecil did
not work out.
SheepAspect/SheepAOP looks rather promising. But I
have simply not been able to get it to work properly.
If anyone want to give it a try then it is available
via NuGet.
I could not find any other promising looking
candidates.
There are plenty of dynamic weavers, but that
is a rather different animal than a static weaver.
So what to do to demonstrate the technique?
I wrote my own!
When the following aspect:
using System;
using System.Configuration;
using Vajhoej.StaticWeaver;
[Aspect]
public class AttrFix
{
[Pointcut(Signature="* F.SomeAttribute::get_SomeValue()")]
public static void AttrFixPointcut() { }
[AfterCallAdvice(Pointcut="AttrFixPointcut")]
public static void AttrFixAdvice(MethodJoinpoint mjp)
{
if(((string)mjp.Result).StartsWith("cfg:"))
{
mjp.Result =
ConfigurationManager.AppSettings[((string)mjp.Result).Substring(4)];
}
}
}
is weaved into the library DLL, then the code works
as desired.
The AOP solution is worth considering if you are OK
license wise.
Arne
PS: No, I did not write it just for this post - I plan
on using it for other purposes as well.
PPS: It is called YAAOPF (Yet Another AOP Framework) and
you can get a copy as open source (Apache 2.0 license)
if someone should be interested!