So I've been looking over YAF for a while, and as someone who is contemplating adopting the software, I would like to make some comments that are related to this thread. I'm going to give my rather blunt opinion, but this are general comments and concerns that someone like myself are having with reviewing ASP.NET software that we would like to integrate into an existing site. This is the first time I've tried to put all of these thoughts together, so bear with me.
I think what this thread shows most of all is that there are some design limitations of the ASP.NET provider model.
I'm actually a big fan of the concept of the Membership and Role providers, but the actual implementation was not done with flexibility and integration in mind. What is the real goal? to allow newbies the ability to easily make "secure" sites without actually having to make any decisions. Sure the concept of multiple providers is thrown in there, but I'm hard-pressed to see what utility that really has. And without any guidance, it seems like nothing but an afterthought.
Another limitation of these two providers is that they only operate in read/write mode. For those of us dealing with SSO systems and pre-built directories of roles that we don't necessarily want to expose willy-nilly to ASP.NET tools, there is no simple way to provide a basic version of the providers. Sure, I can put NotImplementedExceptions everywhere, but how do my applications know to avoid that code??
This could have been done by providing a very basic Membership/Role provider base class that does the absolute core functions (GetUser(), GetRolesForUser(), IsUserInRole(), and not a heck of a lot more), and using interfaces to tag modular functionality. Provide the existing SqlRoleProvider on top of that if you want to use all of the out-of-the-box tools. That way clients could query for the availability of that functionality and respond accordingly.
I can't even talk about some of the other garbage in there... if I've got an LDAP user database with 100K users in it, I'm not going to want to be using things like GetAllUsers() either! Especially when that list would be exposed to someone using a web application.
Then comes the dang Profile provider. Not only does this one come with the same "get it to work without thinking about it" functionality but it also contains detrimental "get it to work without coding it" functionality as well. The database of the default provider is the most unfriendly thing I have ever seen.
Although it might be easy to get your applications to use a single provider in this model, in order to get any real benefit out of this arrangement, you'd have to get them to agree on the naming of all of the properties, as well as their acceptable formats, and this probably is not going to happen. Since you really can't share data easily, and since an application like YAF is still probably providing is own database tables optimized for that application, why would you even bother trying to use another provider at all?
In this case, the providers should be named explicitly. (If you wanted everyone to use the same SqlProfileProvider, that's great, but listing them separately would at least allow you to specify different application keys.)
My bigger beef with the profile provider is applications relying on the automatic code-generation tools. If you are playing around with a simple site, then using is nice, but as you get more complex then that fragile configuration can definitely be an issue.Unfortunately, this is a requirement of the framework since it gives no mechanism for a provider to supply a dynamic set of properties in code, and even if you don't care about nicely-generated class properties, the definitions are still used internally. It either has to be in web.config, or attributes of a class specified using the "inherits" attributes as described in this thread. The "inherits" thing is a nice idea, but why is a global profile configuration, instead of an attribute of the provider? What if I try to get two products that both want that value set to their own?
Personally, I would avoid the ASP.NET provider model here completely. I'm curious to hear why the person that started this thread would even want to use something other than what came with YAF.
If you have to use the framework, though, there may be some ways to get around it. If you have to implement a profile provider and therefore have to inherit from ProfileBase and ProfileInfo, you should be able to at least avoid calling their base functionality to get around all of this. I think I would try to get the profile explicitly using
The last thing that could be done, is to at least isolate things as much as possible by defining things in web.config in such a way that they don't conflict with other applications. For example, using a prefix on the property names which are then mapped to something better. You can then something calling ProfileManager.Providers["YAF"].FindProfilesByUsername() and use a wrapper class around the ProfileBase to make things easier.
<profile>
<providers>
<add name="YAF" ... />
</providers>
<properties>
<group name="YAF">
<add name="YAF_AIM" provider="YAF" ... />
<add name="YAF_Birthday" provider="YAF" ... />
...
</group>
</properties>
</profile>
class YafProfile
{
private BaseProfile _profile;
public BaseProfile { get { return _profile; } }
YafProfile(ProfileBase p) { _profile = p; }
public string AIM
{
get { return (string)_profile.GetPropertyValue("YAF_AIM"); }
set { _profile.SetPropertyValue("YAF_AIM", value);
}
public string Birthday
{
get { return (string)_profile.GetPropertyValue("YAF_Birthday"); }
set { _profile.SetPropertyValue("YAF_Birthday", value);
}
...
}
I think my favorite solution would be to using private reflection to populate the properties metadata programmatically, though, and still use some wrapper class to make it nicer. I wouldn't ever count on the provider to return anything but ProfileBase.
So what am I trying to get out of this rambling?? I'm trying to come up with a set of "best practices" for application developers who might be writing things that would be integrated into another ASP.NET site. Here is what I have:
1. Use the bare minimum number of functions of the providers that you need to do your job. Don't be tempted to use everything just because it is there!
2. If you are going to use the provider model, don't rebuild things that are already there, such as user or role maintenance. The ASP.NET tools are available to do administration of the default providers, and those of us with custom providers probably have our own tools already.
3. If you insist on ignoring 2, provide a configuration option in your application to indicate whether or not the Membership and Role providers are read only. If they are, disable editing functionality in your application.
4. Never assume that the default provider is for you to use, especially if your application requires a special provider. Use a well-known named provider, allowing that name to be configured if you think that is necessary. This would require you to do something like Roles.Providers["YAF"].IsUserInRole() instead of Profile.IsUserInRole() in the code. Really not the big of a deal in the grand scheme of things.
5. Do not use automatically generated profile properties through the if possible. Build your own functionality, if possible. If you must use the collection, prefix your values so they do not conflict with another application.6. Do not require the "inherits" attribute of the configuration. It could conflict with another application.Here are some other things I look for just in general.
7. Don't distribute code-behind files.
8. Uses a custom configuration section, or prefix all keys in appConfig with the application name.
9. If something goes into App_Data, put it in a sub-directory named after the application.
10. Don't require anything in App_Code.
11. Put things you want in a "known" location in a subdirectory of App_Resources named after the application.
I'd love to hear comments or any discussion about the providers as they relate to a project like this being integrated into an existing site. :)
Joel