zoukankan      html  css  js  c++  java
  • 怎么在MVC中使用自定义Membership

    首先我们来看看微软自带的membership:

    我们打开系统下aspnet_regsql.exe 地址一般位于:

    C:WINDOWSMicrosoft.NETFrameworkv2.0.50727  如果没问题一般都是在这个目录下面如果framework里面有多个版本可以选择V2.0以上版本即可

                           

    我装的window7打开的是framework v4.0 图片如下:

     

    我们点击下一步:

     

    默认下一步:

     

    这一步我们选择登录方式然后选择数据库下一步

     

    我选中了agebook 数据 然后我用sql server服务管理器打开看看是不是自动给我们生成了一些表。自带的还是不少表 但是我们为了要使用自己的会员表所以只是给大家看看了 我个人认为自带的不好用。

    我们要自定义Membership类所以还是自己定义一个用户表吧

     

    这个表待会儿我们会用到:

    首先我们用VS2012创建一个MVC应用程序

     

    我们先看看MembershipProvider这个系统自带的方法

    using System;
    using System.Configuration.Provider;
    using System.Runtime;
    using System.Runtime.CompilerServices;
    using System.Web.Configuration;
    
    namespace System.Web.Security
    {
    	// 摘要:
    	//     定义 ASP.NET 为使用自定义成员资格提供程序提供成员资格服务而实现的协定。
    	[TypeForwardedFrom("System.Web, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    	public abstract class MembershipProvider : ProviderBase
    	{
    		// 摘要:
    		//     初始化 System.Web.Security.MembershipProvider 类的新实例。
    		[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    		protected MembershipProvider();
    
    		// 摘要:
    		//     使用自定义成员资格提供程序的应用程序的名称。
    		//
    		// 返回结果:
    		//     使用自定义成员资格提供程序的应用程序的名称。
    		public abstract string ApplicationName { get; set; }
    		//
    		// 摘要:
    		//     指示成员资格提供程序是否配置为允许用户重置其密码。
    		//
    		// 返回结果:
    		//     如果成员资格提供程序支持密码重置,则为 true;否则为 false。默认值为 true。
    		public abstract bool EnablePasswordReset { get; }
    		//
    		// 摘要:
    		//     指示成员资格提供程序是否配置为允许用户检索其密码。
    		//
    		// 返回结果:
    		//     如果成员资格提供程序配置为支持密码检索,则为 true,否则为 false。默认值为 false。
    		public abstract bool EnablePasswordRetrieval { get; }
    		//
    		// 摘要:
    		//     获取锁定成员资格用户前允许的无效密码或无效密码提示问题答案尝试次数。
    		//
    		// 返回结果:
    		//     锁定成员资格用户之前允许的无效密码或无效密码提示问题答案尝试次数。
    		public abstract int MaxInvalidPasswordAttempts { get; }
    		//
    		// 摘要:
    		//     获取有效密码中必须包含的最少特殊字符数。
    		//
    		// 返回结果:
    		//     有效密码中必须包含的最少特殊字符数。
    		public abstract int MinRequiredNonAlphanumericCharacters { get; }
    		//
    		// 摘要:
    		//     获取密码所要求的最小长度。
    		//
    		// 返回结果:
    		//     密码所要求的最小长度。
    		public abstract int MinRequiredPasswordLength { get; }
    		//
    		// 摘要:
    		//     获取在锁定成员资格用户之前允许的最大无效密码或无效密码提示问题答案尝试次数的分钟数。
    		//
    		// 返回结果:
    		//     在锁定成员资格用户之前允许的最大无效密码或无效密码提示问题答案尝试次数的分钟数。
    		public abstract int PasswordAttemptWindow { get; }
    		//
    		// 摘要:
    		//     获取一个值,该值指示在成员资格数据存储区中存储密码的格式。
    		//
    		// 返回结果:
    		//     System.Web.Security.MembershipPasswordFormat 值之一,该值指示在数据存储区中存储密码的格式。
    		public abstract MembershipPasswordFormat PasswordFormat { get; }
    		//
    		// 摘要:
    		//     获取用于计算密码的正则表达式。
    		//
    		// 返回结果:
    		//     用于计算密码的正则表达式。
    		public abstract string PasswordStrengthRegularExpression { get; }
    		//
    		// 摘要:
    		//     获取一个值,该值指示成员资格提供程序是否配置为要求用户在进行密码重置和检索时回答密码提示问题。
    		//
    		// 返回结果:
    		//     如果密码重置和检索需要提供密码提示问题答案,则为 true;否则为 false。默认值为 true。
    		public abstract bool RequiresQuestionAndAnswer { get; }
    		//
    		// 摘要:
    		//     获取一个值,指示成员资格提供程序是否配置为要求每个用户名具有唯一的电子邮件地址。
    		//
    		// 返回结果:
    		//     如果成员资格提供程序要求唯一的电子邮件地址,则返回 true;否则返回 false。默认值为 true。
    		public abstract bool RequiresUniqueEmail { get; }
    
    		// 摘要:
    		//     在创建用户、更改密码或重置密码时发生。
    		public event MembershipValidatePasswordEventHandler ValidatingPassword;
    
    		// 摘要:
    		//     处理更新成员资格用户密码的请求。
    		//
    		// 参数:
    		//   username:
    		//     为其更新密码的用户。
    		//
    		//   oldPassword:
    		//     指定的用户的当前密码。
    		//
    		//   newPassword:
    		//     指定的用户的新密码。
    		//
    		// 返回结果:
    		//     如果密码更新成功,则为 true;否则为 false。
    		public abstract bool ChangePassword(string username, string oldPassword, string newPassword);
    		//
    		// 摘要:
    		//     处理更新成员资格用户的密码提示问题和答案的请求。
    		//
    		// 参数:
    		//   username:
    		//     要为其更改密码提示问题和答案的用户。
    		//
    		//   password:
    		//     指定的用户的密码。
    		//
    		//   newPasswordQuestion:
    		//     指定的用户的新密码提示问题。
    		//
    		//   newPasswordAnswer:
    		//     指定的用户的新密码提示问题答案。
    		//
    		// 返回结果:
    		//     如果成功更新密码提示问题和答案,则为 true;否则,为 false。
    		public abstract bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer);
    		//
    		// 摘要:
    		//     将新的成员资格用户添加到数据源。
    		//
    		// 参数:
    		//   username:
    		//     新用户的用户名。
    		//
    		//   password:
    		//     新用户的密码。
    		//
    		//   email:
    		//     新用户的电子邮件地址。
    		//
    		//   passwordQuestion:
    		//     新用户的密码提示问题。
    		//
    		//   passwordAnswer:
    		//     新用户的密码提示问题答案。
    		//
    		//   isApproved:
    		//     是否允许验证新用户。
    		//
    		//   providerUserKey:
    		//     成员资格数据源中该用户的唯一标识符。
    		//
    		//   status:
    		//     一个 System.Web.Security.MembershipCreateStatus 枚举值,指示是否已成功创建用户。
    		//
    		// 返回结果:
    		//     一个用新创建的用户的信息填充的 System.Web.Security.MembershipUser 对象。
    		public abstract MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status);
    		//
    		// 摘要:
    		//     解密已加密的密码。
    		//
    		// 参数:
    		//   encodedPassword:
    		//     一个字节数组,包含要解密的加密密码。
    		//
    		// 返回结果:
    		//     包含已解密密码的字节数组。
    		//
    		// 异常:
    		//   System.Configuration.Provider.ProviderException:
    		//     将 System.Web.Configuration.MachineKeySection.ValidationKey 属性或 System.Web.Configuration.MachineKeySection.DecryptionKey
    		//     属性设置为 AutoGenerate。
    		protected virtual byte[] DecryptPassword(byte[] encodedPassword);
    		//
    		// 摘要:
    		//     从成员资格数据源删除一个用户。
    		//
    		// 参数:
    		//   username:
    		//     要删除的用户的名称。
    		//
    		//   deleteAllRelatedData:
    		//     如果为 true,则从数据库中删除与该用户相关的数据;如果为 false,则将与该用户相关的数据保留在数据库。
    		//
    		// 返回结果:
    		//     如果用户被成功删除,则为 true;否则为 false。
    		public abstract bool DeleteUser(string username, bool deleteAllRelatedData);
    		//
    		// 摘要:
    		//     对密码进行加密。
    		//
    		// 参数:
    		//   password:
    		//     包含要加密的密码的字节数组。
    		//
    		// 返回结果:
    		//     包含已加密的密码的字节数组。
    		//
    		// 异常:
    		//   System.Configuration.Provider.ProviderException:
    		//     将 System.Web.Configuration.MachineKeySection.ValidationKey 属性或 System.Web.Configuration.MachineKeySection.DecryptionKey
    		//     属性设置为 AutoGenerate。
    		[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    		protected virtual byte[] EncryptPassword(byte[] password);
    		//
    		// 摘要:
    		//     使用指定的密码兼容性模式对指定密码进行加密。
    		//
    		// 参数:
    		//   password:
    		//     包含要加密的密码的字节数组。
    		//
    		//   legacyPasswordCompatibilityMode:
    		//     成员资格密码兼容性模式。
    		//
    		// 返回结果:
    		//     包含已加密的密码的字节数组。
    		protected virtual byte[] EncryptPassword(byte[] password, MembershipPasswordCompatibilityMode legacyPasswordCompatibilityMode);
    		//
    		// 摘要:
    		//     获取一个成员资格用户的集合,其中的电子邮件地址包含要匹配的指定电子邮件地址。
    		//
    		// 参数:
    		//   emailToMatch:
    		//     要搜索的电子邮件地址。
    		//
    		//   pageIndex:
    		//     要返回的结果页的索引。pageIndex 从零开始。
    		//
    		//   pageSize:
    		//     要返回的结果页的大小。
    		//
    		//   totalRecords:
    		//     匹配用户的总数。
    		//
    		// 返回结果:
    		//     包含一页 pageSizeSystem.Web.Security.MembershipUser 对象的 System.Web.Security.MembershipUserCollection
    		//     集合,这些对象从 pageIndex 指定的页开始。
    		public abstract MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords);
    		//
    		// 摘要:
    		//     获取一个成员资格用户的集合,其中的用户名包含要匹配的指定用户名。
    		//
    		// 参数:
    		//   usernameToMatch:
    		//     要搜索的用户名。
    		//
    		//   pageIndex:
    		//     要返回的结果页的索引。pageIndex 从零开始。
    		//
    		//   pageSize:
    		//     要返回的结果页的大小。
    		//
    		//   totalRecords:
    		//     匹配用户的总数。
    		//
    		// 返回结果:
    		//     包含一页 pageSizeSystem.Web.Security.MembershipUser 对象的 System.Web.Security.MembershipUserCollection
    		//     集合,这些对象从 pageIndex 指定的页开始。
    		public abstract MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords);
    		//
    		// 摘要:
    		//     获取数据源中的所有用户的集合,并显示在数据页中。
    		//
    		// 参数:
    		//   pageIndex:
    		//     要返回的结果页的索引。pageIndex 从零开始。
    		//
    		//   pageSize:
    		//     要返回的结果页的大小。
    		//
    		//   totalRecords:
    		//     匹配用户的总数。
    		//
    		// 返回结果:
    		//     包含一页 pageSizeSystem.Web.Security.MembershipUser 对象的 System.Web.Security.MembershipUserCollection
    		//     集合,这些对象从 pageIndex 指定的页开始。
    		public abstract MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords);
    		//
    		// 摘要:
    		//     获取当前访问该应用程序的用户数。
    		//
    		// 返回结果:
    		//     当前访问该应用程序的用户数。
    		public abstract int GetNumberOfUsersOnline();
    		//
    		// 摘要:
    		//     从数据源获取指定用户名所对应的密码。
    		//
    		// 参数:
    		//   username:
    		//     为其检索密码的用户。
    		//
    		//   answer:
    		//     用户的密码提示问题答案。
    		//
    		// 返回结果:
    		//     指定用户名所对应的密码。
    		public abstract string GetPassword(string username, string answer);
    		//
    		// 摘要:
    		//     根据成员资格用户的唯一标识符从数据源获取用户信息。提供一个更新用户最近一次活动的日期/时间戳的选项。
    		//
    		// 参数:
    		//   providerUserKey:
    		//     要获取其信息的成员资格用户的唯一标识符。
    		//
    		//   userIsOnline:
    		//     如果为 true,则更新用户最近一次活动的日期/时间戳;如果为 false,则返回用户信息,但不更新用户最近一次活动的日期/时间戳。
    		//
    		// 返回结果:
    		//     用数据源中指定用户的信息填充的 System.Web.Security.MembershipUser 对象。
    		public abstract MembershipUser GetUser(object providerUserKey, bool userIsOnline);
    		//
    		// 摘要:
    		//     从数据源获取用户的信息。提供一个更新用户最近一次活动的日期/时间戳的选项。
    		//
    		// 参数:
    		//   username:
    		//     要获取其信息的用户名。
    		//
    		//   userIsOnline:
    		//     如果为 true,则更新用户最近一次活动的日期/时间戳;如果为 false,则返回用户信息,但不更新用户最近一次活动的日期/时间戳。
    		//
    		// 返回结果:
    		//     用数据源中指定用户的信息填充的 System.Web.Security.MembershipUser 对象。
    		public abstract MembershipUser GetUser(string username, bool userIsOnline);
    		//
    		// 摘要:
    		//     获取与指定的电子邮件地址关联的用户名。
    		//
    		// 参数:
    		//   email:
    		//     要搜索的电子邮件地址。
    		//
    		// 返回结果:
    		//     与指定的电子邮件地址关联的用户名。如果未找到匹配项,则返回 null。
    		public abstract string GetUserNameByEmail(string email);
    		//
    		// 摘要:
    		//     如果定义了事件处理程序,则引发 System.Web.Security.MembershipProvider.ValidatingPassword
    		//     事件。
    		//
    		// 参数:
    		//   e:
    		//     传递给 System.Web.Security.MembershipProvider.ValidatingPassword 事件处理程序的 System.Web.Security.ValidatePasswordEventArgs。
    		protected virtual void OnValidatingPassword(ValidatePasswordEventArgs e);
    		//
    		// 摘要:
    		//     将用户密码重置为一个自动生成的新密码。
    		//
    		// 参数:
    		//   username:
    		//     为其重置密码的用户。
    		//
    		//   answer:
    		//     指定的用户的密码提示问题答案。
    		//
    		// 返回结果:
    		//     指定的用户的新密码。
    		public abstract string ResetPassword(string username, string answer);
    		//
    		// 摘要:
    		//     清除锁定,以便可以验证该成员资格用户。
    		//
    		// 参数:
    		//   userName:
    		//     要清除其锁定状态的成员资格用户。
    		//
    		// 返回结果:
    		//     如果成功取消成员资格用户的锁定,则为 true;否则为 false。
    		public abstract bool UnlockUser(string userName);
    		//
    		// 摘要:
    		//     更新数据源中有关用户的信息。
    		//
    		// 参数:
    		//   user:
    		//     一个 System.Web.Security.MembershipUser 对象,表示要更新的用户及其更新信息。
    		public abstract void UpdateUser(MembershipUser user);
    		//
    		// 摘要:
    		//     验证数据源中是否存在指定的用户名和密码。
    		//
    		// 参数:
    		//   username:
    		//     要验证的用户的名称。
    		//
    		//   password:
    		//     指定的用户的密码。
    		//
    		// 返回结果:
    		//     如果指定的用户名和密码有效,则为 true;否则为 false。
    		public abstract bool ValidateUser(string username, string password);
    	}
    }
    

    有这么多 方法 我们的写它因为要自定义一个自己的Membership类如下:

    using System;

    using System.Data;

    using System.Configuration;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    using System.Data.OleDb;

    using System.Data.SqlClient;

     

     

     

    /// <summary>

    /// MyMemberShip 的摘要说明

    /// </summary>

    public class MyMemberShip : MembershipProvider

    {

        public MyMemberShip()

        {

            //

            // TODO: 在此处添加构造函数逻辑

            //

        }

     

        string connectionstring = ConfigurationManager.ConnectionStrings["LocalSqlServer"].ConnectionString.ToString();

        private bool _requiresQuestionAndAnswer;

        private int _minRequiredPasswordLength;

     

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)

        {

            if (config["requiresQuestionAndAnswer"].ToLower() == "true")

            {

                _requiresQuestionAndAnswer = true;

            }

            else

            {

                _requiresQuestionAndAnswer = false;

            }

            int.TryParse(config["minRequiredPasswordLength"], out _minRequiredPasswordLength);

            //connStr = config["connectionString"];

            base.Initialize(name, config);

        }

     

        public override string ApplicationName

        {

            get

            {

                throw new Exception("The method or operation is not implemented.");

            }

            set

            {

                throw new Exception("The method or operation is not implemented.");

            }

        }

     

        public override bool ChangePassword(string username, string oldPassword, string newPassword)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override MembershipUser CreateUser(string username,string password,string email,string passwordQuestion,string passwordAnswer,bool isApproved,Object providerUserKey,out MembershipCreateStatus status)

        {

            using (SqlConnection conn = new SqlConnection(connectionstring))

            {

                SqlCommand comm = new SqlCommand();

                comm.CommandText = "insert into users(u_name,u_pwd,u_role) values(@cname,@cpwd,@crole)";

                comm.Parameters.AddWithValue("@cname", username);

                comm.Parameters.AddWithValue("@cpwd", password);

                comm.Parameters.AddWithValue("@crole", "guest");

                comm.Connection = conn;

                conn.Open();

                comm.ExecuteNonQuery();

                MembershipUser user = new MembershipUser("MyMemberShip", username, providerUserKey, email, passwordQuestion, "", isApproved, true, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);

                status = MembershipCreateStatus.Success;

                return user;

            }

     

        }

     

        public override bool DeleteUser(string username, bool deleteAllRelatedData)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override bool EnablePasswordReset

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override bool EnablePasswordRetrieval

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override int GetNumberOfUsersOnline()

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override string GetPassword(string username, string answer)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override MembershipUser GetUser(string username, bool userIsOnline)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override string GetUserNameByEmail(string email)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override int MaxInvalidPasswordAttempts

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override int MinRequiredNonAlphanumericCharacters

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override int MinRequiredPasswordLength

        {

            get { return _minRequiredPasswordLength; }

        }

     

        public override int PasswordAttemptWindow

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override MembershipPasswordFormat PasswordFormat

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override string PasswordStrengthRegularExpression

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override bool RequiresQuestionAndAnswer

        {

            get { return _requiresQuestionAndAnswer; }

        }

     

        public override bool RequiresUniqueEmail

        {

            get { throw new Exception("The method or operation is not implemented."); }

        }

     

        public override string ResetPassword(string username, string answer)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override bool UnlockUser(string userName)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override void UpdateUser(MembershipUser user)

        {

            throw new Exception("The method or operation is not implemented.");

        }

     

        public override bool ValidateUser(string username, string password)

        {

            using (SqlConnection conn = new SqlConnection(connectionstring))

            {

                SqlCommand comm = new SqlCommand();

                comm.CommandText = "select count(0) from users where u_name=@cname and u_pwd=@cpwd";

                comm.Parameters.AddWithValue("@cname", username);

                comm.Parameters.AddWithValue("@cpwd", password);

                comm.Connection = conn;

                conn.Open();

                return ((int)comm.ExecuteScalar()) > 0 ? true : false;

            }

        }

    }

    这个类里面包括了很多验证的方法具体我就不介绍了

    接下来我们看看配置文件怎么配置:

    <configuration>下

        <connectionStrings>

            <remove name="LocalSqlServer" />

            <add name="LocalSqlServer" connectionString="Data Source=.;Initial Catalog=agebook;Persist Security Info=True;User ID=sa;Password=sa" providerName="System.Data.SqlClient" />

        </connectionStrings>

    记着加入forms验证

        <compilation debug="true" targetFramework="4.0" />

        <!--<authentication mode="Windows" />-->

          <authentication mode="Forms"/>

        <authorization>

          <allow users="*" />

    </authorization>

    也可以这样:

    <authentication mode="Forms">

     <forms loginUrl="/Home/Login" name="login"/>

    </authentication>

    配置MemberShip节点

      <system.web>内配置:

          <membership defaultProvider="MyMemberShip" userIsOnlineTimeWindow="20">

              <providers>

                  <remove name="AspNetSqlProvider" />

                  <add name="MyMemberShip"

                    type="MyMemberShip"

                    connectionStringName="SqlServices"

                    enablePasswordRetrieval="false"

                    enablePasswordReset="true"

                    requiresQuestionAndAnswer="true"

                    passwordFormat="Hashed"

                    applicationName="/" />

              </providers>

          </membership>

    <profile>

    <providers>

    <clear/>

    <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName=" LocalSqlServer "  applicationName="/" />

    </providers>

    </profile>

    最后我们在测试一下登录页面

    <form action='/Home/Login' method="post">

    用户名:@Html.TextBox("Username")

    密码:@Html.Password("Password")

    <input type="submit" value="登录" />

    </form>

    控制页面代码如下:

                string name = Request.Form["Username"];

                string pwd = Request.Form["Password"];

                if (name == "" || name == null)

                    name = "t";

                if (pwd == "" || pwd == null)

                    pwd = "s";

     

                if (Membership.ValidateUser(name, pwd))

                {

     

                    Response.Write("您的登录名称:" + User.Identity.Name + " <br>权限为:");

                    foreach (string s in Roles.GetRolesForUser(name))

                    {

                        Response.Write("<li>" + s + "</li>");

                    }

                }

    是不是感觉给自己写的登录方法差不多吧!自带的用户登录控件就带MemberShip包括以前微软最有名的一个事例 PetShop4.0也用了它,一般控件开发用的比较多些,现在不知道用的人还多不多,最近有人问我Membership这问题所以就总结一下,希望对大家有所帮助。

  • 相关阅读:
    C# CSV文件读写
    C# 串口通信
    VS项目平台的x86,x64,Any CPU区分
    TcpClient 简单通讯示例
    大龄屌丝自学笔记Java零基础到菜鸟015
    大龄屌丝自学笔记Java零基础到菜鸟011
    大龄屌丝自学笔记Java零基础到菜鸟018
    大龄屌丝自学笔记Java零基础到菜鸟019
    大龄屌丝自学笔记Java零基础到菜鸟017
    大龄屌丝自学笔记Java零基础到菜鸟013
  • 原文地址:https://www.cnblogs.com/angelasp/p/4078244.html
Copyright © 2011-2022 走看看