will unit of work from llblgenprodatasource2 automatically support recursive

Posts   
 
    
bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 12-May-2008 20:19:17   

llblgen v 2.5 windows 2003 sqlserver 2005

claim m:1 examiner ( claim.examiner_number -> examiner.number where examiner.number is not autogenerated)

I have a claim and examiner relationship defined as above. I know I can use 2 formviews, one for examiner and one for claim. I would force the user to create an examiner first before creating a claim.
However, I would like to use a single formview and llblgenprodatasource2 to show the claim and examiner information. So in the llblgen designer, I will add "fields mapped on related fields" that adds the examiner.name and examiner.phone fields. In the formview, besides having the claim info, I also have claim.examiner_number, examiner.name, examiner.phone.

Question: When the user creates a new claim that requires a new examiner, will the UnitOfWork2 that is automatically generated by llblgenprodatasource2 creates a new examiner as well as new claim in one transaction?

Comment: I know that if I use adapter.entitysave I can specify refetchaftersave and recurse to be true. However since I am using formview and llblgenprodatasource2 it automatically creates the unitofwork2 for me.

Thanks,

BZ

bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 12-May-2008 21:15:27   

In the user guide manual, the example "UnitOfWork usage: single entities" shows how to create a customer entity and then create an address entity and use UnitOfWork2.AddForSave for recursive save.

However, since I am using formview, I do not get to create the UnitOfWork2 but rather the formview works with llblgenprodatasource2 and the UnitOfWork2 is automatically created for me.

May be using FormView with LlblgenProDatasource2 is not recommended after all. Since everything happens automatically and we cannot control customize the UnitOfWork2 unless we recreated our own UnitOfWork2!

Should I not use FormView and LlblgenProDataSource2 and use the plain old TextBox and manually do all the binding in the code behind?

Thanks,

BZ

bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 12-May-2008 22:44:54   

OK, here's what I tested and it works. Please let me know if there's a better way of doing this.

[For the sake of simplicity and due to availability, I am using address and state example which does not make sense in real life because no one would ever create a new State on the fly.]

Database: Address.State_Code <-> State.Code where Code is PK and of type varchar.

In aspx file:

<llblgenpro:LLBLGenProDataSource2 ID="ldsAddress" runat="server" 
            AdapterTypeName="iClaim.DAL.DatabaseSpecific.DataAccessAdapter, iClaim.DALDBSpecific" 
            CacheLocation="Session" DataContainerType="EntityCollection" 
            LivePersistence="False" OnPerformSelect="ldsAddress_PerformSelect" 
            OnPerformWork="ldsAddress_PerformWork"          
            EntityFactoryTypeName="iClaim.DAL.FactoryClasses.MyAddressEntityFactory, iClaim.DAL">
        </llblgenpro:LLBLGenProDataSource2>
        <asp:FormView ID="FormView1" runat="server" DataKeyNames="ID" 
            DataSourceID="ldsAddress">
            <InsertItemTemplate>
                EntityID:
                <asp:TextBox ID="EntityIDTextBox" runat="server" 
                    Text='<%# Bind("EntityID") %>' />
                <br />
                State_Code:
                <asp:TextBox ID="State_CodeTextBox" runat="server" 
                    Text='<%# Bind("State_Code") %>' />
                <br />
                <asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True" 
                    CommandName="Insert" Text="Insert" />
                &nbsp;<asp:LinkButton ID="InsertCancelButton" runat="server" 
                    CausesValidation="False" CommandName="Cancel" Text="Cancel" />
            </InsertItemTemplate>
            <EmptyDataTemplate><asp:LinkButton ID="NewButton" runat="server" CausesValidation="False" 
                    CommandName="New" Text="New" /></EmptyDataTemplate>
        </asp:FormView>

in the ascx file:

protected void ldsAddress_PerformWork(object sender, SD.LLBLGen.Pro.ORMSupportClasses.PerformWorkEventArgs2 e)
        {
            AddressManager.CreateAddress(e.Uow);
        }

In the BLL file:

static public void CreateAddress(UnitOfWork2 uow)
        {
            DataAccessAdapter adapter = new DataAccessAdapter();
            foreach (UnitOfWorkElement2 element in uow.GetEntityElementsToInsert())
            {
                if (element.Entity is MyAddressEntity)
                {
                    MyAddressEntity address = (MyAddressEntity)element.Entity;
                    if (null == address.State)
                    {
                        address.State = new MyStateEntity(address.State_Code);
                        element.Refetch = true;
                        element.Recurse = true;
                    }
                }
                element.Entity.ToString();
            }
            uow.Commit(adapter);
        }

So, basically in the formview I enter value for Address.State_Code, in the BLL I have to go in the UnitOfWork and manually create a new State entity for the recursive to work. Otherwise I will get the PK/FK relationship error where the new PK does not exist.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 13-May-2008 05:14:51   

Yes. That would work. So it's fine. You also could use LivePersistence=false and save the graph: http://llblgen.com/TinyForum/Messages.aspx?ThreadID=10802&StartAtMessage=0&#60234

David Elizondo | LLBLGen Support Team
bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 13-May-2008 05:35:29   

Hi,

I do have further question.

Lets say I want to a form where I allow the user to save an address, I also allow the user to specify the State code and description. Something like:

