Tying IConcurrencyPredicateFactory with adaptor database updates

Posts   
 
    
he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 08:41:16   

Just started writing my first really serious app with LLBLGen Pro

I have created a IConcurrencyPredicateFactory based class so that my updates/deletes concurrency can be managed by a timestamp field (called concurrency).

I know you must have been asked this before (probably millions of times)

But is the code below ok.

Iam using an adaptor.StartTransaction. Could I be using a UnitOfWork2 to manage transactions. If there is a problem with a UnitOfWork2 will it rollback the transaction. How do I capture an exception error to bubble up. Which approach is better?

With my IConcurrencyPredicateFactory based class, how do I pass this to ??? so that it is used for concurrency for updates and deletes.

using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using Infragistics.Win.UltraWinGrid; using PathWest; using PathWest.Data; using PathWest.Data.DatabaseSpecific; using PathWest.Data.EntityClasses; using PathWest.Data.HelperClasses; using PathWest.Data.FactoryClasses; using PathWest.Data.RelationClasses;

using SD.LLBLGen.Pro.ORMSupportClasses; using SD.LLBLGen.Pro.DQE.SqlServer; using Infragistics.Win; using Nullable=Infragistics.Win.UltraWinGrid.Nullable;

namespace PathWest.SystemConfiguration.BusinessLogic { public class ManageCaseTypes : ILookupData {

    private EntityCollection CaseTypes;
    private EntityCollection<CaseTypeEntity> DeletedCaseTypes;

    public EntityCollection GetTableData()
    {

        using (var adaptor = new DataAccessAdapter())
        {
            try
            {
                CaseTypes = new EntityCollection(new CaseTypeEntityFactory());
                DeletedCaseTypes = new EntityCollection<CaseTypeEntity>();
                CaseTypes.RemovedEntitiesTracker = DeletedCaseTypes;
                adaptor.FetchEntityCollection(CaseTypes, null);
                return CaseTypes;
            }
            catch (Exception e)
            {
                throw new Exception("Cannot acquire CaseTypes Table Data ", e);
            }
        }

    }

    public void FormatGrid(UltraGrid caseTypeGrid)
    {
       foreach(UltraGridColumn GridColumn in caseTypeGrid.DisplayLayout.Bands[0].Columns)
       {
           switch (GridColumn.Key)
           {
               case "CaseTypePkey":
                   GridColumn.Header.Caption = "Case Type";
                   break;
               case "CaseTypeDescription":
                   GridColumn.Header.Caption = "Description";
                   break;
               case "CaseDisplayOrder":
                   GridColumn.Header.Caption = "DisplayOrder";
                   break;
               case "HistoricalMapValue":
                   GridColumn.Header.Caption = "Historical Value";
                   break;
               case "Notes":
                   GridColumn.Header.Caption = "Notes";
                   break;
               case "RegisterItemRecieval":
                   GridColumn.Header.Caption = "Item Recieval";
                   break;
               case "RegisterReferrence":
                   GridColumn.Header.Caption = "Referrence Registration";
                   break;
               case "Historical":
                   GridColumn.Header.Caption = "Historical Only";
                   break;
               default:
                   GridColumn.Hidden = true;
                   break;
           }
       }
    }

    public void UpdateTableData()
    {
        using (var adaptor = new DataAccessAdapter())
        {
            adaptor.StartTransaction(IsolationLevel.ReadCommitted, "CaseTypes");
            try
            {
                adaptor.SaveEntityCollection(CaseTypes);
                adaptor.DeleteEntityCollection(DeletedCaseTypes);
                adaptor.Commit();
            }
            catch (Exception e)
            {
                adaptor.Rollback();
                throw new Exception("Lookup table CaseTypes update failure",e);
            }
        }
    }

}


internal class CaseTypeConcurrencyFilterFactory : IConcurrencyPredicateFactory
{
    public IPredicateExpression CreatePredicate(ConcurrencyPredicateType predicateTypeToCreate, object containingEntity)
    {
        IPredicateExpression toReturn = new PredicateExpression();
        var caseType = (CaseTypeEntity)containingEntity;
        switch (predicateTypeToCreate)
        {
            case ConcurrencyPredicateType.Delete:
                toReturn.Add(CaseTypeFields.Concurrency == caseType.Fields[(int)CaseTypeFieldIndex.Concurrency].DbValue);
                break;
            case ConcurrencyPredicateType.Save:
                // only for updates
                toReturn.Add(CaseTypeFields.Concurrency == caseType.Fields[(int)CaseTypeFieldIndex.Concurrency].DbValue);
                break;
        }
        return toReturn;
    }
}

}

confused

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 28-May-2009 08:58:27   

