Runtime revisions todo list

Posts   
1  /  2  /  3
 
    
Trig
User
Posts: 96
Joined: 09-Jun-2004
# Posted on: 09-Aug-2004 01:34:25   

disappointed I don't blame you. The way databinding is done in the 1.x framework is horrendous, let alone the fact that there is NO documentation on it. Thank God they've redone it in 2.0.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 09-Aug-2004 10:57:13   

Trig wrote:

disappointed I don't blame you. The way databinding is done in the 1.x framework is horrendous, let alone the fact that there is NO documentation on it. Thank God they've redone it in 2.0.

Yeah, in 2.0 it's a total different ballgame, a sign it's not that good today simple_smile .

Not only the lack of documentation is shocking, but also the lack of being able to debug the code is something that can't be ignored. When I programmed the ITypedList stuff I already found myself in the complete darkness, messing with code without any documentation (only an obscure article of one of the ORM.NET guys (how ironic wink , however they seem to have gone belly up) but at least you could debug it somewhat. This is different, I totally don't understand what's wrong.

If I have: Customer Customer.Orders

it works, do I add: Order.Customers (bogus, returns a new CustomerCollection, just for kicks) it doesn't work.

So somewhere it can't deal with cycles when the code produces properties themselves. When you have it hardcoded in code, thus no ITypedList, it works, but that is not what I can use. Typical, as DataTable/DataSet also doesn't have to deal with cycles in definitions.

Oh well... it would have been really nice to have. perhaps I get something working in the future, when there is more time simple_smile

Frans Bouma | Lead developer LLBLGen Pro
mdisbrow
User
Posts: 31
Joined: 22-Jun-2004
# Posted on: 13-Aug-2004 14:51:22   

Sorry to bug you, any update on when the beta will be released??

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 13-Aug-2004 14:56:41   

mdisbrow wrote:

Sorry to bug you, any update on when the beta will be released??

Well, a.s.a.p. I hope. I'm currently working on the prefetch paths and I hope to have it done by monday, then some small things are left. Paging took more time than expected as well as the expressions and I lost 2 days fighting vs.net to figure out design time databinding which was unsuccesful...

It takes a little longer, however I decided to take a little more time and to include all that's scheduled instead of dropping features to make it this week.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 14-Aug-2004 21:48:55   

First working sample of PrefetchPaths. This is going to be the kickass feature, trust me simple_smile . Paging can't be done at the same time as paging logic on for example sqlserver requires alot of logic which can't be used in a subquery.

Ok, below is code which fetches all customers from northwind, for them all Order objects and for each order all order details objects. No joins, just subqueries, so no not-used data is read. This is done in 3 queries: one for the customers, one for the orders and one for the order details. What's better: you can fully sort, limit (amount of elements returned) and filter these prefetch paths, per path element. So if you want just the last order for each customer, you can. simple_smile

As you can see, you can nest the paths so a hierarchy can be build. The paths work for all relations, and both for selfservicing and adapter (selfservicing's core code has to be implemented, but that's a matter of time). Adapter logic is almost done (prefetch paths for single entities are still left). With selfservicing, load-on-demand will be turned off for prefetch ed objects (collections and 1:1 referenced properties) so that the prefetched data is not re-loaded through load-on-demand (why otherwise prefetch it wink ).

For m:n relations an extra query has to be made to fetch the related PK-FK elements, but that's not that much.


/// <summary>
/// Tests the prefetch path functionality. Simple test: load all customers and all their orders.
/// </summary>
[Test]
public void PrefetchPathTestSimple()
{
    DataAccessAdapter adapter = new DataAccessAdapter();

    try
    {
        EntityCollection customers = new EntityCollection(new CustomerEntityFactory());
        IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.CustomerEntity);
        prefetchPath.Add(CustomerEntity.PrefetchPathOrders).SubPath.Add(OrderEntity.PrefetchPathOrderDetails);

        adapter.FetchEntityCollection(customers, null, 0, null, prefetchPath);
        ResultsetViewer viewer = new ResultsetViewer();
        viewer.BindCollection(customers); 
        viewer.ShowDialog(null);
    }
    finally
    {
        adapter.Dispose();
    }
}

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 17-Aug-2004 18:37:02   

pfew

It took some real behind-the-scenes refactoring but they now work: prefetch paths in SelfServicing. They did already work for adapter yesterday, including m:n prefetches, filtering, limitation and ordering simple_smile

