New to CSLA - A few questions

New to CSLA - A few questions

Old forum URL: forums.lhotka.net/forums/t/405.aspx


devbanana posted on Monday, June 19, 2006

hi,

 

I just started reading the book and attempting to use CSLA. Firstly, I must say it seems to be a great framework. Smile [:)] Just trying to follow along with the book without getting too confused.

 

I have a few questions that I would like clarification on if possible.

 

Firstly, with authentication and authorization, along with using ASP.NET for the web interface. I want to be able to have both a Windows forms interface and a web interface, so I don't want the implementation to be specific to the web.

 

It spoke a bit about this in the book, in chapter 10 I believe, but it was a bit confusing and I was hoping for a bit more of an explanation.

 

Let's say I have a custom identity and principal, similar to that in the code examples. Let's also say I have a custom provider for forms authentication and membership in ASP.NET. How can I tie these two together, without making the identity and principal tied too closely or too specific to the web interface?

 

Second question:

 

More a matter of design. Let's say I have a list of categories and products. Products have a many to many relationship with the categories, but categories are also related to other categories, where one category may have several children categories.

 

The only way I could think of to solve the above would be using a switchable kind of class, where it is either a parent or a child. There obviously has to be at least one category with no parent. Further, say I'm on a page where the category ID was passed, I'd just want to fetch that particular category, along with its products.

 

However, the only thing is it'd be nice to retrieve the category's immediate parent, but I'm not sure how I'd go about doing this.

 

Any help is much appreciated.

bcdennis replied on Monday, June 19, 2006

If you set your dataportal up as a webservice, then set the asp membership & role provider for the site where the portal is located to point back to your main websites asp membership database, you can do the following in the DataPortal_Fetch method of your custom Identity:

            if (Membership.ValidateUser(criteria.Username, criteria.Password))
            {
                _name = criteria.Username;
                _isAuthenticated = true;

                _roles = new List<string>(Roles.GetRolesForUser(_name));
            }




This should work since the code is being executed in the context of the dataportal (which is a website).
Thus, all your clients can use aspnet's membership & roles, regardless of platform.

devbanana replied on Monday, June 19, 2006

hi,

 

thanks for the reply. I'm still trying to figure out exactly how it was done in the book. It seems the most flexible way really.

 

Also, any comments about the categories issue?

 

Thanks again. Smile [:)]

devbanana replied on Monday, June 19, 2006

hi,

 

OK, this isn't actually implemented in the PTWeb project, but it is talked about in the book I think somewhere around page 537.

 

So, it looks like using this method, there would actually be two principals? One that wraps IPrincipal, and one that is called by the membership provider, which is the one in the project library itself. Would this be correct?   

RockfordLhotka replied on Monday, June 19, 2006

Yes, exactly. You just create a custom principal that inherits from BusinessPrincipalBase, which delegates all its work to an underlying principal object that you obtain from the membership provider. Your custom principal's code isn't complex then, since IPrincipal only defines two members, and they both delegate to the underlying principal. Something like this:

<Serializable()> _
Public Class MyPrincipal
  Private mMembershipPrincipal As IPrincipal

  Public Sub New(membershipPrincipal As IPrincipal)
    mMembershipPrincipal = membershipPrincipal
  End Sub

  Public Overrides Function IsInRole(role As String) As Boolean
    Return mMembershipPrincipal.IsInRole(role)
  End Function

  Public Overrides ReadOnly Property Identity() As IIdentity
    Get
      Return mMembershipPrincipal.Identity
    End Get
  End Property

End Class

This may not be perfectly right, but it is close anyway. Then you follow the basic pattern in the book, where in global.asax and login.aspx, AFTER the user's identity has been determined by the membership service, you set the principal object to this object - passing the previous principal into the constructor.

The only real reason for wrapping the principal from membership services is so your principal can flow through the data portal. Note that this technique ONLY works if the original principal/identity objects are serializable - which the membership ones are (or were when I tested this while writing the book).

But what this means is that if your data portal is running in local mode then you don't need to worry about this at all, because the data portal would run in the "client" context and automatically has the same security. This is only important if you configure the data portal to be remote.

