Skip to content

FoxDbAccess 인스턴스 생성

Fox DB Access 를 사용하여 데이터베이스에 접근하기 위해서는 FoxDbAccess 추상 클래스에서 파생된 FoxSqlDbAccess, FoxOracleDbAccess, FoxNpgsqlDbAccess 등 구체 클래스를 생성해야 합니다. new 연산자를 사용하여 구체 클래스들을 직접 생성하거나 CreateDbAccess 메서드를 사용하여 팩터리 패턴 혹은 의존성 주입 패턴을 사용할 수도 있습니다. 이 문서에서는 FoxDbAccess 객체를 생성하는 방법들과 각 방법의 장단점에 대해 설명합니다.

이 문서와 관련된 예제 코드는 다음 예제를 참조 하십시요.

Concrete 클래스 인스턴스 생성

Fox DB Access 는 데이터베이스에 대한 각 데이터 프로바이더에 대한 래퍼(wrapper) 클래스를 제공합니다. 이들 래퍼 클래스들은 데이터 프로바이더들과 1대1로 대응되며 모두 FoxDbAccess 클래스에서 파생된 클래스 입니다.

FoxDbAccess concrete classes

  • FoxSqlDbAccess

    MS SQL Server 에 접근하기 위해 사용되는 Microsoft.Data.SqlClient 패키지(데이터 프로바이더)에 대한 래퍼 클래스 입니다.NeoDEEX.Data.SqlClient 패키지를 참조하면 사용할 수 있습니다.

  • FoxOracleDbAccess

    Oracle 에 접근하기 위해 사용되는 Managed ODP.NET Core 에 대한 래퍼 클래스 입니다. NeoDEEX.Data.OracleClient 패키지를 참조하면 사용할 수 있습니다.

  • FoxNpgsqlDbAccess

    PostgreSQL에 접근하기 위해 사용되는 Npgsql 라이브러리에 대한 래퍼 클래스 입니다. NeoDEEX.Data.NpgsqlClient 패키지를 참조하면 사용할 수 있습니다.

  • FoxMySqlDbAccess

    MySQL 에 접근하기 위해 사용되는 MySQL Connector/NET 에 대한 래퍼 클래스 입니다. 이 클래스를 사용하기 위해서는 NeoDEEX.Data.MySqlClient 패키지를 참조해야 합니다.

  • FoxOdbcDbAccess

    ODBC 를 사용하여 ODBC driver 를 제공하는 데이터베이스에 접근하기 위해 사용하는 System.Data.Odbc 패키지에 대한 래퍼 클래스 입니다. 이 클래스를 사용하기 위해서는 NeoDEEX.Data.Odbc 패키지를 참조해야 합니다.

FoxDbAccess 의 구체 클래스들을 사용하기 위해서는 필요한 NeoDEEX 패키지를 참조하고 해당 구체 클래스를 new 연산자를 사용하여 객체를 생성하면 됩니다. 다음 예제 코드는 구체 클래스인 FoxNpgsqlDbAccess 클래스의 인스턴스를 생성하고 쿼리를 수행하는 예를 보여 줍니다.

FoxNpgsqlDbAccess dbAccess = new(npgsqlConnectionString);
NpgsqlParameter[] parameters =
[
    new NpgsqlParameter("product_id", 20),
    new NpgsqlParameter("product_name", "A%")
];
string query = "SELECT * FROM products WHERE product_id <= :product_id AND product_name LIKE :product_name";
DataSet ds = dbAccess.ExecuteSqlDataSet(query, parameters);
foreach(DataRow row in ds.Tables[0].Rows)
{
    Console.WriteLine($"Product ID: {row["product_id"]}, Product Name: {row["product_name"]}");
}

구체 클래스의 인스턴스를 생성하는 방법은 레거시 데이터 액세스 코드와 크게 다르지 않으며 직관적입니다. 또한 해당 데이터베이스에 특화된 기능을 사용할 수 있습니다. 예를 들어, FoxNpgsqlDbAccess 클래스는 PostgreSQL 의 함수(function)가 반환하는는 레퍼런스 커서에 대해 FETCH 문을 자동으로 수행행해주는 ExecuteSpDataSet3 메서드를 제공합니다. 이 메서드는 FoxNpgsqlDbAccess 클래스에 구현되어어 있으며 데이터베이스에 독립적인 베이스 클래스인 FoxDbAccess 클래스에는 정의되어 있지 않습니다. 따라서 FoxNpgsqlDbAccess 타입을 사용하지 않는 경우에는 이 메서드를 호출할 수 없습니다. FoxSqlDbAccess 클래스나 FoxOracleDbAccess 클래스 역시 비슷하게 각 데이터베이스에서만 제공하는 일련의 메서드들이 존재하며 구체 클래스 타입을 사용해야 호출이 가능합니다.