Below a complex prefetch path solution: read all employees and in their 'Orders' collection the last Order entity they processed simple_smile No other O/R mapper can do this at the moment, and I'm very happy it now works simple_smile

I paste the selfservicing examples below, adapter has this also of course.


/// <summary>
/// Tests the prefetch path functionality. Loads all employees and per employee the last order object they closed.
/// Similar to FieldCompareSetTestComplex but now with entities.
/// </summary>
[Test]
public void PrefetchPathTestComplex()
{
    EmployeeCollection employees = new EmployeeCollection();
    IPrefetchPath prefetchPath = new PrefetchPath((int)EntityType.EmployeeEntity);
    ISortExpression sorter = new SortExpression();
    sorter.Add(SortClauseFactory.Create(OrderFieldIndex.OrderDate, SortOperator.Descending));
    prefetchPath.Add(EmployeeEntity.PrefetchPathOrders, 1, null, null, sorter);

    employees.GetMulti(null, prefetchPath);
}

M:N relations:


/// <summary>
/// Tests the prefetch path functionality. Simple test: load one customer and all its employees (m:n)
/// </summary>
[Test]
public void PrefetchPathTestSimpleSingleEntityManyToMany()
{
    IPrefetchPath prefetchPath = new PrefetchPath((int)EntityType.CustomerEntity);
    prefetchPath.Add(CustomerEntity.PrefetchPathEmployees);
    CustomerEntity customer = new CustomerEntity("BLONP", prefetchPath);
    Assert.AreEqual(7, customer.Employees.Count);
}

multiple levels: Customers -> Orders -> OrderDetails. just 3 queries run for this simple_smile


/// <summary>
/// Tests the prefetch path functionality. Simple test: load all customers and all their orders.
/// </summary>
[Test]
public void PrefetchPathTestSimple()
{
    CustomerCollection customers = new CustomerCollection();
    IPrefetchPath prefetchPath = new PrefetchPath((int)EntityType.CustomerEntity);
    prefetchPath.Add(CustomerEntity.PrefetchPathOrders).SubPath.Add(OrderEntity.PrefetchPathOrderDetails);
    customers.GetMulti(null, prefetchPath);
}

single entity + prefetch path:


/// <summary>
/// Tests the prefetch path functionality. Simple test: load one customer and all its employees (m:n)
/// </summary>
[Test]
public void PrefetchPathTestSimpleSingleEntityManyToMany()
{
    IPrefetchPath prefetchPath = new PrefetchPath((int)EntityType.CustomerEntity);
    prefetchPath.Add(CustomerEntity.PrefetchPathEmployees);
    CustomerEntity customer = new CustomerEntity("BLONP", prefetchPath);
    Assert.AreEqual(7, customer.Employees.Count);
}

Unique constraint fetch with prefetch path:


/// <summary>
/// Tests the prefetch path functionality. Simple test: load one customer and all its orders, based on the customer's Unique constraint.
/// </summary>
[Test]
public void PrefetchPathTestSimpleSingleEntityUC()
{
    CustomerEntity customer = new CustomerEntity();
    IPrefetchPath prefetchPath = new PrefetchPath((int)EntityType.CustomerEntity);
    prefetchPath.Add(CustomerEntity.PrefetchPathOrders).SubPath.Add(OrderEntity.PrefetchPathOrderDetails);
    customer.FetchUsingUCCompanyName("Blauer See Delikatessen", prefetchPath);
    Assert.AreEqual(7, customer.Orders.Count);
}

Frans Bouma | Lead developer LLBLGen Pro
alexdresko
User
Posts: 336
Joined: 08-Jun-2004
# Posted on: 19-Aug-2004 21:17:06   

Otis wrote:

pfew

It took some real behind-the-scenes refactoring but they now work: prefetch paths in SelfServicing. They did already work for adapter yesterday, including m:n prefetches, filtering, limitation and ordering simple_smile

Well I didn't exactly know what to expect from prefetch paths until I started looking at the examples and it looks great!

If I understand them correctly, this should prevent us from having to fetch multiple entities and collections every time we want to use them?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 20-Aug-2004 10:52:22   

alexdresko wrote:

Otis wrote:

pfew

It took some real behind-the-scenes refactoring but they now work: prefetch paths in SelfServicing. They did already work for adapter yesterday, including m:n prefetches, filtering, limitation and ordering simple_smile

