Binding / Using FormView when using a combination of LLBLGen entity and API

Posts   
 
    
Posts: 254
Joined: 16-Nov-2006
# Posted on: 01-Mar-2007 10:08:44   

Guys,

One of the administration pages I'm designing is a bit different. It's for managing users however rather than using one LLBLGen collection or typed list to load the users from I'm using the Membership API in ASP.NET to create the users using calls such as Membership.CreateUser. The membership user tables obviously contain very generic data for a user e.g. email address / name and password reset questions and answer. I've also created a new User table table which contains other data for the user which is specific to the application e.g. company / department details / default purchase order no pre fix e.t.c.

The pattern I've used for other pages is a GridView combined with a FormView, in fact I've used the same pattern the HnD LLBLGen forum source code uses on some pages such as ManageIPBans.aspx. I need to present a list of all users which combines information from the asp net membership store and also the user table. You can access the user information from the ASP.NET tables such as aspnet_Membership however you need to use the CreateUser API calls to create a new user and can't simply create a new record in the aspnet_Membership table. Because of this I can't simply created a typed list which contains columns from both the aspnet_Membership and User table and then bind this typed list to the LLBLGenDataSource which is bound to the GridView.

On creation of a new user within the FormVIew I will need to use the CreateUser API call and then use the GUID value of the new user to use to create the new LLBLGen User entity with the remaining details.

Any ideas on the best approach to implement this so I could reuse as much LLBLGen / ASP.NET code as possible i.e. using data binding. The only approach I can think of is to manually add the rows to the grid view and avoid using a FormView because I can't see how to use data binding however I'm sure there's a better way.

Cheers

Matt

Posts: 254
Joined: 16-Nov-2006
# Posted on: 01-Mar-2007 20:43:30   

Any thoughts?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 01-Mar-2007 21:48:41   

Isn't that asp.net system a provider based system? I.o.w.: you can write the provider (which should be straight forward) to manage the data and thus how entities are created in the db. Or am I missing something?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 254
Joined: 16-Nov-2006
# Posted on: 01-Mar-2007 21:57:02   

The membership system is a provider based system however there is already an existing provider which writes to the SQL Server database store called AspNetSqlMembershipProvider. There is no point in writing this as you would still want the data to be saved to the database.

The issue here is ASP.NET provides database tables to fill default generic data such as email address, user name e.t.c. in a combination of two tables

aspnet_Membership aspnet_Users

However I'm building further tables to contain application specific data for the user e.g data related to organisation structures the user is part of. I've created a TypedList which contains many fields from each entity and can certain use this however I wondered if there was a better approach.

Posts: 254
Joined: 16-Nov-2006
# Posted on: 01-Mar-2007 22:41:15   

I've started implementing this see attached code. This is the only way I can see to implement it.

The key part is in

    protected void llbGenUsersDataSource_PerformWork(object sender, SD.LLBLGen.Pro.ORMSupportClasses.PerformWorkEventArgs e)
    {
        List<UnitOfWorkElement> uowElement = null;

        FullUsersRow userRow = null;

        // First check if this is an insert
        uowElement = e.Uow.GetEntityElementsToInsert();
        if (uowElement.Count > 0)
        {
            userRow = uowElement[0].Entity as FullUsersRow;

            // As can't save user row we need to obtain the User membership
            // object and the User entity and then update all properties 
            // and save each entity.
            TextBox userName = UserFormView.Row.FindControl("NameTextBox") as TextBox;
            
            MembershipUser membershipUser = Membership.CreateUser(  userName.Text, 
                                                                    Membership.GeneratePassword(10, 3));
            membershipUser.Comment  = userRow.Comment;
            membershipUser.Email    = userRow.Email;
            Membership.UpdateUser(membershipUser);

            UserEntity userEntity = new UserEntity();
            userEntity.UserId = (Guid)membershipUser.ProviderUserKey;
            userEntity.Save();
        }

However I'm facing a more fundamental issue at the moment in the PerformWork event isn't being called even though the PerformSelect method is.

Is this related to the fact the entity the data source is bound to is a TypedList which is read only. If so how else can I handle an event to add this kind of code above.

Attachments
Filename File size Added on Approval
ManageUsers.zip 2,771 01-Mar-2007 22:42.26 Approved
Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 02-Mar-2007 07:23:41   

Is this related to the fact the entity the data source is bound to is a TypedList which is read only.

Yes it is. Typed List can't be saved.

If so how else can I handle an event to add this kind of code above.

Bind to an EntityCollection instead, and use prefetchPaths and fields Mapped on related fields to get data from related tables.

Posts: 254
Joined: 16-Nov-2006
# Posted on: 02-Mar-2007 11:00:36   

Thanks so if the code is modified to

    protected void llbGenUsersDataSource_PerformSelect(object sender, SD.LLBLGen.Pro.ORMSupportClasses.PerformSelectEventArgs e)
    {
        AspnetMembershipCollection members = new AspnetMembershipCollection();

        PrefetchPath prefetchPathToUse = new PrefetchPath((Int32)MyApp.EntityType.AspnetMembershipEntity);
        prefetchPathToUse.Add(AspnetMembershipEntity.PrefetchPathUser);
        prefetchPathToUse.Add(AspnetMembershipEntity.PrefetchPathAspnetUsers);

        members.GetMulti(null, prefetchPathToUse);

        llbGenUsersDataSource.EntityCollection = members;
}

I assume you mean using code such as the above?

In this case should I simply add Bind expression which access nested properties e.g. Bind("User.TelephoneNo")

Is it possible to bind nested properties in this way?

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 02-Mar-2007 14:49:28   

Use the LLBLGen Pro Designer to map User.TelephoneNo to the Main table (AspnetMembership) as a "field mapped on related field. (say you will call it UserTelephoneNo).

Then by using prefetchPath when you fetch the main entity, this field get populated with data.

And you can bind to that newly created fied (UserTelephoneNo).

Posts: 254
Joined: 16-Nov-2006
# Posted on: 03-Mar-2007 23:00:13   

That works great however why is there no support for nested properties within data binding?

Couldn't this be added to the LLBLGen data source control relatively easily? It would certainly help with a lot of real world scenarios where entities relate to other entities.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 05-Mar-2007 08:17:51   

Only Entity Properties that map to database fields are marked with Browsable(true), to be displayed automatically in a Grid when you choose AutogenerateColumns. Otherwise you would end up with many unwanted columns generated for you.

Although I remember to hear about using DataBindItem for accessing related entities data.

Posts: 254
Joined: 16-Nov-2006
# Posted on: 05-Mar-2007 09:28:10   

Sure I understand that however I'm referring to syntax such as

Bind("TelephoneNo")

Why can't you do

Bind("User.TelephoneNo")

If this isn't supported by the framework perhaps this can be extended within the LLBLGen control to help achieve this.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 05-Mar-2007 10:00:19   

Bind("Type.Property"), will retrieve from Type the property, at least that's what you expect. But it doesn't do that, as it simply reflects 1 level deep, not more levels deep. It's ASP.NET which doesn't allow this, as all the datasourcecontrol does is give back a list of objects.

Frans Bouma | Lead developer LLBLGen Pro