With my IConcurrencyPredicateFactory based class, how do I pass this to ??? so that it is used for concurrency for updates and deletes.

How about using Dependency Injection

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 09:19:28   

Thanks I will try that later but I would really like to do this in code before using attaributes

I now have

public void UpdateTableData() { CaseTypes.ConcurrencyPredicateFactoryToUse = (IConcurrencyPredicateFactory) new CaseTypeConcurrencyFilterFactory().CreatePredicate(ConcurrencyPredicateType.Save, new CaseTypeEntity()); DeletedCaseTypes.ConcurrencyPredicateFactoryToUse = (IConcurrencyPredicateFactory)new CaseTypeConcurrencyFilterFactory().CreatePredicate(ConcurrencyPredicateType.Delete, new CaseTypeEntity());

        using (var adaptor = new DataAccessAdapter())
        {

            adaptor.StartTransaction(IsolationLevel.ReadCommitted, "CaseTypes");
            try
            {
                adaptor.SaveEntityCollection(CaseTypes);
                adaptor.DeleteEntityCollection(DeletedCaseTypes);
                adaptor.Commit();
            }
            catch (Exception e)
            {
                adaptor.Rollback();
                throw new Exception("Lookup table CaseTypes update failure",e);
            }
        }
    }

Which does not work

Can you tell me where I am going wrong.

Also can you please give me a code fragment on how to implement injection in this instance.

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 09:33:52   

I have set up a trace to the output window to see what llblgen pro is doing I commented out my IConcurrencyPredicateFactory code and ran a few updates

From an update I have

Executed Sql Query: Query: UPDATE [WardenII].[lookup].[CaseType] SET [Notes]=@Notes WHERE ( [WardenII].[lookup].[CaseType].[CaseTypePKey] = @CaseTypePkey1) Parameter: @Notes : String. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: "rrrrrrrrrrrr". Parameter: @CaseTypePkey1 : StringFixedLength. Length: 1. Precision: 0. Scale: 0. Direction: Input. Value: "R".

So its doing the update based soley on the primary key. There is no concurreny control in play.

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 09:53:27   

Ok. I tried doing the dependancy injection (probably stuffed it up but at least I am trying) I now have