Well I didn't exactly know what to expect from prefetch paths until I started looking at the examples and it looks great!

If I understand them correctly, this should prevent us from having to fetch multiple entities and collections every time we want to use them?

Yes, you can now specify to pre-load related entities and collections when you load an entity or collection of entities. For selfservicing this means that you save performance in some situations (as lazy loading does the job as well but not that efficient in some situations), for adapter it means way less code for the developer simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 20-Aug-2004 21:08:14   

About COM+ and Adapter: I'm a bit unclear how to do that. The DataAccessAdapterBase class is 3400 lines long. I tried to make it derive from servicedcomponent (which is required to make calls flow from one class to another in the same COM+ transaction) but then all code calling into adapter is required to have a strong name and will be registered into COM+. Not great.

So, if I want to enable COM+ in adapter, I have to make a duplicate class, DataAccessAdapterComPlus which contains almost all the 3400 lines of the other class, derives from ServicedComponent and has a few lines different: (no ADO.NET transaction and instead of an ado.net commit/rollback, it does a COM+ rollback/commit).

This is an unmaintainable option.

Another 'solution' will be that I create a template for the COM+ adapter, and aggregate the DataAccessAdapterBase instance in there. Also not great.

I'm not sure if I can aggregate a COM+ serviced component in a normal class and make the calls to, say, SaveEntity() flow inside the transaction already going on. Probably not.

I really want to add COM+ support, but it's Microsoft requirement to derive from ServicedComponent that makes it hard, if not impossible to make this work.

In selfservicing, you use a special com+ enabled transaction object, which makes the call flow from the caller to the database, which is what's needed. With a normal Adapter class, this is not possible...

The single-inheritance requirement forces me to make a decision between crappy options. If someone has information how to solve this, please let me know. I do know about COM+ without enterprise services, but that works only on Windows2003 (and has limitations as well).

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 22-Aug-2004 15:26:46   

EnterpriseFrustrationServices...

I thought I had found a way to make this work. Unfortunately, when I create a new servicedcomponent instance from a serviced component instance, the Context is new. I can't figure out why, also MS DTC bugs left and right and I'm not allowed to do anything, even I'm admin.

I really don't know what's going on, I use the same code trick as I use in SelfServicing with TransactionComPlus, which did work, but out of the blue with SP2 it might not. Even if I made a mistake somewhere (there is no help available on this), these EnterpriseServices of .NET is not enterprise ready material you want to trust your code on. rage

I now even get weird errors that a transaction is being aborted while I just try to create a new instance of a serviced component...

(edit) After hours of cursing on everything Microsoft, it works!. Well... after I set the activation in COM+ to 'must activate in client context'. Really weird.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 23-Aug-2004 15:38:57   

Unit Of Work almost done. It's getting real close now simple_smile (beta is expected tomorrow if VB.NET templates are not throwing a lot of errors)


/// <summary>
/// Tests the UnitOfWork class. 
/// </summary>
[Test]
public void UnitOfWorkTest()
{
    CustomerEntity newCustomer = EntityCreator.CreateNewCustomer(1);
    newCustomer.TestRunId = _testRunID;
    AddressEntity newAddress = EntityCreator.CreateNewAddress(1);
    newAddress.TestRunId = _testRunID;
    newCustomer.VisitingAddress = newAddress;
    newCustomer.BillingAddress = newAddress;
    UnitOfWork uow = new UnitOfWork();
    bool addResult = uow.AddForSave(newCustomer, true);
    Assert.IsTrue(addResult);

    // create a product
    ProductEntity newProduct = EntityCreator.CreateNewProduct(1);
    newProduct.TestRunId = _testRunID;
    bool result = newProduct.Save();
    Assert.IsTrue(result);
    Guid prodID = newProduct.ProductId;

    addResult = uow.AddForDelete(newProduct);
    Assert.IsTrue(addResult);

    // test pre-commit state.
    Assert.IsTrue(newCustomer.IsNew);
    Assert.IsTrue(newAddress.IsNew);
    Assert.AreEqual(EntityState.Fetched, newProduct.Fields.State);

    // commit
    uow.Commit(new Transaction(IsolationLevel.ReadCommitted, "UOWTest"), true);

    // test save actions of UOW.
    Assert.AreEqual(EntityState.OutOfSync, newAddress.Fields.State);
    Assert.AreEqual(EntityState.OutOfSync, newCustomer.Fields.State);
    Assert.IsTrue((((int)newAddress.Fields[(int)AddressFieldIndex.AddressId].CurrentValue)>0));
    Assert.IsTrue((((int)newCustomer.Fields[(int)CustomerFieldIndex.CustomerId].CurrentValue)>0));
    int addressID = newAddress.AddressId;
    Assert.AreEqual(EntityState.Fetched, newAddress.Fields.State);
    int customerID = newCustomer.CustomerId;
    Assert.AreEqual(EntityState.Fetched, newCustomer.Fields.State);
    Assert.IsTrue((newCustomer.VisitingAddressId==addressID));
    Assert.AreEqual(newAddress, newCustomer.VisitingAddress);
    Assert.IsTrue((newAddress==newCustomer.VisitingAddress));
    Assert.AreEqual(newAddress.ObjectID, newCustomer.VisitingAddress.ObjectID);

    // test delete actions of UOW:
    ProductEntity savedProduct = new ProductEntity(prodID);
    Assert.IsTrue(savedProduct.IsNew);
    Assert.AreEqual(EntityState.Deleted, newProduct.Fields.State);
}

