我在内存中遇到了 Sqlite 的一些问题。
我有一个具有 CPF 字段的类 - 类似于美国的 SSN。作为商业规则,CPF 在系统中必须是唯一的。
所以我决定检查有这个字段的类。现在可能这里有代码异味:我用 ORM 检查这是否是一个冲突的 CPF。
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 Helper 类上的实现。
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;
}
但它一直告诉我会话已关闭。那么,有人对如何解决这个问题有任何建议吗?
还是这太臭了,无法拯救?
我希望这足够清楚。请原谅我的错误:我来自巴西,不是以英语为母语的人。
谢谢,
路易斯安吉洛。