[DependencyInjectionInfo(typeof(IEntity2), "CaseTypeConcurrencyFilterFactory", ContextType = DependencyInjectionContextType.NewInstancePerTarget, TargetNamespaceFilter = "PathWest.Data")] [Serializable] class CaseTypeConcurrencyFilterFactory : IConcurrencyPredicateFactory { public IPredicateExpression CreatePredicate(ConcurrencyPredicateType predicateTypeToCreate, object containingEntity) { IPredicateExpression toReturn = new PredicateExpression(); var caseType = (CaseTypeEntity)containingEntity; switch (predicateTypeToCreate) { case ConcurrencyPredicateType.Delete: toReturn.Add(CaseTypeFields.Concurrency == caseType.Fields[(int)CaseTypeFieldIndex.Concurrency].DbValue); break; case ConcurrencyPredicateType.Save: // only for updates toReturn.Add(CaseTypeFields.Concurrency == caseType.Fields[(int)CaseTypeFieldIndex.Concurrency].DbValue); break; } return toReturn; } }

Was not quite sure what to use for TargetNamespaceFilter so I used TargetNamespaceFilter = "PathWest.Data.

In my CaseTypesEntity class (generated by LLBLGen Pro) I have this name space namespace PathWest.Data.EntityClasses

**Anyway the short of it is, is that it does nothing **

Trace output from an update

Executed Sql Query: Query: UPDATE [WardenII].[lookup].[CaseType] SET [Notes]=@Notes WHERE ( [WardenII].[lookup].[CaseType].[CaseTypePKey] = @CaseTypePkey1) Parameter: @Notes : String. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: "VoilVolVol". Parameter: @CaseTypePkey1 : StringFixedLength. Length: 1. Precision: 0. Scale: 0. Direction: Input. Value: "V".

Which show an update is based soley on the primary key still.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 28-May-2009 09:54:57   

Setting the ConcurrencyPredicateFactoryToUse of an entityCollection will only be passed to the entities added to the collection. But if you already have entities inside the collection before setting the property, it will not propagate to these entities.

So you should loop through the entities and set the ConcurrencyPredicateFactoryToUse for each of them.

Or set the property of the collection before populating it with entities.

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 10:09:20   

Thanks

I had tried that just before I read your message from looking at other forum questions

using (var adaptor = new DataAccessAdapter()) { try { CaseTypes = new EntityCollection(new CaseTypeEntityFactory()); DeletedCaseTypes = new EntityCollection<CaseTypeEntity>(); CaseTypes.ConcurrencyPredicateFactoryToUse = (IConcurrencyPredicateFactory)new CaseTypeConcurrencyFilterFactory().CreatePredicate(ConcurrencyPredicateType.Save, new CaseTypeEntity()); DeletedCaseTypes.ConcurrencyPredicateFactoryToUse = (IConcurrencyPredicateFactory)new CaseTypeConcurrencyFilterFactory().CreatePredicate(ConcurrencyPredicateType.Delete, new CaseTypeEntity()); CaseTypes.RemovedEntitiesTracker = DeletedCaseTypes; adaptor.FetchEntityCollection(CaseTypes, null); return CaseTypes; } catch (Exception e) { throw new Exception("Cannot acquire CaseTypes Table Data ", e); } }

But I got an exception when I tried an update

((System.InvalidCastException)(e)) {"Unable to cast object of type 'SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression' to type 'SD.LLBLGen.Pro.ORMSupportClasses.IConcurrencyPredicateFactory'."}

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 10:17:04   

Actually a previous statement of mine was wrong. I said But I got an exception when I tried an update

Actually I got the exception when calling the GetTableData() method which assigns the ConcurrencyPredicateFactoryToUse to the instantiated CaseTypeConcurrencyFilterFactory.

I did not want to mislead you, sorry.

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 10:40:30   

Ok Fixed it. I was trying too hard (that sounds better than I didn't know what I was doing so I stufffed up big time).

Code is now

public EntityCollection GetTableData() {

        using (var adaptor = new DataAccessAdapter())
        {
            try
            {
                CaseTypes = new EntityCollection(new CaseTypeEntityFactory());
                DeletedCaseTypes = new EntityCollection<CaseTypeEntity>();

** CaseTypes.ConcurrencyPredicateFactoryToUse = new CaseTypeConcurrencyFilterFactory(); DeletedCaseTypes.ConcurrencyPredicateFactoryToUse = new CaseTypeConcurrencyFilterFactory();** CaseTypes.RemovedEntitiesTracker = DeletedCaseTypes; adaptor.FetchEntityCollection(CaseTypes, null); return CaseTypes; } catch (Exception e) { throw new Exception("Cannot acquire CaseTypes Table Data ", e); } }

    }

Did an update and got

Executed Sql Query: Query: UPDATE [WardenII].[lookup].[CaseType] SET [Notes]=@Notes WHERE ( ( [WardenII].[lookup].[CaseType].[CaseTypePKey] = @CaseTypePkey1) AND ( [WardenII].[lookup].[CaseType].[Concurrency] = @Concurrency2)) Parameter: @Notes : String. Length: 50. Precision: 0. Scale: 0. Direction: Input. Value: "zzzzzzzzzzzzzzz". Parameter: @CaseTypePkey1 : StringFixedLength. Length: 1. Precision: 0. Scale: 0. Direction: Input. Value: "Q". Parameter: @Concurrency2 : Binary. Length: 8. Precision: 0. Scale: 0. Direction: Input. Value: binary lob.

I still don't know how ones uses dependancy injection using attributes. Perhaps you can enlighten me please.

I have seen generic solutions so I am going to do that as every table I have has four identical fields one of which is for concurrency control. The others are the DateCreated, TimeCreated and whoupdated it.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 28-May-2009 10:40:36   

CaseTypes.ConcurrencyPredicateFactoryToUse = (IConcurrencyPredicateFactory)new CaseTypeConcurrencyFilterFactory().CreatePredicate(ConcurrencyPredicateType.Save, new CaseTypeEntity());

ConcurrencyPredicateFactoryToUse expects an IConcurrencyPredicateFactory. But you are trying to CreatePredicate() returns an IPredicateExpression.

The following should be the code to set the ConcurrencyPredicateFactoryToUse:

CaseTypes.ConcurrencyPredicateFactoryToUse = new CaseTypeConcurrencyFilterFactory();
DeletedCaseTypes.ConcurrencyPredicateFactoryToUse = new CaseTypeConcurrencyFilterFactory();

And then you should start ading entities to these collections, but you should know that for updates you should have existing entities (IsNew == false), and the entity should be dirty and having changed fields...for the framework to attempt to save it.

Also for delete the entities should be new.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 28-May-2009 10:44:24   

I just saw your last message after posting mine. simple_smile Glad you have worked it out.

For DI, please check the link I've posted earlier.

he00273
User
Posts: 133
Joined: 02-Oct-2008
# Posted on: 28-May-2009 10:53:21   

Thanks for the DI link.

I see you have to do two things apply the attribute to your classes and modify your app.config file.

I only did one so I guess that is why it did not work.

I will play with it tomorrow. Its going home time for me now.

sunglasses