A lot of assert code, but mainly, it's very simple code. It shows a unit of work object in selfservicing, where entities are appended to and which is committed at the end of the work actions. Adapter will get this too of course.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 23-Aug-2004 17:15:21   

And adapter:


/// <summary>
/// Tests the UnitOfWork class. 
/// </summary>
[Test]
public void UnitOfWorkTest()
{
    DataAccessAdapter adapter = new DataAccessAdapter();

    try
    {
        CustomerEntity newCustomer = EntityCreator.CreateNewCustomer(1);
        newCustomer.TestRunId = _testRunID;
        AddressEntity newAddress = EntityCreator.CreateNewAddress(1);
        newAddress.TestRunId = _testRunID;
        newCustomer.VisitingAddress = newAddress;
        newCustomer.BillingAddress = newAddress;
        UnitOfWork2 uow = new UnitOfWork2();
        bool addResult = uow.AddForSave(newCustomer, true);
        Assert.IsTrue(addResult);

        // create a product
        ProductEntity newProduct = EntityCreator.CreateNewProduct(1);
        newProduct.TestRunId = _testRunID;
        bool result = adapter.SaveEntity(newProduct, true);
        Assert.IsTrue(result);
        Guid prodID = newProduct.ProductId;

        addResult = uow.AddForDelete(newProduct);
        Assert.IsTrue(addResult);

        // test pre-commit state.
        Assert.IsTrue(newCustomer.IsNew);
        Assert.IsTrue(newAddress.IsNew);
        Assert.AreEqual(EntityState.Fetched, newProduct.Fields.State);

        // commit
        uow.Commit(adapter, true);

        // test save actions of UOW.
        Assert.AreEqual(EntityState.Fetched, newAddress.Fields.State);
        Assert.AreEqual(EntityState.Fetched, newCustomer.Fields.State);
        Assert.IsTrue((((int)newAddress.Fields[(int)AddressFieldIndex.AddressId].CurrentValue)>0));
        Assert.IsTrue((((int)newCustomer.Fields[(int)CustomerFieldIndex.CustomerId].CurrentValue)>0));
        int addressID = newAddress.AddressId;
        int customerID = newCustomer.CustomerId;
        Assert.IsTrue((newCustomer.VisitingAddressId==addressID));
        Assert.AreEqual(newAddress, newCustomer.BillingAddress);
        Assert.IsTrue((newAddress==newCustomer.BillingAddress));
        Assert.AreEqual(newAddress.ObjectID, newCustomer.VisitingAddress.ObjectID);
        Assert.AreEqual(newAddress.ObjectID, newCustomer.BillingAddress.ObjectID);

        // test delete actions of UOW:
        ProductEntity savedProduct = new ProductEntity(prodID);
        adapter.FetchEntity(savedProduct);
        Assert.IsTrue(savedProduct.IsNew);
        Assert.AreEqual(EntityState.Deleted, newProduct.Fields.State);
    }
    finally
    {
        adapter.Dispose();
    }

}

Ok, 2 minor template adjustments left, and then it's working towards a releasable beta (i.e.: VB.NET build tests, readme writing). I expect a beta tomorrow (tuesday)

Frans Bouma | Lead developer LLBLGen Pro
swallace
User
Posts: 648
Joined: 18-Aug-2003
# Posted on: 23-Aug-2004 19:15:41   

