Xmlファイルをロール情報のストアとするカスタムロールプロバイダXmlRoleProviderを作成してみます。まず、プロジェクトとしてはカスタムメンバシッププロバイダーを作る。その1(モデル定義) で作成したCustomProviderというソリューションから引き続いて作業を行うことを想定して記載しています。

1. XmlRoleProviderの仕様

今回はRoleProviderで定義されている機能はサポートしたつもりです(MembershipProviderと較べて実装する機能がすくないので)。カスタムメンバシッププロバイダではApplicationNameごとにユーザを管理することはできませんでしたが、今回は管理手法をリファクタリングして、ApplicationNameごとにロールを管理できるようにしています。

実装されている機能一覧はこちら

ロール プロバイダの実装
http://msdn.microsoft.com/ja-jp/library/8fw7xh74.aspx

2.ロール情報のモデル作成

カスタムメンバシッププロバイダと同様にXmlSerializerでドメインモデルをシリアライズするようにします。

2.1 ロールモデルのスキーマ

ロールのモデルのXml表現は次のようなものとしました。rolesをルート要素。子要素roleがひとつのロールを現し、applicationNameがアプリケーション名,nameがロール名を現し、所属するユーザ名がusers要素以下のuser要素のテキストとして列挙されます。

<?xml version="1.0"?>
<roles xmlns="urn:Handcraft.RoleInfoList">
  <role xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" applicationName="/" name="Director" xmlns="urn:Handcraft.RoleInfo">
    <users>
      <user>test</user>
      <user>test1</user>
      <user>Supe</user>
    </users>
  </role>
  <role xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" applicationName="/" name="SuperRole" xmlns="urn:Handcraft.RoleInfo">
    <users>
      <user>Supe</user>
    </users>
  </role>
</roles>

2.2 Roleモデルクラスの作成

