0

我在内存中遇到了 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;
    }

但它一直告诉我会话已关闭。那么,有人对如何解决这个问题有任何建议吗?

还是这太臭了,无法拯救?

我希望这足够清楚。请原谅我的错误:我来自巴西,不是以英语为母语的人。

谢谢,

路易斯安吉洛。

4

2 回答 2

0

我会在系统中创建 CPF 时检查唯一性,并在数据库中有一个额外的唯一约束来强制执行。然后,如果将每个对 CPF 的引用设置为无级联(默认为无),则无法将新创建的重复 CPF 分配给实体并毫无例外地保存它,因此它不会意外发生。

于 2012-11-28T14:26:26.640 回答
0

我有同样的问题。发生的事情是,当连接关闭时,内存中的 SQLite 将删除整个模式。如果您创建一个为所有测试保留的会话,它将保留所有其他会话的结构。

有关代码和更完整的解释,请查看以下答案:Random error when testing with NHibernate on an in-Memory SQLite db

于 2012-12-02T15:04:16.327 回答