You’re right. Didn’t thougt about just removing a aspect and adding your own implementation. Way easier then using a IoC container.
I don’t like passing around the container anyway as it hides code-dependencies. I once was told folks even call it an anti-pattern. Eg this guy: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/
I suppose you could do that as well, although we currently don’t have the IOC involved in creating metadata. Almost everything is a helper method to make it easier and quicker to create metadata—if you don’t like the way the helper method works, then just write your own helper method. We’ve been slowly but surely getting rid of larger extension/helper methods, so passing in an IOC so that it can create the aspect seems kind of like overkill.
You can either just write your own extension method, like so:
public static ClassCacheAspect SetSuperCacheAspectValues(
this IMetaClass metaClass,
Action<ClassCacheAspect> setValues)
{
return metaClass.UpdateAspect<IClassCacheAspect, ClassCacheAspect>(
new SuperClassCacheAspect(),
setValues
);
}
We can call this as follows:
Elements.Classes.Person.SetSuperCacheAspectValues(a => a.Capacity = 1000);
If I go the IOC route, then I would make the base helper method accept another parameter (I guess?). This is kind of neat, and would let me push the machinery for creating a new aspect down to the next-level methods.
The method that works with aspects that implement ICopyTarget
looks like this:
public static TConcrete SetAspectValues<TService, TConcrete>(
this IMetaClass metaClass,
IServiceRequestHandler handler,
Action<TConcrete> setValues
)
where TConcrete : TService, ICopyTarget<TService>
where TService : IMetaAspect
{
return metaClass.UpdateAspect<TService, TConcrete>(
handler,
(aspect, existingAspect) => aspect.CopyFrom(existingAspect),
setValues
);
}
The fully generalized one that has no expectations of the aspect actually creates the aspect using the IOC.
public static TConcrete SetAspectValues<TService, TConcrete>(
this IMetaClass metaClass,
IServiceRequestHandler handler,
Action<TConcrete, TService> copyValues,
Action<TConcrete> setValues
)
where TConcrete : TService
where TService : IMetaAspect
{
var aspect = handler.GetInstance<TConcrete>();
var existingAspect = metaClass.Aspects.FirstOfTypeOrDefault<TService>();
if (existingAspect != null)
{
copyValues(aspect, existingAspect);
}
setValues(aspect);
return aspect;
}
And, finally, the caching-specific method looks like this:
public static ClassCacheAspect SetCacheAspectValues(
this IMetaClass metaClass,
IServiceRequestHandler handler,
Action<ClassCacheAspect> setValues)
{
return metaClass.UpdateAspect<IClassCacheAspect, ClassCacheAspect>(
handler,
setValues
);
}
Now I don’t have to ever call new
for an aspect, but I have to pass in the handler, every single time.
Elements.Classes.Person.SetSuperCacheAspectValues(handler, a => a.Capacity = 1000);
I would have to make sure that the handler
(IOC) was available during metadata construction (which it generally isn’t, but could be, via constructor injection on the metadata builder class, e.g.)
I think this is a matter of preference, but given how small the chance is that I would want a different cache aspect to be created—and how easy it is to make my own helper method—then I would opt not to use the IOC, just so I don’t force all callers to (A) have a reference to an IOC around and (B) have an extra parameter that isn’t needed in 99.9% of the cases.
Although, since these are helper methods, there’s nothing stopping anyone from creating the methods I outlined above and using that pattern instead. Perfectly valid to use the IOC there, but a bit uglier to get it down to where it can be used.
… what about replacing the aspect implementation with another one? DI came in my mind when reading this article. What about using a IoC container for construction? Maybe with constructor injection?
Cheers, Marc
ASP.Net 5 (aka vNext) does some parts very similar. See here …
Cheers, Marc
In this sample the Where() could be replaced with a TypeOf<>() I guess. But yes, I got the point ;-)
This one’s from Chris Meadowcroft:
const string EnglishListPrefix = "{";
const string EnglishListSuffix = "}";
const string IntermediateSeparator = ", ";
const string LastSeparator = " and ";
static string BuildStringWithEnglishListSyntax(IEnumerable<string> itemsEnumerable)
{
StringBuilder result = new StringBuilder(EnglishListPrefix);
using(IEnumerator<string> items = itemsEnumerable.GetEnumerator())
{
if(items.MoveNext()) // make sure it's not an empty list
{
bool isFirstString = true;
bool isLastString = false;
while(!isLastString)
{
string current = items.Current;
isLastString = !items.MoveNext();
if(!isFirstString)
{
result.Append(isLastString ? LastSeparator : IntermediateSeparator);
}
else
{
isFirstString = false;
}
result.Append(current);
}
}
}
result.Append(EnglishListSuffix);
return result.ToString();
}
surprised that he really recommend to use com-automation at all. its shaky even für interactive winform-apps. but using com-automation in asp.net app?!?!? … don’t know how they get fogbugz stable at all using these techniques…
Wildcards are a way to provide partial support for propert generics, in which—if B inherits from A—List<B> also inherits from List<A>. In both Java and C#, this is not the case, which makes passing generic parameters all the more difficult.
The where
keyword in C# corresponds to the extends
keyword in Java. It indicates that the actual generic parameter must conform to the given base type (which can be a class or an interface). In your example, the implementation of the generic class may call any features defined in MYBASETYPE
on MYTYPE
.
hi marco,
just to get things the right way (as i don’t know java very well):
is this wildcard-thing the same as the “WHERE” in C# 2?
For example:
public class A<MYTYPE>
where MYTYPE : MYBASETYPE, new
{
…
}
have to try your examples above in the c# and see if i get this working as well as it looks like a nice exercise for rainy weekends like this ;-)
cheers, marc
Didn’t know that.
So, we definitivly should cancel IE ;-)
btw: i am missing a “thumbs up” image for the comments :)
cheers, marc