CustomProviderクラスライブラリプロジェクトにRoleInfo.csクラスを作成しRoleInfo,RoleInfoListクラスを作成します。以下のように編集します。RoleInfoクラスが一つのロール,RoleInfoクラスがロールの集合を管理するためのクラスです。ロールプロバイダで使用するためのユーティリティーメソッドがいくつか定義されています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace CustomProvider
{
    /// <summary>
    /// ロール情報のモデルクラス
    /// </summary>
    [XmlRoot("role", Namespace = "urn:Handcraft.RoleInfo")]
    public class RoleInfo
    {
        public RoleInfo()
        {
            ApplicationName = "/";
            RoleName = string.Empty;
            UserNames = new List<string>();
        }
        [XmlAttribute("applicationName")]
        public string ApplicationName { get; set; }
        [XmlAttribute("name")]
        public string RoleName { get; set; }
        [XmlIgnore]
        private List<string> UserNames { get; set; }
        [XmlArrayItem(typeof(string), ElementName="user")]
        [XmlArray("users")]
        public string[] _Users
        {
            get { return GetAllUserInRole(); }
            set
            {
                RemoveAllUser();
                AddUsers(value);
            }
        }
        public void AddUser(string username)
        {
            UserNames.Add(username);
        }
        public void RemoveUser(string username)
        {
            UserNames.Remove(username);
        }
        public bool ContainUser(string username)
        {
            return UserNames.Contains(username);
        }
        public void RemoveAllUser()
        {
            UserNames.Clear();
        }
        public void AddUsers(string[] username)
        {
            foreach (string user in username)
            {
                AddUser(user);
            }
        }
        public string[] GetAllUserInRole()
        {
            return UserNames.ToArray();
        }

        public static RoleInfo CreateRoleInfo(string applicationName, string roleName)
        {
            RoleInfo r = new RoleInfo { ApplicationName = applicationName, RoleName = roleName };
            return r;
        }
    }
    /// <summary>
    /// ロールのキークラス
    /// </summary>
    class RoleKey : IComparable<RoleKey>
    {
        public string RoleName { get; set; }
        public string ApplicationName { get; set; }
        public override int GetHashCode()
        {
            return string.Concat(ApplicationName, RoleName).GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (!(obj is RoleKey))
            {
                return false;
            }
            RoleKey o =  obj as RoleKey;
            return ApplicationName.Equals(o.ApplicationName) && RoleName.Equals(o.RoleName);
        }

        #region IComparable<RoleKey> Members

        public int CompareTo(RoleKey other)
        {
            int result = ApplicationName.CompareTo(other.ApplicationName);
            if (result == 0)
            {
                return RoleName.CompareTo(other.RoleName);
            }
            else
            {
                return result;
            }
        }

        #endregion
    }
    [XmlRoot("roles", Namespace="urn:Handcraft.RoleInfoList")]
    public class RoleInfoList : IXmlSerializable
    {
        /// <summary>
        /// ロール情報を格納するコレクション
        /// </summary>
        private SortedList<RoleKey, RoleInfo> _roles = new SortedList<RoleKey, RoleInfo>();
        /// <summary>
        /// RoleInfoを追加する
        /// </summary>
        /// <param name="roleInfo"></param>
        public void AddRoleInfo(RoleInfo roleInfo)
        {
            _roles.Add(new RoleKey { ApplicationName = roleInfo.ApplicationName, RoleName = roleInfo.RoleName }, roleInfo);
        }
        /// <summary>
        /// RoleInfoを削除する
        /// </summary>
        /// <param name="roleInfo"></param>
        public void RemoveRoleInfo(RoleInfo roleInfo)
        {
            RemoveRoleInfo(roleInfo.ApplicationName, roleInfo.RoleName);
        }
        /// <summary>
        /// RoleInfoを削除する
        /// </summary>
        /// <param name="applicationName"></param>
        /// <param name="roleName"></param>
        public void RemoveRoleInfo(string applicationName, string roleName)
        {
            _roles.Remove(new RoleKey { ApplicationName = applicationName, RoleName = roleName });
        }
        public bool RoleExists(string applicationName, string roleName)
        {
            return _roles.ContainsKey(new RoleKey { ApplicationName = applicationName, RoleName = roleName });
        }
        public string[] GetAllRoles(string applicationName)
        {
            var v = from role in _roles.Values
                    where role.ApplicationName == applicationName
                    select role.RoleName;

            return v.ToArray();
        }
        public string[] GetRolesForUser(string applicationName, string username)
        {
            var v = from role in _roles.Values
                    where (role.ApplicationName == applicationName &&  role.ContainUser(username))
                    select role.RoleName;

            return v.ToArray();
        }

        public RoleInfo GetRoleInfo(string applicationName, string roleName)
        {
            try
            {
                return _roles[new RoleKey { ApplicationName = applicationName, RoleName = roleName }];
            }
            catch (KeyNotFoundException)
            {
                return null;
            }   
        }
        #region IXmlSerializable Membersの実装

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// 独自でデシリアライズを行う
        /// </summary>
        /// <param name="reader"></param>
        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer s = new XmlSerializer(typeof(RoleInfo));

            reader.Read();
            while (reader.NodeType != System.Xml.XmlNodeType.EndElement && reader.NodeType != System.Xml.XmlNodeType.None)
            {
                RoleInfo o = s.Deserialize(reader) as RoleInfo;
                AddRoleInfo(o);
            }
        }

        /// <summary>
        /// 独自でシリアライズを行う
        /// </summary>
        /// <param name="writer"></param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer s = new XmlSerializer(typeof(RoleInfo));
            foreach (RoleInfo val in _roles.Values)
            {
                s.Serialize(writer, val);
            }
        }
        #endregion
    }
}

XmlSerializerでシリアライズできるようにXmlElementなどがアノテートされています。詳細についてはMSDNライブラリや本サイトのサンプルXmlSerializerを使ってインスタンスをXML形式でシリアライズする を参照して下さい。一部のC# 3.0以上の機能が使われていますが、C# 2.0用に書き換えることによって.NET 2.0環境でも動作するようになります。

今回の説明は以上です。次回はXmlRoleProviderを実装してみます。