devbanana replied on Monday, June 19, 2006

Ah, thanks for replying.

 

True, it only needs to be used when it'll use DataPortal remotely, but if it ever needs to be used like that, I really don't want to have to change any code. I want to make it as simple as possible.

 

RockfordLhotka:

Yes, exactly. You just create a custom principal that inherits from BusinessPrincipalBase, which delegates all its work to an underlying principal object that you obtain from the membership provider. Your custom principal's code isn't complex then, since IPrincipal only defines two members, and they both delegate to the underlying principal. Something like this:

<Serializable()> _
Public Class MyPrincipal
  Private mMembershipPrincipal As IPrincipal

  Public Sub New(membershipPrincipal As IPrincipal)
    mMembershipPrincipal = membershipPrincipal
  End Sub

  Public Overrides Function IsInRole(role As String) As Boolean
    Return mMembershipPrincipal.IsInRole(role)
  End Function

  Public Overrides ReadOnly Property Identity() As IIdentity
    Get
      Return mMembershipPrincipal.Identity
    End Get
  End Property

End Class

This may not be perfectly right, but it is close anyway. Then you follow the basic pattern in the book, where in global.asax and login.aspx, AFTER the user's identity has been determined by the membership service, you set the principal object to this object - passing the previous principal into the constructor.

The only real reason for wrapping the principal from membership services is so your principal can flow through the data portal. Note that this technique ONLY works if the original principal/identity objects are serializable - which the membership ones are (or were when I tested this while writing the book).

But what this means is that if your data portal is running in local mode then you don't need to worry about this at all, because the data portal would run in the "client" context and automatically has the same security. This is only important if you configure the data portal to be remote.

bcdennis replied on Monday, June 19, 2006

 If implemented per page 537 (which is alot better than I have previously described) there is technically 2, one in the ASP context and one in the Csla application context.  The Csla one wraps the ASP one.

bcdennis replied on Monday, June 19, 2006

In the book, Rocky uses "hand-rolled" UserName and Role tables to store user information.  If you did the same thing, you'd have to keep your aspnet membership database sync'd with your custom membership database.  You could also write a custom membership and role provider for ASP that pulled authentication info from your customer tables, but I've found ASP's authentication model and supporting API quite complete.

re: Categories
Have you thought about doing something along the lines of;
Categories(ReadOnlyRoot)
+--->RelatedCategories(ReadOnlyChildList)
       +--->RelatedCategories(ReadOnlyChild)  (where this is derived from your associative/fact table)?
+--->RelatedProducts(ReadOnlyChildList)
      +--->RelatedProducts (derived also from an associative/fact table)



devbanana replied on Monday, June 19, 2006

hi,

 

On membership:

 

The thing about the default membership implementation is firstly, it's hard to extend; to add any additional functionality you have to wrap Membership and add your own methods. Secondly, it complicates the business logic a bit. You can't quite have a Member business object, because there's already MembershipUser, so you can't use the business object class interface you do for other objects.

 

On categories:

Are you saying there'd be a RelatedCategories list as child to Category, and a RelatedCategory as child of that list? Still there can be multiple levels of categories. Unless RelatedCategory also has a child RelatedCategories list, but I'm not sure if there is anything wrong in doing that. Also most of the business logic would be duplicated, unless I have some generic Category class that wraps all data access common to both.

 

Also, however it is implemented, how could a category retrieve it's parent category? Would it be acceptable to have a _parentID field, and just a method, GetParentcategory(), which could call Category.GetCategory(_parentID);

bcdennis replied on Monday, June 19, 2006

re: Membership
Then I think your best solution would be to implement a custom membership & role provider for ASP, instead of using ASP as your membership provider for your CSLA application.

re: Categories
How's your underlying data modeled?

devbanana replied on Monday, June 19, 2006

bcdennis:
re: Membership
Then I think your best solution would be to implement a custom membership & role provider for ASP, instead of using ASP as your membership provider for your CSLA application.

I presume you mean a membership provider, while still working within ASP.NET membership so it can use the login controls and such. Admittedly not the easiest way, but I can probably use this from project to project. Code reuse. Big Smile [:D]

