メモリ内の Sqlite に問題があります。
米国の SSN に似た CPF フィールドを持つクラスがあります。ビジネス ルールとして、CPF はシステム内で一意である必要があります。
そこで、このフィールドを持つクラスをチェックすることにしました。ここにコードの匂いがあるかもしれません。これが競合する CPF であるかどうかを ORM に確認します。
private CPF cpf;
public virtual CPF CPF
{
get { return cpf; }
set
{
if (this.ormCreated) //Do not check if it is loaded from the DB. Otherwise, it loops, generating a StackOverflow exception
{
cpf = value;
}
else
{
this.setNewCpf(value);
}
}
}
private void setNewCpf(CPF newCpf)
{
if (this.cpf == newCpf)
{
return;
}
if (Helper.Orm.IsConflictingCpf(newCpf))
{
throw new ConflictingCpfException();
}
else
{
cpf = newCpf;
}
}
ORM ヘルパー クラスでの実装は次のとおりです。
bool OrmHelper.IsConflictingCpf(CPF cpf)
{
int? cpfNumber = cpf.NumeroSemDV;
if (cpfNumber.HasValue)
{
var teste = findByCpfNumber<Client>(cpf);
return
(
findByCpfNumber<Client>(cpf) != null ||
findByCpfNumber<Adversary>(cpf) != null
);
}
else
{
//CPFSemDV = Nullable
return false;
}
}
private PersonType findByCpfNumber<PersonType> (CPF cpf) where PersonType : PessoaFisica
{
int? cpfNumber = cpf.NumeroSemDV;
using (var session = this.NewSession())
using (var transaction = session.BeginTransaction())
{
try
{
var person = session.Query<PersonType>()
.Where(c => c.CPF.NumeroSemDV == cpfNumber)
.FirstOrDefault<PersonType>();
return person;
}
catch (Exception) { transaction.Rollback(); }
finally
{
session.Close();
}
}
return null;
}
問題は私のテストで発生します。FluentNHibernate とインメモリ SQLite を使用しています。
protected override FluentConfiguration PersistenceProvider
{
get
{
return Fluently
.Configure()
.Database(
SQLiteConfiguration
.Standard
.InMemory()
.ShowSql()
);
}
}
これが失敗したテストです。
protected override void Given()
{
base.Given();
var clients = new List<Client>();
Client client1 = new Client("Luiz Angelo Heinzen")
{
Capaz = true,
CPF = new CPF(18743509),
eMail = "lah@furb.br"
};
session.Save(client1);
session.Evict(client1);
}
[Then]
public void Motherfaker()
{
Client fromDb;
var clientsFromDb = session.Query<Client>()
.Where(c => c.eMail == "lah@furb.br");
fromDb = clientsFromDb.FirstOrDefault<Client>();
Assert.AreEqual(fromDb.FullName, "Luiz Angelo Heinzen");
}
失敗する理由は?最初は、テーブルが存在しなかったために失敗していました。メモリ内で sqlite は、新しいセッションごとにスキーマを破棄します。そこで、NewSession() で同じセッションを返すようにコードを変更しました。しかし、NHibernate 例外で失敗するようになりました: セッションが閉じられました。私はテストしましたが、これからfindByCpfNumberを変更する場合
private PersonType findByCpfNumber<PersonType> (CPF cpf) where PersonType : PessoaFisica
{
int? cpfNumber = cpf.NumeroSemDV;
using (var session = this.NewSession())
using (var transaction = session.BeginTransaction())
{
try
{
var person = session.Query<PersonType>()
.Where(c => c.CPF.NumeroSemDV == cpfNumber)
.FirstOrDefault<PersonType>();
return person;
}
catch (Exception) { transaction.Rollback(); }
finally
{
session.Close();
}
}
return null;
}
これに
private PersonType findByCpfNumber<PersonType> (CPF cpf) where PersonType : PessoaFisica
{
int? cpfNumber = cpf.NumeroSemDV;
//using (var session = this.NewSession())
var session = this.NewSession();
using (var transaction = session.BeginTransaction())
{
try
{
var person = session.Query<PersonType>()
.Where(c => c.CPF.NumeroSemDV == cpfNumber)
.FirstOrDefault<PersonType>();
return person;
}
catch (Exception) { transaction.Rollback(); }
finally
{
//session.Close();
this.CloseSession(session);
}
}
this.CloseSession(session);
return null;
}
エラーはもう発生しません。明らかに、CloseSession メソッドを実装する必要があります。本番データベースのセッションを閉じ、Sqlite が使用されている場合は何もしません。
しかし、セッションを破棄しないように SQLite を構成したいと思います。release_mode 、Pooling、および Max Pool 属性については、こちらを参照してください。しかし、FluentNHibernate でそれを見つけることができないようです。そのため、動作するかどうかをテストすることさえできません。FluentNHibernate のクローンを作成しましたが、release_mode が on_close に設定されているようですが、役に立ちません。
私も試しました:
public override ISession NewSession()
{
if (this.session == null)
{
if (sessionFactory == null)
{
CreateSessionFactory();
}
this.session = sessionFactory.OpenSession();
}
if (!session.IsOpen)
{
sessionFactory.OpenSession(session.Connection);
session.Connection.Open();
}
return session;
}
しかし、セッションが閉じられていると私に言い続けます。それで、これにアプローチする方法について何か提案はありますか?
それとも、これが救いようのないほど臭いのでしょうか?
これが十分に明確であることを願っています。私はブラジル出身で、英語のネイティブ スピーカーではありません。
ありがとう、
ルイス・アンジェロ。