in aspx file:


<llblgenpro:LLBLGenProDataSource2 ID="ldsAddress" runat="server" 
            AdapterTypeName="iClaim.DAL.DatabaseSpecific.DataAccessAdapter, iClaim.DALDBSpecific" 
            CacheLocation="Session" DataContainerType="EntityCollection" 
            LivePersistence="False" OnPerformSelect="ldsAddress_PerformSelect" 
            OnPerformWork="ldsAddress_PerformWork"          
            EntityFactoryTypeName="iClaim.DAL.FactoryClasses.MyAddressEntityFactory, iClaim.DAL">
        </llblgenpro:LLBLGenProDataSource2>
        <asp:FormView ID="FormView1" runat="server" DataKeyNames="ID" 
            DataSourceID="ldsAddress">
            <InsertItemTemplate>
                EntityID:
                <asp:TextBox ID="EntityIDTextBox" runat="server" 
                    Text='<%# Bind("EntityID") %>' />
                <br />
                State_Code:
                <asp:TextBox ID="State_CodeTextBox" runat="server" 
                    Text='<%# Bind("State_Code") %>' />
                <br />
                State_Description:
                <asp:TextBox ID="State_DescriptionTextBox" runat="server" 
                    Text='<%# Bind("State_Description") %>' />
                <br />
                <asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True" 
                    CommandName="Insert" Text="Insert" />
                &nbsp;<asp:LinkButton ID="InsertCancelButton" runat="server" 
                    CausesValidation="False" CommandName="Cancel" Text="Cancel" />
            </InsertItemTemplate>
</asp:FormView>

in ascx file:


protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                SD.LLBLGen.Pro.ORMSupportClasses.IPrefetchPath2 path = new SD.LLBLGen.Pro.ORMSupportClasses.PrefetchPath2((int)iClaim.DAL.EntityType.AddressEntity);
                path.Add(iClaim.DAL.EntityClasses.MyAddressEntity.PrefetchPathState);

                // here use your LLBLGenDataSource object name
                this.ldsAddress.PrefetchPathToUse = path;
            } 
        }
...
protected void ldsAddress_PerformSelect(object sender, SD.LLBLGen.Pro.ORMSupportClasses.PerformSelectEventArgs2 e)
        {
}
...
protected void ldsAddress_PerformWork(object sender, SD.LLBLGen.Pro.ORMSupportClasses.PerformWorkEventArgs2 e)
        {
            iClaim.BLL.Admin.AddressManager.CreateAddress(e.Uow);
        }

in BLL file:


static public void CreateAddress(UnitOfWork2 uow)
        {
            DataAccessAdapter adapter = new DataAccessAdapter();
            foreach (UnitOfWorkElement2 element in uow.GetEntityElementsToInsert())
            {
                if (element.Entity is MyAddressEntity)
                {
                    MyAddressEntity address = (MyAddressEntity)element.Entity;
                    if (null == address.State)
                    {
                        address.State = new MyStateEntity(address.State_Code);
                        address.State.Description = address.State_Description;
                        element.Refetch = true;
                        element.Recurse = true;
                    }
                }
            }
            uow.Commit(adapter);
        }

PROBLEM: address.State_Description is empty. Please note that this is the "field maps on related field".

Thanks,

BZ

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 13-May-2008 10:24:31   

Fields mapped on related fields should be used for Read-Only purposes.

You shouldn't bind the fields mapped on related fields in the InsertItemTemplate of the FormView. Rather just use normal un-bound text box, and catch its content in code behind to set the Address.State.Description field, not the field mapped on related field.

bunzee
User
Posts: 84
Joined: 20-Mar-2007
# Posted on: 13-May-2008 18:37:48   

Walaa,

Boy I really don't want the code behind to set unbounded text box value to Address.State.Description. The whole FormView and LlblgenProDataSource2 that automatically generate the UnitOfWork2 encapsulates the data unbinding for the GUI designer . Doing this the GUI designer must be able to break out the UnitOfWork2 or must pass all the text boxes values to the BLL. I suppose the latter is a better choice.

In one of the thread, daelmo suggested the following which is not going to work since in the FormView insert mode the Address entity of the Employee entity is null.

// __LLBLGENPRO_USER_CODE_REGION_START CustomEntityCode

public virtual System.String City
{
    get
    {
        if (this.Address != null)
        {
            return this.Address.City;
        }
        else
        {
            return (System.String)TypeDefaultValue.GetDefaultValue(typeof(System.String));
        }
    }
    set
    {
        if (this.Address != null)
        {
            this.Address.City = value;
        }
    }
}


///...
///... same for ZIP


// __LLBLGENPRO_USER_CODE_REGION_END

So dissect out the UnitOfWork2 generated by LlblgenProDataSource2 and set unbound text box value is my only option?

Thanks,

BZ

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 14-May-2008 09:31:06   

Daelmo's code can be modified to be:

public virtual System.String City
{
    get
    {
        if (this.Address != null)
        {
            return this.Address.City;
        }
        else
        {
            return (System.String)TypeDefaultValue.GetDefaultValue(typeof(System.String));
        }
    }
    set
    {
        if (this.Address == null)
        {
                this.Address = new AddressEntity();
        }

        this.Address.City = value;
    }
}