The problem
Membership.GetUser works fine if I use the GUID, but fails if I try it without parameters or with the name:
//This works and adds records to aspnet_user & aspnet_Membership tables
MembershipUser membershipUser = Membership.CreateUser(_name, _pwd, _email, null, null, true, null, out createStatus);
//These fail with an InvalidCastException
MembershipUser membershipUser2 = Membership.GetUser(_name, true);
MembershipUser membershipUser3 = Membership.GetUser();
//If I go look up the GUID for this user (userId), this works!
MembershipUser membershipUser4 = Membership.GetUser(new Guid("E1428CD3-CF17-494E-AB77-CF8F6010F585"), true);
Other Mysteries
//This works
Int32 count = Membership.GetNumberOfUsersOnline();
//this fails (but totalRecords has the right value)
Int32 totalRecords;
MembershipUserCollection col = Membership.GetAllUsers(0,100, out totalRecords);
The Setup
- MVC
- .NET 4.0
- Default SQL Membership Provider (no custom or overrides)
ex:
<membership defaultProvider="SqlProvider">
<providers>
<clear />
<add name="SqlProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="MyDB"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
applicationName="myapp"
requiresUniqueEmail="false"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="2"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
passwordStrengthRegularExpression="" />
</providers>
</membership>
- Standard aspnet_* database tables with no customization or extra keys
- aspnet_Membership_GetAllUsers runs fine manually from the DB. Redirected output shows the expected results are being returned
- aspnet_Membership_GetUserByName is working as well from the DB
Exception Details
Specified cast is not valid.
at System.Data.SqlClient.SqlBuffer.get_SqlGuid()
at System.Data.SqlClient.SqlDataReader.GetGuid(Int32 i)
at System.Web.Security.SqlMembershipProvider.GetUser(String username, Boolean userIsOnline)
at System.Web.Security.Membership.GetUser(String username, Boolean userIsOnline)
Thoughts
It's almost like there's something wrong inside the Membership.GetUser method. I reflected out the code for the System.Web.Security.SqlMembershipProvider.GetUser for my System.Web.dll (version 4.0) and I get the following:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
SecUtility.CheckParameter(ref username, true, false, true, 256, "username");
SqlDataReader reader = (SqlDataReader) null;
try
{
SqlConnectionHolder connectionHolder = (SqlConnectionHolder) null;
try
{
connectionHolder = SqlConnectionHelper.GetConnection(this._sqlConnectionString, true);
this.CheckSchemaVersion(connectionHolder.Connection);
SqlCommand sqlCommand = new SqlCommand("dbo.aspnet_Membership_GetUserByName", connectionHolder.Connection);
sqlCommand.CommandTimeout = this.CommandTimeout;
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.Add(this.CreateInputParam("@ApplicationName", SqlDbType.NVarChar, (object) this.ApplicationName));
sqlCommand.Parameters.Add(this.CreateInputParam("@UserName", SqlDbType.NVarChar, (object) username));
sqlCommand.Parameters.Add(this.CreateInputParam("@UpdateLastActivity", SqlDbType.Bit, (object) (bool) (userIsOnline ? 1 : 0)));
sqlCommand.Parameters.Add(this.CreateInputParam("@CurrentTimeUtc", SqlDbType.DateTime, (object) DateTime.UtcNow));
SqlParameter sqlParameter = new SqlParameter("@ReturnValue", SqlDbType.Int);
sqlParameter.Direction = ParameterDirection.ReturnValue;
sqlCommand.Parameters.Add(sqlParameter);
reader = sqlCommand.ExecuteReader();
if (!reader.Read())
return (MembershipUser) null;
string nullableString1 = this.GetNullableString(reader, 0);
string nullableString2 = this.GetNullableString(reader, 1);
string nullableString3 = this.GetNullableString(reader, 2);
bool boolean1 = reader.GetBoolean(3);
DateTime creationDate = reader.GetDateTime(4).ToLocalTime();
DateTime lastLoginDate = reader.GetDateTime(5).ToLocalTime();
DateTime lastActivityDate = reader.GetDateTime(6).ToLocalTime();
DateTime lastPasswordChangedDate = reader.GetDateTime(7).ToLocalTime();
Guid guid = reader.GetGuid(8);
bool boolean2 = reader.GetBoolean(9);
DateTime lastLockoutDate = reader.GetDateTime(10).ToLocalTime();
return new MembershipUser(this.Name, username, (object) guid, nullableString1, nullableString2, nullableString3, boolean1, boolean2, creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate);
}
finally
{
if (reader != null)
reader.Close();
if (connectionHolder != null)
connectionHolder.Close();
}
}
catch
{
throw;
}
}
Next Step
I'm hoping to get some direction from the SO crowd on where I should go next. I figure I could either override this method and put together my own provider just to debug it, or I could just go around the darned thing and call out to the DB myself directly. This seems like a lot of heartache from some basic DB CRUD.
Current Status
- 3/5/2013: I discovered this morning that my
aspnet_Users
table has the UserId
as an nvarchar(256)
instead of a uniqueidentifier
. Perhaps the SqlDataReader.GetGuid()
is choking there. I'll run some tests this evening to see if that's the problem. I'm wondering if my table structure is outdated, because online documentation shows this field as a uniqueidentifier
.