Реализация собственного провайдера профилей в ASP.NET MVC - PullRequest
6 голосов
/ 07 мая 2009

Я много пытался реализовать провайдер настраиваемого профиля в ASP.NET MVC. Я прочитал много-много уроков, но не могу найти, где моя проблема. Это очень похоже на Реализация поставщика профилей в ASP.NET MVC .

Но я хотел бы создать свой собственный поставщик профилей, поэтому я написал следующий класс, который наследуется от ProfileProvider:

public class UserProfileProvider : ProfileProvider
{
    #region Variables
    public override string ApplicationName { get; set; }
    public string ConnectionString { get; set; }
    public string UpdateProcedure { get; set; }
    public string GetProcedure { get; set; }
    #endregion

    #region Methods
    public UserProfileProvider()
    {  }

    internal static string GetConnectionString(string specifiedConnectionString)
    {
        if (String.IsNullOrEmpty(specifiedConnectionString))
            return null;

        // Check <connectionStrings> config section for this connection string
        ConnectionStringSettings connObj = ConfigurationManager.ConnectionStrings[specifiedConnectionString];
        if (connObj != null)
            return connObj.ConnectionString;

        return null;
    }
    #endregion

    #region ProfileProvider Methods Implementation
    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
    {
        if (config == null)
            throw new ArgumentNullException("config");

        if (String.IsNullOrEmpty(name))
            name = "UserProfileProvider";

        if (String.IsNullOrEmpty(config["description"]))
        {
            config.Remove("description");
            config.Add("description", "My user custom profile provider");
        }

        base.Initialize(name, config);

        if (String.IsNullOrEmpty(config["connectionStringName"]))
            throw new ProviderException("connectionStringName not specified");

        ConnectionString = GetConnectionString(config["connectionStringName"]);

        if (String.IsNullOrEmpty(ConnectionString))
            throw new ProviderException("connectionStringName not specified");


        if ((config["applicationName"] == null) || String.IsNullOrEmpty(config["applicationName"]))
            ApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
        else
            ApplicationName = config["applicationName"];

        if (ApplicationName.Length > 256)
            throw new ProviderException("Application name too long");

        UpdateProcedure = config["updateUserProcedure"];
        if (String.IsNullOrEmpty(UpdateProcedure))
            throw new ProviderException("updateUserProcedure not specified");

        GetProcedure = config["getUserProcedure"];
        if (String.IsNullOrEmpty(GetProcedure))
            throw new ProviderException("getUserProcedure not specified");
    }

    public override System.Configuration.SettingsPropertyValueCollection GetPropertyValues(System.Configuration.SettingsContext context, System.Configuration.SettingsPropertyCollection collection)
    {
        SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();

        SqlConnection myConnection = new SqlConnection(ConnectionString);
        SqlCommand myCommand = new SqlCommand(GetProcedure, myConnection);
        myCommand.CommandType = CommandType.StoredProcedure;

        myCommand.Parameters.AddWithValue("@FirstName", (string)context["FirstName"]);

        try
        {
            myConnection.Open();
            SqlDataReader reader = myCommand.ExecuteReader(CommandBehavior.SingleRow);

            reader.Read();

            foreach (SettingsProperty property in collection)
            {
                SettingsPropertyValue value = new SettingsPropertyValue(property);

                if (reader.HasRows)
                {
                    value.PropertyValue = reader[property.Name];
                    values.Add(value);
                }
            }

        }
        finally
        {
            myConnection.Close();
            myCommand.Dispose();
        }

        return values;
    }

