Know your enemy; Sun Tzu's The Art of War
If you wan to be able to archive your goals in a EPiServer project you need to know the product. And EPiServer have (thanks heaven) open dll’s so we can use reflector to browse thru the inner workings of the product. Sometime I guess they regret that decision, but I honestly think that's one of the things that make EPiServer as a product great.
Then back to topic, who can we know our enemy
A twitter user posted a tweet some days ago where he said: “Ohhh, I SOOO want an event to trigger when dynamic properties change. Please! #EPiServer“
But instead of begging he should have tried to know his enemy, an start the all mighty reflector :)
AFAIK there are only one place dynamic properties are saved and that's in the EditDynProp.aspx. And that page have one button, called ApplyButton….
Here we see that there is a method SaveCollection that is called
And that calls a method Save
Which calls PopulatePropertyCommand
And here we see that OnBeforeSavingProperty is called
And here it is. BeforeSavingProperty, that's an event that get called when a dynamic property is saved. Either if it’s empty or have values. This event is only called when you save a dynamic property.
- public class AttachEvents : PlugInAttribute
- {
- public static void Start()
- {
- PageDB.BeforeSavingProperty += new EventHandler<PropertyEventArgs>(PageDB_BeforeSavingProperty);
- }
- static void PageDB_BeforeSavingProperty(object sender, PropertyEventArgs e)
- {
- int a = 0;
- }
This opens up some nice things that can be done. For instance create a version list on dynamic property page. Which is one of the feature I miss most.
It this event was not there we could also used PageAdaptors to hook our self up to the ApplyButton click event and done it there.
This feature have been around since CMS 5 it seems. So happy coding and use your Reflector when you can :)
Nice work Anders!
There is a chance that the user (!) stopped looking when he saw the DynamicPropertiesDB class, which indicate a class in the EPiServer.DataAccess namespace. This namespace has traditionally been a "keep your dirty fingers away" kind of namespace.
However, it is now included in the SDK Reference (though documentation is missing.) This makes me wonder if we're actually allowed to use it now, which would have been nice for this particular feature at least.
Anyone care to pitch in?
It's probably true that the user in question stopped looking there :) (that lazy man :))
but events are public pr default, and if they exists they are usable.
If the founding fathers didn't want us to use them they could have used (Good forbid) protected internal delegates
Nice post Mr.Hattestad
If you look at the desciption for EPiServer.DataAccess namespace on sdk.episerver.com it says:
"The EPiServer.DataAccess namespace contains internal classes that is only intended for internal functions in EPiServer, you should not use these classes because you will break compability with future upgrades. There is though one important enumeration that comes to use when publishing content using EPiServer.DataFactory."
PageDB is not a publically supported API. Use the workaround at your own risk!!
It is actully marked with public. :)
public class PageDB : DataAccessBase
There is a BIG difference between a Type being marked public and it being part of the publically supported API of a product. That's where the documentation overrides.
Yeah; I agree on that lazy part.
I like to think of APIs like a forest. This is not specific to EPiServer in any way. You're allowed to wonder about, exploring things. But if you wonder too far off the path, you're likely to get lost. Reflector is your GPS, so to speak, so you can explore more than you could without it, but the dark forest can be a dangerous place never the less. Suddenly someone puts up a fence, or cuts down the trees, and then getting back to the path gets more difficult.
Upgrades, new functionality, hotfixes; they will from time to time demand breaking changes, that is the life of software. The effort put into keeping the most used parts of the API backwards compatible will always be greater than on the less used ones.
Solving complicated problems sometimes demand complicated solutions, using undocumented parts of the API, or changing how the system works in ways that is not optimal. But as Joel so eloquently puts it - "If it's this hard, maybe we're doing something wrong":
http://joelabrahamsson.com/entry/if-its-this-hard-maybe-were-doing-something-wrong
In this particular case, when using that event, we discovered that the event argument class had a property called CurrentPage, of the type PageReference. This is the first warning that something is not quite right. In all (I think) other places, CurrentPage is a PageData object. Also, the event seems to be triggered for non-modified properties, so it is a little sharp around the edges.
All in all, we're opting on not using this event, it does not feel quite right, and we want to keep the upgrade path as clean as possible. I did no see that usage description on the namespace, which makes this an even clearer no-no.
Lesson learned: just because you can do it does not mean you should.
Have no trouble seeing that this stuff can change, but
Steve=>
When I see a solution so easy as to just attach myself to an event I feel that we are doing something right :)
but I have no trouble seeing your point on upgrades and hot fixes.
Paul=>
Why do you mark other classes with internal then? :)
Anders =>
So we can use them in other projects if we like.
In the case of these DataAccess-types, there is a need to reference them across other EPiServer "core"-assemblies, so "internal" wont work leaving not much choice but to mark them as "public". (something like c++'s "friend" would be nice to have sometimes).
When *those* requirements aren't needed, we sure do use "internal" as you probably have noticed ;-)
/johan
[InternalsVisibleTo] :)
@Johan why not use the assemblyinfo file to mark which assemblies should have access to the internal marked class by using the InternalsVisibleTo attribute (http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx)?
Frederik
stop Fredrik don't give away methods on how to
Mark stuff internal... :)
I'm not picking sides here, just IMHO: It is still possible to access internal/private/whatever and to "new" non-virtual members, even though it gets hairy and ugly. So the access modifiers are mainly about expressing the /intended/ use of a class - something which /can/ be expressed by other means (like documentation). Nobody can build a framework which is perfectly extensible everywhere, especially not without introducing breaking changes as the core features are improved. Just because you can you shouldn't, with great (reflector) power comes great responsibility, jada-jada, etc. Or along the lines of Joel's very popular blog post: "If it's this hard, maybe we shouln't do it at all."
Maybe classes should have a [FeatureStability(x)] or [Hackability(x)] attribute conveying the estimated risk of a breaking change in future versions.
I would like to point out that I started this post about how changeable EPiServer is. And by changeable I think all know that sometimes our changes will not survive an upgrade. That’s the name of the game.
I think that is one of the primary EPiServer strengths!, And I hope that the development team have the same attitude :) [Dont use private/internal]
We have a lot of legacy code to deal with or technical debt as we call it here.
We now have a policy where we err on the side of openess (i.e. make classes public), extendability (i.e. make methods virtual) and testability (i.e. use interfaces and DI techniques). It will be a long time before this is felt in any great strength and of course mistakes get made. Keep your constructive feedback coming!