Linq To SQL - ALinq Provider를 이용하여 Firebird 사용
지난번까지 Firebird에 대한 설치 및 ADO.NET 접근 방법을 알아보았는데요.
.NET 프로그래머에게도 유용한 Firebird 무료 데이터베이스
; https://www.sysnet.pe.kr/2/0/1038
Firebird 데이터베이스와 ADO.NET
; https://www.sysnet.pe.kr/2/0/1039
이에 더해서 DB 접근을 너무나 쉽게 해주는 Linq to SQL을 살펴보지 않으면 서운하겠지요. ^^
검색을 해보면, 다음의 사이트에서 Firebird 및 기타 데이터베이스들에 대한 "Linq to ..." 제품군을 구할 수 있습니다.
ALinq -- The Best Database Linq Provider
; http://www.alinq.org/?gclid=CP2D54XF5KgCFc2DpAodRV1uCA
현재, (2011-05-16) 기준으로 2.5.2 버전을 다운로드해 설치해봤는데요.
위에서처럼 단일 설치 파일 내에 Access, DB2, Firebird, MySql, Oracle, PostgreSQL, SQLite에 대한 Linq Provider를 함께 제공하고 있는 것을 볼 수 있습니다.
설치하고 나면, C:\Program Files\ALinq 하위에 각각 다음과 같은 폴더 구조가 생성되는데,
- bin - ALinq Provider DLL 포함
- samples - C# / VB 예제 파일들
- share - 개별 DB의 ADO.NET Provider DLL 포함
bin 폴더 안에, Firebird용으로 ALinq.Firebird.dll이 제공되고 공통 파일로 ALinq.dll 파일이 포함됩니다. 그리고, share 폴더에는 2.5.0 버전의 FirebirdSql.Data.FirebirdClient.dll이 포함되어 있는데 ALinq.Firebird.dll에서 그 2.5.0 버전의 DLL로 강력한 이름(Strong name) 참조를 하고 있습니다.
만약, "
Firebird 데이터베이스와 ADO.NET" 글에서 살펴본 2.6.0 버전의 FirebirdSql.Data.FirebirdClient를 사용하고 싶다면 어떻게 해야 할까요? 이에 대해 alinq.org 측에 문의를 했더니 support 메일로 다음과 같이 응답이 왔습니다.
SeongTae Jeong:
Yes, you can reference the FirebirdSql.Data.FirebirdClient 2.6.0.0, but you need to append a qualifyAssembly element in the app.config (for win project) or web.confg (for web project).
Here is a sample, for more information about qualifyAssembly, please reference MSDN.
<?xml version="1.0"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<qualifyAssembly partialName="FirebirdSql.Data.FirebirdClient" fullName="FirebirdSql.Data.FirebirdClient, Version=2.6.0.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c"/>
</assemblyBinding>
</runtime>
</configuration>
I wish my answer can help you.
Shu Mai
분명히, 예전에 공부했음에도 불구하고 생각이 안 나던 assemblyBinding 정책을 이렇게 다시 한번 배우게 되니... 이젠 정말 잊어버리지 않을 것 같습니다. ^^
참고로, 만약 위와 같은 app.config 설정 없이 2.6.0 버전과 사용하면 다음과 같은 오류를 만날 수 있습니다.
An unhandled exception of type 'System.IO.FileLoadException' occurred in ALinq.dll
Additional information: Could not load file or assembly 'FirebirdSql.Data.FirebirdClient, Version=2.5.0.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
굳이, ALinq 제품을 사용하는 모든 컴퓨터에 msi 파일을 이용하여 설치할 필요는 없습니다. 최종적으로 개발자는 다음과 같은 3가지 파일만 복사해 두면 되고, 프로젝트에서는 이를 참조해서 같이 배포해 주기만 하면 됩니다.
- /bin/ALinq.dll
- /bin/ALinq.Firebird.dll
- /share/FirebirdSql.Data.FirebirdClient.dll
이제, 코드 작성을 실제로 한번 해볼까요?
이번에도 지난번에 작성한 간단한 TESTTABLE 기준으로 코드를 만들어 볼 텐데요. 우선, Console 응용 프로그램을 하나 만들고, .NET 4.0에서 기본 제공되는 System.Data.Linq와 위의 3가지 DLL을 참조 추가 합니다.
시작점으로 DataContext를 마련해 두어야 하는데, Firebird용으로 만들면 다음과 같습니다.
[ALinq.Mapping.ProviderAttribute(typeof(ALinq.Firebird.FirebirdProvider))]
public partial class TestDatabaseContext : ALinq.DataContext
{
public TestDatabaseContext(string connectionString)
: base(connectionString)
{
}
}
이를 사용하는 간단한 코드를 Main에 넣어두고 실행하면,
static void Main(string[] args)
{
string connectionString = @"DataSource=localhost;Port=3050;Database=c:\temp\test.fdb;User=SYSDBA;Password=masterkey";
TestDatabaseContext dc = new TestDatabaseContext(connectionString);
}
new TestDatabaseContext(...) 실행 지점에서 다음과 같은 오류가 발생합니다. ^^;
"
LicenseException was unhandled
Found license fail!
"
그렇습니다. 이 제품은 Trial 버전이라는 중간 단계 없이 처음부터 라이선스를 구매해야 테스트가 되는 제품입니다.
가격을 볼까요?
ALinq - Purchase
; http://www.alinq.org/Purchase.aspx
Full Edition으로 구매하는 것은 Single - $299, Team - $899, Site - $2,399이고, Firebird 하나만 구매한다면 Single - $129, Team - $399, Site - $999로 상당히 저렴한 편입니다.
사실, 가격은 저렴하지만 이 순간이 가장 고민됩니다. 왜냐하면, 과연 이 제품이 개발을 완료하는 시점까지 나의 요구 사항을 만족할 수 있을지 알 수 없기 때문입니다. (단적인 예로, System.Data.Linq에 포함된 기본 DataContext와 동일하게 기능을 제공할지는 테스트를 해봐야 아는 것입니다.)
어쨌든, 라이선스 문제를 해결하고 계속 테스트를 진행해 보겠습니다.
지난번에 실습용으로 만들어 둔 TESTTABLE의 테이블 스키마에 알맞게 타입을 생성하고 특성을 적절하게 정의해줍니다.
[Table(Name = "TESTTABLE")]
public class TestTables
{
[Column(IsPrimaryKey = true, DbType = "VarChar(50)")]
public string Name { get; set; }
[Column(DbType = "VarChar(150)")] // Firebird는 NVarChar를 지원하지 않음.
public string Description { get; set; }
}
이어서, TestTables 타입을 DataContext에 추가해주고,
[ALinq.Mapping.ProviderAttribute(typeof(ALinq.Firebird.FirebirdProvider))]
public partial class TestDatabaseContext : ALinq.DataContext
{
...[생략]...
public ALinq.Table<TestTable> TestTables
{
get
{
return this.GetTable<TestTable>();
}
}
}
이를 사용하는 간단한 코드를 작성하고 실행해 보면 되겠지요.
static void Main(string[] args)
{
string connectionString = ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString;
TestDatabaseContext dc = new TestDatabaseContext(connectionString);
if (dc.DatabaseExists())
{
dc.DeleteDatabase();
}
dc.CreateDatabase();
TestTable item = new TestTable();
item.Name = Guid.NewGuid().ToString();
item.Description = item.Name + "_Description";
dc.TestTables.InsertOnSubmit(item);
dc.SubmitChanges();
var inserted = from record in dc.TestTables
where record.Name == item.Name
select record;
Console.WriteLine(inserted.First().Description);
}
다행히, 이 정도의 간단한 테스트에는 합격입니다. 좀 더 나아가서, 이번엔 TransactionScope도 테스트해 볼까요? System.Transactions를 참조하고 다음과 같이 코드를 추가합니다.
using (TransactionScope scope = new TransactionScope())
{
dc = new TestDatabaseContext(connectionString);
item = new TestTable();
item.Name = Guid.NewGuid().ToString();
item.Description = item.Name + "_Description";
dc.TestTables.InsertOnSubmit(item);
dc.SubmitChanges();
// scope.Complete();
}
아쉽군요. 결과는 정상적으로 Commit 된 상태를 반환하고 있습니다. 즉 (최소한 Firebird용의) ALinq Provider는 TransactionScope과의 연동에 문제가 있습니다. (같은 코드를 System.Data.Linq의 DataContext로 MSSQL 서버와 연동하면 정상적으로 roll-back 됩니다.) 바로 이런 면들이, trial 단계를 거치지 않고 결제를 했을 때 '향후에 밝혀지는 문제점'들이라고 할 수 있겠는데요.
따라서, 불편하게도 ALinq로는 다음과 같이 로컬 트랜잭션을 직접 구동하는 코드를 작성해 주어야 합니다.
dc.Connection.Open();
DbTransaction dbTx = dc.Connection.BeginTransaction();
dc.Transaction = dbTx;
item = new TestTable();
item.Name = Guid.NewGuid().ToString();
item.Description = item.Name + "_Description";
dc.TestTables.InsertOnSubmit(item);
dc.SubmitChanges();
dbTx.Rollback();
이에 대해, support@alinq.org 측에 문의를 했더니 다음과 같이 답변을 받았습니다.
The TransactionScope feature is depends on the database.
I have google and found it seems not works with firebird database.
Here is a sample for you how to use the transaction.
using (var conn = new FbConnection(conn_str))
{
var dc = new FirebirdNorthwind(conn);
conn.Open();
var tran = conn.BeginTransaction();
try
{
//do something
dc.SubmitChanges();
tran.Commit();
}
catch
{
tran.Rollback();
}
}
그렇군요. 이것은 ALinq 자체의 문제가 아닌, Firebird 데이터베이스 측에서 TransactionScope을 지원하지 않는다고 합니다.
어쨌든, ALinq를 다뤄보는 것은 이 정도 선에서 마무리하고... 다음번에는 무료 Linq Provider에 대해서 글을 써보겠습니다. ^^
(
첨부한 파일은 위의 테스트를 담은 프로젝트입니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]