구체 클래스 타입을 사용하는 경우 직관적이며 개별 데이터베이스에 특화된 코드를 작성할 수 있지만 코드의 이식성이 낮아집니다. 여러 데이터베이스에 적응이 가능한 앱을 작성하고자 하는 경우에는 수정해야할 코드의 양이 크게 늘어납니다. 또한 개발자는 데이터베이스 별로 구체 클래스들에 대한 학습이 필요하게 됩니다.

의존성 주입 패턴

구체 클래스를 사용하면 코드는 데이터베이스에 의존성을 갖게 됩니다. 의존성을 제거하는 방법은 객체 생성 팩터리 패턴 혹은 의존성 주입(dependency injection) 패턴을 사용할 수 있습니다. Fox DB Access 는 의존성 제거를 의해 FoxDbAccess 베이스 클래스를 사용하고 이 클래스의 정적(static) 메서드인 CreateDbAccess 메서드를 사용할 수 있습니다.

CreateDbAccess 메서드를 호출하면 FoxDbAccess 구현은 구성 설정에서 데이터베이스 연결 정보를 찾아 구체 클래스의 인스턴스를 생성하여 반환합니다. 구성 설정에서 database 섹션의 하위 컬렉션인 connectionStrings 컬렉션에서 연결 정보를 검색합니다. 연결 정보가 1개인 경우 해당 연결 정보를 사용하며 연결 정보가 2개 이상인 경우 defaultConnectionString 속성이 지정하는 이름을 가진 연결 정보를 사용하거나 CreateDbAccess 메서드 호출에 전달된 연결 정보 이름을 사용합니다.

Note

구성 설정에 관한 일반적인 내용은 Fox Configuration 문서를 참고 하십시요.

예를 들어 다음과 같은 neodeex.config.json 파일이 적용된 경우,

{
  "$schema": "https://neodeex.github.io/doc/neodeex.config.schema.json",
  "database": {
    "connectionStrings": {
      "PostgreSQL": {
        "type": "NeoDEEX.Data.NpgsqlClient.FoxNpgsqlDbAccess",
        "connectionString": "... your connection string ..."
      }
    }
  }
}

CreateDbAccess 메서드 호출은 FoxNpgsqlDbAccess 객체를 생성하며 구성 설정에 지정된 연결 문자열을 사용합니다. CreateDbAccess 메서드가 반환하는 타입은 데이터베이스에 의존적이지 않은 베이스 클래스 타입인 FoxDbAccess 타입입니다. 다음 코드는 CreateDbAccess 메서드를 사용하는 예제 코드 입니다.

1
2
3
4
5
6
7
FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
FoxDbParameterCollection parameters = dbAccess.CreateParamCollection();
parameters.AddWithValue("product_id", DbType.Int16, 20);
parameters.AddWithValue("product_name", DbType.String, "A%");
string query = "SELECT * FROM products WHERE product_id <= :product_id AND product_name LIKE :product_name";
DataSet ds = dbAccess.ExecuteSqlDataSet(query, parameters);
// DataSet 사용 코드 (생략)

Note

매개변수를 처리하는 매개변수 컬렉션인 FoxDbParameterCollection 타입은 FoxDbAccess 타입과 비슷하게 추상 베이스 클래스이며 이 클래스에서 데이터베이스 별로 파생된 FoxNpgsqlParameterCollection, FoxSqlParameterCollection, FoxOracleParameterCollection 등의 클래스가 존재합니다.

연결 정보 선택

연결 정보가 2개 이상 존재하는 경우, CreateDbAccess 메서드를 호출할 때 사용하고자 하는 연결 정보 이름을 제공해야 합니다. 예를 들어 다음과 같이 PostgreSQL 과 Oracle 에 대한 연결 정보를 가진 구성 설정을 사용한다고 가정해 봅시다.

{
  "$schema": "https://neodeex.github.io/doc/neodeex.config.schema.json",
  "database": {
    "connectionStrings": {
      "PostgreSQL": {
        "type": "NeoDEEX.Data.NpgsqlClient.FoxNpgsqlDbAccess",
        "connectionString": "... your connection string ..."
      },
      "Oracle": {
        "type": "NeoDEEX.Data.OracleClient.FoxOracleDbAccess",
        "connectionString": "... your connection string ..."
      }
    }
  }
}

두 개의 데이터베이스 연결 정보 중 하나를 선택하려면 CreateDbAccess 메서드에 연결정보 이름을 제공해야 합니다. 이 경우 PostgreSQL 혹은 Oracle 중 하나를 사용하면 됩니다. 다음 코드는 Oracle 이라는 이름을 가진 연결 정보를 사용합니다.

FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess("Oracle");
// 이하 코드는 동일

Oracle 이라는 이름을 가진 연결정보는 FoxOracleDbAccess 클래스의 인스턴스를 생성하고 연결 정보에 포함된 연결 문자열을 사용하여 초기화를 수행합니다.