Shoot. I was hoping the uow feature would create a sort-of LLBL-side transaction that would span adapters. We have situations where we have to do inserts to multiple servers (each with a different adapter, of course). The entities are not related, but we want it to be transactionalized. The linked-server stuff in MSSQL is not great, so we were hoping that uow would give us that feature.

I notice the line for the uow adapter code:

        // commit
        uow.Commit(adapter, true);

which has the adapter object in the parameters. However, you've already called:

        bool addResult = uow.AddForSave(newCustomer, true);

Is it too late to get a feature where .AddForSave, and other .AddFor... calls have the adapter on them, so that the uow associates the adapter with the .AddFor call? That way it can perform it all in one chunk with a .Commit() but with no adapter in the commit call.

It's Ok with us if it's calling a different MSSQL transaction for each adapter within the .Commit of the uow, just so we know if they all happened, and which one's failed.

Too late for a feature?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 23-Aug-2004 20:38:30   

Hmm. Well it's not too late to make changes, as I've finished it just a few hours ago wink .

The reason why it is not the way you suggest is that the unit of work object has to be serializable: collect actions on the client and send them over to the server to make them effective. The same is true for a GUI tier sending things down to the DAL/BL.

Also, making it a cross-adapter spanning class would suggest a robust transaction mechanism which can never be provided by C# code alone, but needs COM+.

To solve your problem, it is best to keep a unit of work per database. Then, you create a ServicedComponent derived class.

In there you create for each database a new com+ context host which contains an adapter object (this is new as well). You can then commit each uow with their own adapter, which are hosted by a COM+ context host and thus will run inside the COM+ transaction. This will make the transaction span the multiple adapters and make it truly atomic.

Please let me know if this is workable for you.

Frans Bouma | Lead developer LLBLGen Pro
Cadmium avatar
Cadmium
User
Posts: 153
Joined: 19-Sep-2003
# Posted on: 23-Aug-2004 20:49:27   

So many new features to play with, any sign of a beta on the horizon? simple_smile Some of those features will have a huge impact on my current project.

swallace
User
Posts: 648
Joined: 18-Aug-2003
# Posted on: 23-Aug-2004 20:57:29   

Otis wrote:

In there you create for each database a new com+ context host which contains an adapter object (this is new as well). You can then commit each uow with their own adapter, which are hosted by a COM+ context host and thus will run inside the COM+ transaction. This will make the transaction span the multiple adapters and make it truly atomic.

I'll give that a try once the new beta is out. I don't want to hold you up. This version has a great deal that I'm looking forward to, especially MSAccess and aggregates. Back to work for you! wink

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 23-Aug-2004 21:56:42   

swallace wrote:

Otis wrote:

In there you create for each database a new com+ context host which contains an adapter object (this is new as well). You can then commit each uow with their own adapter, which are hosted by a COM+ context host and thus will run inside the COM+ transaction. This will make the transaction span the multiple adapters and make it truly atomic.

I'll give that a try once the new beta is out. I don't want to hold you up. This version has a great deal that I'm looking forward to, especially MSAccess and aggregates. Back to work for you! wink

MSAccess will follow later this week. The rest tomorrow. simple_smile

Cadmium: I hope the beta will start tomorrow. One small template addition left, VB.NET template checks and a readme simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Cadmium avatar
Cadmium
User
Posts: 153
Joined: 19-Sep-2003
# Posted on: 23-Aug-2004 22:00:21   

Otis wrote:

Cadmium: I hope the beta will start tomorrow. One small template addition left, VB.NET template checks and a readme simple_smile

Can't wait simple_smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 24-Aug-2004 14:42:07   

Ok, functionality is done, build tests are next and a readme so you all can get started.

I'll include my unittest programs so you can see how to use the new features (I wrote for each new feature a unit test/couple of unit tests). Stay tuned! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 24-Aug-2004 22:49:44   
Frans Bouma | Lead developer LLBLGen Pro
Posts: 11
Joined: 09-Jul-2004
# Posted on: 10-Sep-2004 18:04:58   

Is this list of todos in the latest downloads?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39774
Joined: 17-Aug-2003
# Posted on: 10-Sep-2004 18:13:31   

Gareth wrote:

Is this list of todos in the latest downloads?

It's in beta now, download the runtime library beta from the runtime libraries section in the customer area.

Frans Bouma | Lead developer LLBLGen Pro
1  /  2  /  3