    public override void SetPropertyValues(System.Configuration.SettingsContext context, System.Configuration.SettingsPropertyValueCollection collection)
    {
        SqlConnection myConnection = new SqlConnection(ConnectionString);
        SqlCommand myCommand = new SqlCommand(UpdateProcedure, myConnection);
        myCommand.CommandType = CommandType.StoredProcedure;

        foreach (SettingsPropertyValue value in collection)
        {
            myCommand.Parameters.AddWithValue(value.Name, value.PropertyValue);
        }

        myCommand.Parameters.AddWithValue("@FirstName", (string)context["FirstName"]);

        try
        {
            myConnection.Open();
            myCommand.ExecuteNonQuery();
        }

        finally
        {
            myConnection.Close();
            myCommand.Dispose();
        }
    }

Вот мое действие CreateProfile в моем контроллере:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateProfile(string Username, string Password, string FirstName, string LastName)
{
    MembershipCreateStatus IsCreated = MembershipCreateStatus.ProviderError;
    MembershipUser user = null;

    user = Membership.CreateUser(Username, Password, "test@test.com", "Q", "A", true, out IsCreated);

    if (IsCreated == MembershipCreateStatus.Success && user != null)
    {
        ProfileCommon profile = (ProfileCommon)ProfileBase.Create(user.UserName);

        profile.FirstName = FirstName;
        profile.LastName = LastName;
        profile.Save();
    }

    return RedirectToAction("Index", "Home");
}

Моя процедура usp_GetUserProcedure ничего особенного:

ALTER PROCEDURE [dbo].[usp_GetUserProcedure] 
-- Add the parameters for the stored procedure here
@FirstName varchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

-- Insert statements for procedure here
SELECT * FROM dbo.Users WHERE FirstName = @FirstName
END

И мой файл Web.Config:

<profile enabled="true"
         automaticSaveEnabled="false"
         defaultProvider="UserProfileProvider"
         inherits="Test.Models.ProfileCommon">
<providers>
<clear/>
<add name="UserProfileProvider"
         type="Test.Controllers.UserProfileProvider"
         connectionStringName="ApplicationServices"
         applicationName="UserProfileProvider"
         getUserProcedure="usp_GetUserProcedure"
         updateUserProcedure="usp_UpdateUserProcedure"/>
</providers>
</profile>

Но я всегда получаю это исключение:

Процедура или функция «usp_GetUserProcedure» ожидает параметр «@FirstName», который не был предоставлен.

Есть мысли о том, что я могу делать неправильно?

Ответы [ 2 ]

2 голосов
/ 03 августа 2009

Наиболее вероятная причина в том, что

myCommand.Parameters.AddWithValue("@FirstName", (string)context["FirstName"]);

(string)context["FirstName"] является нулевым значением. Даже если вы передадите параметр в sproc, если значение для обязательного параметра равно нулю, вы увидите эту ошибку. SQL Server (эффективно) не различает параметр, который не был передан, и параметр, переданный со значением NULL.

Вы видите ошибку SQL. Это не связано с MVC, и MVC на самом деле не вызывает ваших проблем. Определите, является ли значение NULL действительным значением context["FirstName"], и если да, измените свою функцию, чтобы она принимала значения NULL. Если нет, выясните, почему context["FirstName"] равно нулю.

Кроме того, я не думаю, что в этой строке будут правильно добавлены имена ваших параметров (с префиксом "@").

myCommand.Parameters.AddWithValue (value.Name, value.PropertyValue);

Кроме того, поскольку это MVC, убедитесь, что у вас есть элемент управления с именем Имя в форме, отправляемой на:

public ActionResult CreateProfile(string Username, string Password, string FirstName, string LastName)

Читает поля на основе имени, а не ID

0 голосов
/ 07 мая 2009

Да, это потому, что я использую класс для своих свойств, ProfileCommon, который наследуется от ProfileBase.

public class ProfileCommon : ProfileBase
{
public virtual string Label
{
    get
    {
        return ((string)(this.GetPropertyValue("Label")));
    }
    set
    {
        this.SetPropertyValue("Label", value);
    }
}

public virtual string FirstName
{
    get
    {
        return ((string)(this.GetPropertyValue("FirstName")));
    }
    set
    {
        this.SetPropertyValue("FirstName", value);
    }
}

public virtual string LastName
{
    get
    {
        return ((string)(this.GetPropertyValue("LastName")));
    }
    set
    {
        this.SetPropertyValue("LastName", value);
    }
}

public virtual ProfileCommon GetProfile(string username)
{
    return Create(username) as ProfileCommon;
}
}

Вы видите, что я использую этот класс в файле Web.Config:

<profile enabled="true"
     automaticSaveEnabled="false"
     defaultProvider="UserProfileProvider"
     inherits="Test.Models.ProfileCommon">
[...]

А в ASP.Net MVC, если я написал свои свойства в Web.Config, я больше не смогу получить к ним доступ, используя Profile.PropertyName. Может быть, есть способ, но я не нахожу примеров.

...