2 개 이상의 연결 정보를 포함하는 구성 설정에서 CreateDbAccess 메서드에 연결 정보 이름이 제공되지 않았을 때 기본적으로 선택할 연결 정보 이름을 지정하는 것도 가능합니다. "defaultConnectionString" 속성을 추가하여 디폴트 연결 정보 이름을 지정하면 됩니다.

{
  "$schema": "https://neodeex.github.io/doc/neodeex.config.schema.json",
  "database": {
    "defaultConnectionString": "PostgreSQL",
    "connectionStrings": {
      "PostgreSQL": {
        "type": "NeoDEEX.Data.NpgsqlClient.FoxNpgsqlDbAccess",
        "connectionString": "... your connection string ..."
      },
      "Oracle": {
        "type": "NeoDEEX.Data.OracleClient.FoxOracleDbAccess",
        "connectionString": "... your connection string ..."
      }
    }
  }
}

위와 같은 구성 설정을 사용하는 경우 CreateDbAccess() 호출과 CreateDbAccess("PostgreSQL") 호출은 동일하게 작동합니다.

의존성 주입 방식에서 구체적인 클래스 메서드 호출

CreateDbAccess 메서드가 반환하는 타입은 FoxDbAccess 타입입니다. FoxDbAccess 타입은 다양한 데이터베이스에 대한 단일 API 를 제공하기 때문에 특정 데이터베이스를 위한 메서드는 포함되지 않습니다. 예를 들어 FoxNpgsqlDbAccess 클래스는 앞서 언급했던 ExecuteSpDataSet3 메서드를 구현하고 있지만 CreateDbAccess 메서드가 반화하는 FoxDbAccess 타입은 이 메서드에 대한 선언이 존재하지 않습니다.

FoxDbAccess 에 대한 구체 클래스의 메서드를 호출하기 위해서는 형변환이 필요합니다. CreateDbAccess 메서드가 반환하는 타입이 FoxDbAccess 타입일지라도 CreateDbAccess 메서드가 반환하는 실제 객체는 구체 클래스의 인스턴스이기 때문입니다.

1
2
3
4
5
6
7
FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
FoxDbParameterCollection parameters = dbAccess.CreateParamCollection();
parameters.AddWithValue("p_id", DbType.Int32, 20);
parameters.AddWithValue("p_name", DbType.String, "A%");
string functionName = "get_products_with_param";
DataSet ds = ((FoxNpgsqlDbAccess)dbAccess).ExecuteSpDataSet3(functionName, (FoxNpgsqlParameterCollection)parameters);
// DataSet 사용 코드 (생략)

FoxDbAccess 타입 관련 권장 사항

Fox DB Access 를 사용함에 있어서 FoxDbAccess 추상 타입을 사용할 것인지 FoxNpgsqlDbAccess 와 같은 구체 클래스를 사용할 것인지에 대한 권고 사항입니다.

  • 항상 CreateDbAccess 메서드를 통해 FoxDbAccess 인스턴스를 생성하십시요. 연결 문자열과 같은 정보를 비롯하여 Fox DB Profile 설정을 구성 설정으로부터 읽어 올 수 있으므로 하드 코드를 막을 수 있습니다.

  • 데이터베이스에 대한 의존성을 줄이기 위해서는 가급적 FoxDbAccess 타입을 사용하는 것이 좋습니다.

  • FoxDbAccess 타입을 사용하면서 특정 데이터베이스에 대한 DbType 지정이 필요하다면 FoxNpgsqlParameterCollection 타입과 같은 구체 타입을 사용하십시요. CreateParamCollection 메서드를 호출하고 그 결과를 원하는 타입으로 형 변환이 가능합니다.

    1
    2
    3
    4
    FoxDbAccess dbAccess = FoxDbAccess.CreateDbAccess();
    FoxNpgsqlParameterCollection parameters = (FoxNpgsqlParameterCollection)dbAccess.CreateParamCollection();
    parameters.AddWithValue("p_id", NpgsqlDbType.Integer, 20);
    parameters.AddWithValue("p_name", NpgsqlDbType.Varchar, "A%");
    
  • 이식성이 중요하다면 SQL 문장과 DbType 에 대해서도 의존성을 낮출 수 있는 Fox Query를 사용하는 것이 좋습니다.

  • 의존성, 이식성, 개발 생산성 보다 성능이 가장 중요하다면 특정 데이터베이스에 대한 구체적인 클래스들을 사용하십시요. FoxNpgsqlParameterCollection, FoxOracleParameterCollection 과 같은 구체 클래스들은 DbType 변환 등의 오버헤드를 최소화 할 수 있어서 성능상 유리합니다.

    Note

    데이터베이스에 독립적인 FoxDbAccess 타입, DbType 타입, FoxDbParameterCollection 을 사용할 때 발생하는 오버헤드는 크지 않습니다. 대부분의 상황에서는 이들 클래스를 사용함에 따른 오버헤드는 무시할 수 있습니다.