bcdennis:
re: Categories
How's your underlying data modeled?

It's not, yet. I'm thinking something like:

CategoryID | ParentID | Name | Description | Image

bcdennis replied on Monday, June 19, 2006

devbanana:

I presume you mean a membership provider, while still working within ASP.NET membership so it can use the login controls and such. Admittedly not the easiest way, but I can probably use this from project to project. Code reuse. Big Smile [:D]

The default membership provider for ASP is AspNetSqlMembershipProvider (which itself defaults to attaching a aspnet.mdf database file) and role provider is ASpNetSqlRoleProvider (which also houses its data in aspnet.mdf).  You can change this to a custom membership & role provider that sources its data from your own tables which ASP will use for all the Forms Authentication stuff (including login controls.)  To view this configuration, go to IIS management console.  Right click on your virtual root and select "Properties".  On the Service Properties dialog, click on the ASP.NET tab, then click "Edit Configuration".  On the ASP.NET Configuration Settings dialog, click on the Authentication tab and you'll find the settings for the Membership Provider  and Role Provider (to ASP).  You can also manually edit the Web.config file accordingly. 

devbanana:

It's not, yet. I'm thinking something like:

CategoryID | ParentID | Name | Description | Image


Well, I think there's some flaws with this approach.  You can't model a many-to-many relationship using that data structure.  For one, that model restricts  a sub-category to only have one parent, which isn't a many-to-many relationship to begin with.
The standard approach to doing many-to-many is something like;
Categories (CategoryId, Name, Description, Image)
RelatedCategories (ParentId, ChildId)  --- where ParentId and ChildId are themselves CategoryIds. (this is called an associative table or fact table.)
You could then go;
Products (Productid, ...)
ProductCategories (ProductId, CategoryId)
which would then allow you to derive all the categories that product falls in along with the related categories (from your fact table.)

Using that approach, a given child category could/would have many "parent" categories.
If a Category and a Child Category are logically different things (versus inter-related same things) then you could use a standard parent-child model such as;
Categories (CategoryId, Name, Description, Image)
ChildCategories (ChildCategoryId, CategoryId (FK), ...)


devbanana replied on Monday, June 19, 2006

hi,

 

I don't need categories to have a many to many relationship with other categories, just a one to many. Each category has one parent, each category may have no, one, or many children.

 

So how would you organize the objects in this situation?

 

Thanks again.

bcdennis replied on Monday, June 19, 2006

Category(Editable/Readonly Root)
 +---> Parent
 +---> ChildCategories(Editable/ReadOnly ChildList)
            +--->CategoryInfo(Editable/ReadOnly Child)  (where CategoryInfo is some light-weight Category BO)

Maybe something like that?



devbanana replied on Monday, June 19, 2006

hi,

 

The top category, it really wouldn't have a parent, only the child categories. Again, there should be multiple levels of child categories, or at least the option for there to be an unknown number of levels of child categories for each parent category.

 

These categories all act the same, just some are children and some are parents.

 

If there would be a parent field, would it just contain the ID of its parent, or the actual top category? I would think only the ID, as otherwise you'd have a nice loop of references going, lol.

 

Thanks again.

devbanana replied on Monday, June 19, 2006

hi,

 

How about this?

 

Category is the root

ChildCategories is under that, and this holds ChildCategory objects

ChildCategories basically has the same definition but also has private string _parentID; and a GetParentCategory method.

 

Common behavior is delegated to a GenericCategory class.

 

Would this be OK?

 

Also, back to membership:

Can IPrincipal be serialized? I see no such attribute in the class definition in MSDN. I'm hoping it can, or that'll immensely complicate things.

bcdennis replied on Monday, June 19, 2006

re: Categories
Sounds like a good plan.

re: Membership
yeah pretty sure it can be.

devbanana replied on Monday, June 19, 2006

hi,

 

Excellent. Big Smile [:D]

 

Yes, I hope so. I'll have to test it so I can be sure.

 

This is my first project using CSLA, so I'm still ironing out some things.

 

By the way, this forum doesn't keep you logged in, even when you check the appropriate box. Sad [:(] Quite annoying.

Copyright (c) Marimer LLC