smithvoice.com
 Y'herd thisun? 

“Soup to Nuts, a complete imaging control and app and all the code that does it (plus a free ebook if you want it). Now that C# does optional parameters this is a simple port for those who aren't bilingual.”

from right here by smith

Hack the Profile Provider

TaggedCoding, CSharp, ASP.Net

You don't want to reinvent the wheel on an ASP.Net profile system but you need to support some perfectly understandable business rules for the profile data that the precooked Profile Provider doesn't handle well?

No problem, you create your own Profile Provider based on the System.Web.Profile.ProfileProvider class, create tables for data to be saved as data instead of that weird serial string, write your own code in the required methods and SPROCs and off you go.

Yeah... but what if one of those rules requires a lookup validation and message to the user?  Your custom implemented Save() has to return void,  a status code won't work and a custom exception from SetPropertyValues will choke your web site.

So what if one of those rules is that there can be no duplication of values; like one of your properties is a screen name used in forums or reviews so you don't want to have everyone named Joseph to use the handle "Joe".

Well, there's a hack for that.

The base System.Web.Profile.ProfileProvider will allow you to throw exceptions... sort of.  You can't just create any old custom exception and toss it up from SetPropertyValues method, it will go up the internal pipe that is coded for only handling SQLExceptions and thus be unhandled and out of reach of your calling code.  Users will get a Yellow Screen.

You could set a handler in your web.config and redirect but that's no help to your users.

The key lies in the accepted SQLException. 

Just toss a new System.Data exception such as System.Data.DuplicateNameException and fill its innerExcpetion with your custom exception (or be lazy and put a code const in the message property, you're already hacking so why not go all the way? <g>).  Then put a try...catch around the call to Save(), check for the System.Data.DuplicateNameException and test its innerException (or lazy message).


Like this:


SPROC:


USE [myDBName]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[ProfileData_Upd]
(
@userName nvarchar(50),
@applicationName nvarchar(50),
@isAnonymous bit,
@screenName nvarchar(256)
)
As
   
BEGIN
DECLARE @RetVal int 
SET @RetVal = 0
SET NOCOUNT ON;

/* Determine if Displayname is already being used by another profle */
BEGIN
        IF (EXISTS (SELECT *
           FROM  dbo.ProfileData d, dbo.Profiles s 
           WHERE d.ProfileId = s.ProfileID AND 
  LOWER(s.UserName) <> LOWER(@userName) AND
   LOWER(@screenName) = LOWER(d.screenName) ))
        BEGIN
   Set @RetVal = 1
            RETURN @RetVal
        END
        END

Declare @activity datetime
Set @activity=GetUTCDate()

/*Determine if a profile exists, if not create one.*/
Declare @profileID int
Execute Profiles_GetProfileID @profileID output, @userName, @applicationName, @isAnonymous
If @profileID Is Null Begin
 Execute Profiles_Ins @profileID output, @userName, @applicationName, @isAnonymous
End

/*Insert the profile data.*/
Declare @profileDataID int
Set @profileDataID=(Select ProfileDataID From ProfileData Where (ProfileID=@profileID))
If @profileDataID Is Null Begin
Insert ProfileData
(
ProfileID,
screenName
)
Values
(
@profileID,
@screenName
)
End 
        Else Begin
Update ProfileData Set
ScreenName=@screenName
Where ProfileID=@profileID
End

/*Update date/time profile had activity and was updated.*/
Execute Profiles_Activity_Upd @userName, @applicationName, @isAnonymous, @activity, @activity

RETURN @RetVal
END
GO



Custom Profile SetPropertyValues:
...
using System.Configuration
...

        public override void SetPropertyValues(SettingsContext context, 
SettingsPropertyValueCollection settingsPropertyValues)
        {

            string userName = context["UserName"].ToString();
            bool isAuthenticated = Convert.ToBoolean(context["IsAuthenticated"]);


            SqlConnection sqlConnection = new SqlConnection(connectionString);
            SqlCommand sqlCommand = new SqlCommand("ProfileData_Upd", sqlConnection);
            sqlCommand.CommandType = CommandType.StoredProcedure;


            sqlCommand.Parameters.Add("userName", SqlDbType.NVarChar, 50).Value = userName;
            sqlCommand.Parameters.Add("applicationName", SqlDbType.NVarChar, 50).Value = applicationName;
            sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = !isAuthenticated;
            var returnParameter = sqlCommand.Parameters.Add("RetVal", SqlDbType.Int);
            returnParameter.Direction = ParameterDirection.ReturnValue;
                        
            foreach (SettingsPropertyValue settingsPropertyValue in settingsPropertyValues)
            {
                switch (settingsPropertyValue.Property.Name)
                {
                    case "ScreenName":
                        sqlCommand.Parameters.Add("screenName", SqlDbType.NVarChar, 256).Value = 
                            Convert.ToString(settingsPropertyValue.PropertyValue);
                        break;
                    default:
                        throw new Exception("Unsupported property.");
                }
            }

            try
            {
                sqlConnection.Open();
                sqlCommand.ExecuteNonQuery();
                int returnValue = (int)returnParameter.Value;
                if (returnValue == SVProfileProps.PROFILEUPDATESTATUS_DUPLICATEDISPLAYNAME)
                {
                    throw new DuplicateNameException("duplicateDisplayName");
   //geeze you're lazy
                }

            }
            catch (SqlException e)
            {
                throw new Exception(e.Message);
            }
            
            finally
            {
                sqlConnection.Close();
            }
        }



Page code:


        protected void butSave_Click(object sender, EventArgs e)
        {
            var profile = HttpContext.Current.Profile;
                       
            profile.SetPropertyValue(SVProfileProps.PROPERTY_DISPLAYNAME_STRING, txtDN.Text);
            try
            {
                profile.Save();
            }

            catch (System.Data.DuplicateNameException ex)
            {
                if (ex.Message == "duplicateDisplayName")
                {
                    Response.Write("duplicate displayname");
                }
            }
                     
        }



home     who is smith    contact smith     rss feed π
Since 1997 a place for my stuff, and it if helps you too then all the better smithvoice.com