Question

I have a BO (Country) with a child BO (State) which also has a child BO (City). When I update the parent BO (Country), add a child State and run save, when an exception occurs in the DAL (on purpose), the transaction is not rolled back. I am using SqlCE. I am attaching a sample stripped down project that demonstrates the issue. What am I doing wrong?

Test code:

    Country originalCountry = null;
    try
    {
        originalCountry = Country.GetCountry(1);
        var country = Country.GetCountry(1);
        country.CountryName = "My new name";
        var state = country.States.AddNew();
        state.StateName = "Dummy state";
        country.States.EndNew(country.States.IndexOf(state));
        country.Save();
    }
    catch (Exception exception)
    {
        var country = Country.GetCountry(1);
        if (originalCountry.CountryName != country.CountryName)
        {
            System.Console.WriteLine("Values ARE NOT the same: " + originalCountry.CountryName + " vs. " + country.CountryName);
        }
        else
        {
            System.Console.WriteLine("Values are the same: " + originalCountry.CountryName + " vs. " + country.CountryName);
        }
    }

Country.cs

[Transactional(TransactionalTypes.TransactionScope)]
protected override void DataPortal_Update()
{
    Update();
}

private void Update()
{
    using (var ctx = DalFactory.GetManager())
    {
        var dal = ctx.GetProvider<ICountryDal>();
        using (BypassPropertyChecks)
        {
            var dto = new CountryDto();
            TransferToDto(dto);
            dal.Update(dto);
        }
        FieldManager.UpdateChildren(this);
        throw new Exception("Rollback should occur.");
    }
}

Sample project

Était-ce utile?

La solution 2

The DalManager (and the ConnectionManager) relies on reference counting to determine when close the actual connection.

The rules are not making sure to dispose the DalManager and hence the DalManager and reference counting is off. Resulting in the update happening on a connection that was created and opened in one of the Fetch operations and is therefore not be enlisted in the TransactionScope on the Update method.

See: http://msdn.microsoft.com/en-us/library/bb896149%28v=sql.100%29.aspx

All rules must be changed to dispose of the DalManager. Original rule:

    protected override void Execute(RuleContext context)
    {
        var name = (string)context.InputPropertyValues[_nameProperty];
        var id = (int)context.InputPropertyValues[_idProperty];
        var dal = DalFactory.GetManager();
        var countryDal = dal.GetProvider<ICountryDal>();
        var exists = countryDal.Exists(id, name);
        if (exists)
        {
            context.AddErrorResult("Country with the same name already exists in the database.");
        }
    }

DalManager is IDisposable but is not explicitly disposed here so it depends on when the GC will actually collect the object.

Should be:

    protected override void Execute(RuleContext context)
    {
        var name = (string)context.InputPropertyValues[_nameProperty];
        var id = (int)context.InputPropertyValues[_idProperty];
        using (var dal = DalFactory.GetManager())
        {
            var countryDal = dal.GetProvider<ICountryDal>();
            var exists = countryDal.Exists(id, name);
            if (exists)
            {
                context.AddErrorResult("Country with the same name already exists in the database.");
            }
        }
    }

Autres conseils

From my understanding of SQL CE and transactions, they only support a transaction on a single database connection when using TransactionScope.

It looks like your code is following the model put forward by some of the CSLA samples, but the actual opening/closing of the database connection is hidden in the GetManager or GetProvider abstraction, so there's no way to say for sure how that's handled.

It does seem that SQL CE has some limitations on transactions with TransactionScope though, so you should make sure you aren't violating one of their restrictions somehow.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top