web-dev-qa-db-ja.com

頻繁に使用されるデータのデータベースからデータを取得するための好ましい方法は何ですか?

{id、name、unitなどの}従業員テーブルがあると言います。このデータを頻繁に使用する場合にこのデータを取得するための推奨される方法は何ですか。最初のオプションは、必要に応じてデータベースからクエリすることです。 、

public static List<Employee> GetEmployees(string unitID)
{
    List<Employee> employees = new List<Employee>();

    using (SqlConnection sqlConn = new SqlConnection(string.Format(constring, UserID, Password, Server, Database)))
    {
        using (SqlCommand sqlComm = new SqlCommand(string.Format("SELECT ID, Name FROM Employee WHERE UnitID = {0} ORDER BY Name", unitID), sqlConn))
        {
            try
            {
                sqlConn.Open();
                using (SqlDataReader sqlReader = sqlComm.ExecuteReader())
                {
                    if (sqlReader.HasRows)
                    {
                        while (sqlReader.Read())
                        {
                            Employee employee = new Employee()
                            {
                                ID= sqlReader["ID"].ToString(),
                                Name = StaticField.id_ID.TextInfo.ToTitleCase(sqlReader["Name"].ToString())
                            };
                            employees.Add(employee);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBoxEx.Show(ex.Message, "GetEmployees Error", MessageBoxButton.OK, MessageBoxImage.Stop);
            }

            return employees;
        }
    }
}

または、2番目のオプションとして、DataSetを作成してから、データセットにTableAdapterとして従業員テーブルを追加します。

var employeeListTableAdapter = new DataSetEmployeeTableAdapters.EmployeeListTableAdapter();
employeeListTableAdapter.ClearBeforeFill = true;
employeeListTableAdapter.Fill(dataset.EmployeeList);

次に、行をフィルタリングする必要があるときは、このようなものを使用します。

DataView data = dataset.EmployeeList.DefaultView;
data.RowFilter = string.Format("Unit = {0}", unitID);
3
SIRS

これを本当に柔軟で透過的にするには、インターフェースと、インターフェースを実装するいくつかの具象クラスが必要であり、真のオブジェクト指向プログラミングのための静的クラスとメソッドを避ける必要があります。

public interface IEmployeeRepository
{
    IEnumerable<Employee> FindByUnit(int unitId);
    Employee Find(int id);
}

これらのオブジェクトをキャッシュする難しさを後で説明するために、Findメソッドをインターフェイスに追加しています。

現在、EmployeeRepositoryクラスはIEmployeeRepositoryインターフェイスを実装しており、毎回データベースにクエリを実行します。

public class EmployeeRepository : IEmployeeRepository
{
    public EmployeeRepository(string connectionString)
    {
        if (string.IsNullOrEmpty(connectionString))
            throw new ArgumentNullException("connectionString");

        this.connectionString = connectionString;
    }

    private string connectionString;

    public Employee Find(int id)
    {
        // Query by Id and return a single Employee
    }

    public IEnumerable<Employee> FindByUnit(int unitId)
    {
        // Query the database and return the list of employees
    }
}

次に、具象クラスとインターフェースを使用するコードをいくつか示します。

IEmployeeRepository repository = new EmployeeRepository("...");

IEnumerable<Employee> employees = repository.FindByUnit("3");

これにより、現在の場所に移動します。データベースは毎回照会されます。これで、キャッシングを導入できます。

public class CachedEmployeeRepository : IEmployeeRepository
{
    public CachedEmployeeRepository(IEmployeeRepository employees)
    {
        if (employees == null)
            throw new ArgumentNullException("employees");

        this.employees = employees;
        this.cacheById = new Dictionary<int, Employee>();
        this.cacheByUnitId = new Dictionary<int, Employee>();
    }

    private Dictionary<int, Employee> cacheById;

    private Dictionary<int, IEnumerable<Employee>> cacheByUnitId;

    public Employee Find(int id)
    {
        Employee employee = null;

        if (cacheById.ContainsKey(id))
        {
            employee = employees.Find(id);

            if (employee != null)
            {
                cacheById[id] = employee;
            }
        }
        else
        {
            employee = cacheById[id];
        }

        return employee;
    }

    public IEnumerable<Employee> FindByUnit(int unitId)
    {
        if (cacheByUnitId.ContainsKey(unitId))
        {
            return cacheByUnitId[unitId];
        }

        IEnumerable<Employee> results = employees.FindByUnit(unitId);

        if (results.Any())
        {
            cacheByUnitId[unitId] = results;
        }

        return results;
    }
}

CachedEmployeeRepositoryは、コンストラクターでEmployeeRepositoryを受け取り、IEmployeeRepositoryインターフェースを実装します。これで、アプリケーションにシームレスにキャッシングを導入できます。

IEmployeeRepository repository = new CachedEmployeeRepository(
    new EmployeeRepository("..."));

IEnumerable<Employee> employees = repository.FindByUnit("3");

コードの残りの部分は、キャッシュが行われていることを知らないか、知る必要もありません。

ただし、キャッシングには注意してください。 Idによる従業員の検索とユニットIDによるそれらのリストの検索には異なるキャッシュがあるため、Employeeクラスの異なるインスタンスがあることに注意してください。

コンピュータサイエンスには2つの難しい問題があります。キャッシュの無効化、名前の付け方、オフバイワンエラーです。 - Martin Fowler

2
Greg Burghardt

次のような単純なキャッシュを実装できます。

public class DatabaseHelper
{
    static private Dictionary<string,List<Employee>> _lookup = new Dictionary<string, List<Employee>>();

    static public List<Employee> GetEmployees(string unitID, bool refresh)
    {
        List<Employee> list;

        if (!refresh)
        {            
            bool exists = _lookup.TryGetValue(unitID, out list);
            if (exists) return list;
        }

        list = GetEmployees(unitID);
        _lookup[unitID] = list;
        return list;
    }

   static private List<Employee> GetEmployees(string unitID)
   {
       //You already wrote this part
   }
}
1
John Wu