TypeCasted

My .Net Adventure

Archive for the ‘CSLA’ Category

CSLA Mixed Metaphors

leave a comment »

In CSLA there exists a mixed metaphor a constant struggle between the principals of data centric design and pure object oriented design. This is most apparent when contrasting the ReadOnlyListBase and the BusinessBase class. One is essentially a simple data transfer object, and the other a fully functional Business Object. Often times during the course of a development effort these two paradigms collide. Even with very simple object models this can be a problem. Take for example a simple scenario with a Customer object that contains a CustomerType child object both of the BusinessBase variety.

Customer CustomerType Class Diagram

Customer CustomerType Class Diagram

The domain rules for this model require simply that when creating a new Customer object or editing an existing Customer object a cutomer name and its associated CustomerType must be provided. To support the creation of a new customer I have created a simple screen I call CustomerDetail. This screen allows the user to provide a name for the Customer as well as an associated CustomerType.

Customer Detail

Customer Detail

The screen itself appears very simple. Though simple, the ComboBox control holding the possible CustomerTypes gives us our first look at the mixed metaphor. Here is why. When filling the ComboBox control for the CustomerType selection I chose to use a object derived from ReadOnlyListBase named CustomerTypeInfoCollection. For performance reasons this is the approach I will usually use when filling ComboBox controls. The alternative (objects derived from BusinessBase) generally will have much larger object graphs. Aside from the memory footprint they consume often times they make several round trips the database to populate a single instance. Because of these concerns it makes the most sense to me to create ReadOnlyListBase objects to manage these kinds of controls. Looking at the code below you can see I am setting the ItemsSource of the ComboBox to the CustomerTypeList property of the screens DataContext (in this case the DataContext is the windows code behind).

 
    <Label Grid.Row="1" Margin="0,2,0,0" Grid.Column="0">Customer Type:</Label>
    <ComboBox IsReadOnly="True" ItemsSource="{Binding Path=CustomerTypeList}" 
                SelectedValue="{Binding Path=Customer.CustomerType, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource CustomerTypeToIdConverter}}" 
               SelectedValuePath="Id" DisplayMemberPath="Type" 
               Grid.Row="1" Margin="0,2,0,0" Grid.Column="1" Width="150" Name="TypeControl" Height="24">
    </ComboBox>

If you look at the xaml you will notice something is just a bit off. The ItemsSource property of the ComboBox control is bound to a property called CustomerTypeList. As stated earlier CustomerTypeList is a ReadOnlyListBase object. The SelectedValue is bound to a property called CustomerType. CustomerType is a BusinessBase object. Making the xaml more confusing the SelectedValuePath is set to Id (id is an integer from the CustomerTypeInfoItem object). By reading the xaml and considering what the databinding engine is likley to do the inferred pseudo code would look something like this.

int customerTypeId = CustomerTypeList[SelectedItem].Id;
Customer.CustomerType = customerTypeId;

By looking at the xaml above it appears that I am telling the databinding system to assign an int to a CustomerType object when the ComboBox selection is changed. This obviously will not work for many reasons. Thankfully to resolve this issue WPF data binding gives us the ability to use ValueConverters and we can use them to manage this conflict. Correcting this issue is requires two things to happen. Firstly when selecting an item in the ComboBox we need to take the Id from the selected ReadOnlyBase CustomerTypeListItem and somehow get a BusinessBase CustomerType with that id. Secondly when loading the screen for editing a Customer we have to somehow give databinding the Id of the CustomerType BusinessBase object so the selection in the ComboBox control is set to the correct item. Converters give the solution for both of these hurdles. Lets take a look.

    public class CustomerTypeToIdValueConverter : IValueConverter
    {
        #region IValueConverter Members

        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            CustomerType customerType = value as CustomerType ;
            if(customerType != null)
            {
                return customerType.Id;
            }

            return 0;
        }

        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value that is produced by the binding target.</param>
        /// <param name="targetType">The type to convert to.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if((int)value == 0)
            {
                return null;
            }
            return CustomerType.GetAsChildById((int) value);
        }

        #endregion
    }

Not only does this ValueConvert take care of the briding the gap between a ReadOnly object and a BusinssObject it also allows us to fully leverage the IDataErrorInfo implementation in CSLA. Because our SelectedValue can remain bound to a BusinessObject we can simply tell databinding to ValidateOnDataErrors and we get full support for business rules and validation. And I think that is pretty darn cool.

Thanks to Jim Brown for helping me get my thoughts organized for this post.

Enjoy!

Written by brettedotnet

July 2, 2009 at 3:17 am

Integration Testing Child Objects

leave a comment »

When writing integration tests for your CSLA domain some choices about how to deal with child objects need to be made. For example, do you test all your child objects through their parents? Do you attempt to isolate the child by somehow mocking the parent? Do you just ignore child objects all together? Or if you are like most places you just do not write integration tests or unit tests (Yes it is true). I lean towards testing all objects in isolation. If you don’t isolate an object when testing it you really are not getting the feedback you need to understand what problems may exists.

So how do you isolate the data portal methods in a child CSLA object?

The approach I generally take is to create a fake editable root object. This object is a BusinessBase object that leverages method dependency injection enabling you to inject child objects (and parent objects) into it. You can then call save on the fake root object allowing the CSLA framework to delegate the calls to the child objects as it normally would. Let’s take a look at how that might look in a test.

/// <summary>
/// Tests ChildDataPortal.Insert
/// </summary>
[TestMethod]
public void TestCreateAndSaveNewChild()
{
    //Create instance of the Object Under Test
    Department department = Department.CreateAsChild("TEST");
    department.Description = "Test Department";

    //Create instance of FakeEditableRoot
    FakeEditableRoot fake = FakeEditableRoot.Create();
    fake.InjectChild(department);
    fake = fake.Save();

    //Get the saved child
    department = fake.ChildObject as Department;

    //Assert
    Assert.IsTrue(Department.Exists("TEST"));
    Assert.IsTrue(department.Id == "TEST");
    Assert.IsTrue(department.Description == "Test Department");
}

So what does FakeEditableRoot look like? Sadly it is not very interesting but take a look if you like.

/// <summary>
/// Fake CSLA Root. Use this to test child object and their data portal operations
/// </summary>
[Serializable]
public sealed class FakeEditableRoot : MyBusinessBase<FakeEditableRoot>
{
    /// <summary>
    /// The child object under test
    /// </summary>
    private static PropertyInfo<BusinessBase> ChildObjectProperty = RegisterProperty<BusinessBase>(typeof(FakeEditableRoot), new PropertyInfo<BusinessBase>("ChildObject"));

    /// <summary>
    /// Allows you to determine what 'Parent' object is passed to the child data portal operations
    /// </summary>
    private static PropertyInfo<BusinessBase> ParentObjectProperty = RegisterProperty<BusinessBase>(typeof(FakeEditableRoot), new PropertyInfo<BusinessBase>("ParentObject"));

    /// <summary>
    /// Used for testing child collections
    /// </summary>
    private static PropertyInfo<IExtendedBindingList> ChildCollectionProperty = RegisterProperty<IExtendedBindingList>(typeof(FakeEditableRoot), new PropertyInfo<IExtendedBindingList>("ChildCollectionObject"));

    #region Factory Methods

    /// <summary>
    /// Factory Method used to create a new instance of FakeEditableRoot
    /// </summary>
    /// <returns>New instance of FakeEditableRoot</returns>
    public static FakeEditableRoot Create()
    {
        return new FakeEditableRoot();
    }

    /// <summary>
    /// Sets the parent object to be passed to the data portal operations.
    /// </summary>
    /// <param name="parent">The parent.</param>
    public void InjectParent(BusinessBase parent)
    {
        ParentObject = parent;
    }

    /// <summary>
    /// Injects the child collection to test.
    /// </summary>
    /// <param name="childCollection">The child collection.</param>
    public void InjectChildCollection(IExtendedBindingList childCollection)
    {
        ChildCollection = childCollection;
    }

    /// <summary>
    /// Injects the child object to test.
    /// </summary>
    /// <param name="child">The child.</param>
    public void InjectChild(BusinessBase child)
    {
        ChildObject = child;
    }

    /// <summary>
    /// User to simulate a fetch
    /// </summary>
    /// <returns></returns>
    public static FakeEditableRoot GetById()
    {
        return DataPortal.Fetch<FakeEditableRoot>(null);
    }

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="FakeEditableRoot"/> class.
    /// </summary>
    private FakeEditableRoot()
    {

    }

    /// <summary>
    /// Private constructor to force the Factory Method Pattern.
    /// </summary>
    /// <param name="childObject">The child object.</param>
    private FakeEditableRoot(BusinessBase childObject)
    {
        ChildObject = childObject;
    }

    /// <summary>
    /// Private constructor to force the Factory Method Pattern.
    /// </summary>
    /// <param name="childObject">The child object.</param>
    /// <param name="parentObject">The parent object.</param>
    private FakeEditableRoot(BusinessBase childObject, BusinessBase parentObject)
    {
        ChildObject = childObject;
        ParentObject = parentObject;
    }

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets or sets the child object.
    /// </summary>
    /// <value>The child object.</value>
    public BusinessBase ChildObject
    {
        get
        {
            return GetProperty<BusinessBase>(ChildObjectProperty);
        }
        private set
        {
            SetProperty<BusinessBase>(ChildObjectProperty, value);
        }
    }

    /// <summary>
    /// Gets or sets the child object.
    /// </summary>
    /// <value>The child object.</value>
    public BusinessBase ParentObject
    {
        get
        {
            return GetProperty<BusinessBase>(ParentObjectProperty);
        }
        private set
        {
            SetProperty<BusinessBase>(ParentObjectProperty, value);
        }
    }

    /// <summary>
    /// Gets or sets the child collection object.
    /// </summary>
    /// <value>The child collection object.</value>
    public IExtendedBindingList ChildCollection
    {
        get
        {
            return GetProperty<IExtendedBindingList>(ChildCollectionProperty);
        }
        private set
        {
            SetProperty<IExtendedBindingList>(ChildCollectionProperty, value);
        }
    }

    #endregion

    #region Data Access - Fetch

    /// <summary>
    /// Datas the portal_ fetch.
    /// </summary>
    /// <param name="criteria">The criteria.</param>
    protected override void DataPortal_Fetch(object criteria)
    {

    }

    #endregion

    #region Data Access - Insert

    /// <summary>
    /// Save the current instance
    /// </summary>
    protected override void DataPortal_Insert()
    {
        //Get connection and command
        Database db = new OracleDatabase(ManagedConnectionString.Instance.ConnectionString);
        using (DbConnection connection = db.CreateConnection())
        {
            connection.Open();
            using (DbTransaction transaction = connection.BeginTransaction())
            {
                try
                {
                    if (ParentObject != null)
                    {
                        FieldManager.UpdateChildren(ParentObject, transaction);
                    }
                    else
                    {
                        FieldManager.UpdateChildren(this, transaction);
                    }

                    //Commit Transaction
                    transaction.Commit();
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
        }
    }

    #endregion

    #region Data Access - Update

    /// <summary>
    /// Save the current instance
    /// </summary>
    protected override void DataPortal_Update()
    {
        //Get connection and command
        Database db = new OracleDatabase(ManagedConnectionString.Instance.ConnectionString);
        using (DbConnection connection = db.CreateConnection())
        {
            connection.Open();
            using (DbTransaction transaction = connection.BeginTransaction())
            {
                try
                {
                    //Update Children
                    if (ParentObject != null)
                    {
                        FieldManager.UpdateChildren(ParentObject, transaction);
                    }
                    else
                    {
                        FieldManager.UpdateChildren(this, transaction);
                    }

                    //Commit Transaction
                    transaction.Commit();
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
        }
    }

    #endregion

}

Enjoy!

Written by brettedotnet

June 18, 2009 at 2:16 am

BusinessListBase AuthorizationRules

leave a comment »

I have never been a huge fan of how CSLA handles security. One drawback I have found is that you cannot secure a BusinessListBase without first wrapping it with a BusinessBase. I happen to think it is a perfectly valid use case for someone to call Save() on a root collection. I also think it is perfectly valid for someone to want to secure the CRUD operations at this level. So how can you do it?

First some design choices need to be made. For instance, can a user who is not authorized to call save on a root Customer object call save on a root CustomerCollection object? Or are these two independent user stories that should have separate security contexts? If you think like me you answer yes to the first question and no to the last. That is to say, if a user can’t save a root object the user should not be able to save a root collection of those same objects.

If you have already created you own set of CSLA base classes the solution to this is very easy. By overriding the void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e) method on BusinessListBase you get a framework hook to do anything you so desire before the DataPortal operation is executed. I use it to check the AuthorizationRules! And since I have already stated that if a user can’t save a root object the user should not be able to save a root collection of those same objects, I can delegate the security checks to the actual business object. See my code below.

    [Serializable]
    public class MyBusinessListBase<T, C> : BusinessListBase<T, C>
        where T : BusinessListBase<T, C>
        where C : Csla.Core.IEditableBusinessObject
    {
        #region BusinessListBase overrides

        /// <summary>
        /// Called by the server-side DataPortal prior to calling the
        /// requested DataPortal_xyz method.
        /// </summary>
        /// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
        protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
        {
            base.DataPortal_OnDataPortalInvoke(e);
            CheckCollectionAuthorization(e);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Checks the collection authorization.
        /// </summary>
        private static void CheckCollectionAuthorization(DataPortalEventArgs e)
        {
            if (e.Operation == DataPortalOperations.Fetch)
            {
                if (!Csla.Security.AuthorizationRules.CanGetObject(typeof(C)))
                {
                    throw new System.Security.SecurityException(string.Format("User not authorized to {0} object type {1}",
                                                                              "get",
                                                                              typeof(C).Name));
                }
            }
            else if (e.Operation == DataPortalOperations.Create)
            {
                if (!Csla.Security.AuthorizationRules.CanCreateObject(typeof(C)))
                {
                    throw new System.Security.SecurityException(string.Format("User not authorized to {0} object type {1}",
                                                                              "create",
                                                                              typeof(C).Name));
                }
            }
            else if (e.Operation == DataPortalOperations.Update)
            {
                if (!Csla.Security.AuthorizationRules.CanEditObject(typeof(C)))
                {
                    throw new System.Security.SecurityException(string.Format("User not authorized to {0} object type {1}",
                                                                              "Update",
                                                                              typeof(C).Name));
                }
            }
        }

        #endregion
    }

Written by brettedotnet

June 17, 2009 at 11:20 am

Who’s Your Daddy?

leave a comment »

In CSLA 3.5 Rocky introduced to us for the first time the child data portal. This is a significant change in how we manage child objects in CSLA. To support this shift Rocky leverages the FieldManager object as well as some tricky reflection to ensure that the correct calls to the ChildDataPortal are invoked. Understanding how CSLA determines the proper ChildDataPortal method to invoke is critical for debugging when issues arise with these declarative style operations.

Below is a simple DataPortal_Insert method from a EditableRoot object name Customer. Note the call to FieldManager.UpdateChildren(this, transaction). This call eventually results in the Child_Insert(Customer parentObject, DbTransaction transaction) method being invoked.

#region Data Access - Insert

/// <summary>
/// Save the current instance
/// </summary>
protected override void DataPortal_Insert()
{
    //Create the db connection
    Database db = new OracleDatabase(ManagedConnectionString.Instance.ConnectionString);
    CommandCriteria criteria = new InsertCommandCriteria(this);

    using (DbConnection connection = db.CreateConnection())
    {
        connection.Open();

        using (DbTransaction transaction = connection.BeginTransaction())
        using (DbCommand command = criteria.GetCommand(db, this, ConcurrencyCommandType.Insert))
        {
            try
            {
                //Insert Self
                db.ExecuteNonQuery(command, transaction);

                //Update Children
                FieldManager.UpdateChildren(this, transaction);

                //Commit Transaction
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                Log.FatalFormat("Customer:DataPortalInsert Failure.", ex.Message);
                throw;
            }
        }
    }
}

#endregion

And of course the ChildDataPortal Insert method.

#region Child Data Access - Insert

/// <summary>
/// Save the current instance
/// </summary>
/// <param name="parentObject">The parent object.</param>
/// <param name="transaction">The transaction.</param>
private void Child_Insert(Customer parentObject, DbTransaction transaction)
{
    try
    {
        //Save Address Child
        FieldManager.UpdateChildren(this, transaction);

        Database db = new OracleDatabase(ManagedConnectionString.Instance.ConnectionString);
        CommandCriteria criteria = new InsertCommandCriteria(parentObject.Id, this);
        using (DbCommand command = criteria.GetCommand(db, this, ConcurrencyCommandType.Insert))
        {
            db.ExecuteNonQuery(command, transaction);
            LoadProperty<int>(IdProperty, (int)db.GetParameterValue(command, "DB_KEY_out"));
            LoadProperty<ConcurrencyControlItem>(ConcurrencyControlItemProperty, db.GetConcurrencyControlItemParameterValue(command));
        }
    }
    catch (Exception ex)
    {
        Log.FatalFormat("Child_Insert Failure.", ex.Message);
        throw;
    }
}

So how does CSLA determine what method to call?

1. The FiledManager loops through all of its registered objects looking for child objects that implement IEditableBusinessObject or IEditableCollection.

2. Eventually a call to the GetMethod(Type objectType, string method, params object[] parameters) on the MethodCaller class is made for each child object.

Step two is where the important stuff is. How does Rocky determine what overload of Child_Insert to call? After all there is nothing stopping you from having 51 different Child_Insert methods 1 for each of your daddies! For instance a Address child object may be a child of several Root objects in your domain. Outlined below are the steps Rocky goes through when deciding what overload to call.

Lets try Strongly Typed?
Rocky first attempts to find a method with the same name and same number and types of parameters. Easy enough!

Well how about a little less Strongly Typed
If the first attempt fails to find a method CSLA will look for a method with the same name and the same number of parameters. Hummmm?! This can cause problems. We very well could have 2 methods with the same name and the number of arguments. If this happens CSLA throws and a AmbiguousMatchException.

Fuzzy Time (Not sure why he calls the method ‘FindMethodUsingFuzzyMatching’)
If CSLA throws a AmbiguousMatchException Rocky catches it and attempts a fuzzy match. That is the framework inspects both number of parameters as well as their type.

Can’t Find Jack!
If none of the above result in a match. CSLA says fine! Look for a method with the specified name only.

Now you know the rest of the story.

Written by brettedotnet

May 13, 2009 at 5:51 pm

When is InValid Valid?

leave a comment »

When working with legacy systems. That is when working on making legacy systems not so legacy. You are often times burdened with bad data. Perhaps the system used to enter data 10 years ago did not have any real kind of rule support to enforce clean inserts and Updates. Or perhaps a bad data feed corrupted some of the data and it never got cleaned up. Whatever the reason may be you can run into issues loading that data into a domain model. An example I am dealing with right now describes this issue pretty well.

I have a Employer object that has a child Contact object. The contact data in the database is not up to par with the business rules we enforce in our domain. Thus every time we load the Employer object and its child Contact object the object is invalid (because of broken rules on the Contact object). Generally this is a good thing. The object will be invalid and the broken rules will have to be addressed before the Save operation will execute. There is another issue however. We have a security requirement that prevents all non administrative users from updating the Contact object. Essentially this means that when any user who is not an administrator attempts to update the Employer object they will not be able to save because one of its children is invalid. That is the broken rules in the child Contact object will prevent the save method from executing even when the child is not dirty.

So for this project the answer to the question “When is InValid Valid?” is “When an object is not dirty and has broken rules”.

Thankfully this is pretty simple to implement in our base classes. See below.

        /// <summary>
        /// Gets a value indicating whether this instance is valid.
        /// </summary>
        /// <value><c>true</c> if this instance is valid; otherwise, <c>false</c>.</value>
        public override bool IsValid
        {
            get
            {
                if (!IsDirty)
                {
                    return true;
                }
                return base.IsValid;
            }
        }

Written by brettedotnet

April 15, 2009 at 6:49 pm

CSLA BrokenRules….

leave a comment »

Let me first say that for the most part I am a fan of CSLA. That being said there are some things about the framework that I just really do not like. One of my biggest complaints is that there is no framework support for gathering all of the broken rules in a given object graph. Imagine a parent business object named Customer that has a child collection of invoices.

croppercapture3Imagine you have a user story the requires you to edit a customers invoices through the customer object. A perfectly valid scenario by the way. What you would probably do is create a view that loads a customer by its Id along with the its collection of invoices. Now if you were to say, change some data in a specific Invoice that resulted in a business rule breakage, both the Customer, InvoiceCollection, and the actual Invoice who’s rule your broke would be marked invalid. Because the object graph is now invalid saving the customer object is not possible and rightly so! However the problem arises when you attempt to tell the user what went wrong. The UI of course has access to the BrokenRulesCollection. See below.

Customer.BrokenRulesCollection.ToString();

Sadly however, the BrokenRulesCollection property will only return the broken rules of the Customer object. In our example the Customer has no broken rules! The broken rule(s) are in the child object(s). And there is no framework level support to get them and indicate to the user what went wrong!

So what are you left with?

Reflection?
Framework extension?

Both of these options require some amount of time and planning, and depending on your implementation there is no guarantee of either soultions working with the next version of CSLA.

My Real Gripe

I like to write code, so for me adding this behavior this is not so bad. In fact it is kind of fun. My real issue is a tad more philosophical. If CLSA is going to essentially force you to use the Parent/Child model. If CSLA is going to make framework level decisions based on the specific use of said model. If CSLA is going to prevent the Save() and Delete() methods from executing based on this model. CSLA should supply a simple framework supported way to get an enumerated list of broken rules. Now there may be several perfectly valid reason for why Rocky leaves this out. In fact he has addressed this very question many times on the CSLA forum. However, at the very least give us a method that will do the reflection for us and update this method each time the CSLA framework changes so we don’t have to. After all Rocky is not shy about using reflection. Gripes aside CSLA does not provide this and we need it. So what do we do?

Options

As I see it there are really two approaches. The first is to slide new base class between your CSLA objects and the CSLA framework base classes. This base class can provide a method that will do the needed reflection when asked. This method would have to reflect its fields and recursively traverse the object graph collecting all broken rules in each of its children. This is a very typical approach that I have used with some success on a few projects. Another option is to write an extension method on BusinessBase. The work is essentially the same however this is nice alternative to creating your own set of base classes. Let see how that might look.

        ///
        /// Gets all broken rules of the passed in object and all of its child objects.
        ///
        /// The business object.
        ///
        public static string GetBrokenRuleString(this BusinessBase businessObject)
        {
            //Precondition Check
            if (businessObject == null)
            {
                throw new ArgumentNullException("businessObject", "Parameter data cannot be null.");
            }

            string errorMessage = string.Empty;
            IDataErrorInfo errorInfo;

            //retrun if valid
            if (businessObject.IsValid || !businessObject.IsDirty)
            {
                return errorMessage;
            }

            //Get root rules
            if (!businessObject.IsSelfValid)
            {
                errorInfo = businessObject;
                errorMessage += errorInfo.Error;
            }

            //Lets look at children
            Type businessObjectType = businessObject.GetType();
            PropertyInfo property = businessObjectType.GetProperty("FieldManager", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
            FieldDataManager fieldDataManager = (FieldDataManager)property.GetValue(businessObject, null);
            List children = fieldDataManager.GetChildren();

            //Call on children
            for (int i = 0; i < children.Count; i++)
            {
                BusinessBase child = children[i] as BusinessBase;
                if (child != null)
                {
                    if (child.IsDirty)
                    {
                        if (string.IsNullOrEmpty(errorMessage))
                        {
                            errorMessage += GetBrokenRuleString(child);
                        }
                        else
                        {
                            errorMessage += string.Concat(Environment.NewLine, GetBrokenRuleString(child));
                        }
                    }
                }
                else
                {
                    if (children[i].GetType().BaseType.IsGenericType)
                    {
                        IExtendedBindingList childCollection = (IExtendedBindingList)children[i];
                        foreach (BusinessBase businessBase in childCollection)
                        {
                            if (businessBase.IsDirty)
                            {
                                if (string.IsNullOrEmpty(errorMessage))
                                {
                                    errorMessage += GetBrokenRuleString(businessBase);
                                }
                                else
                                {
                                    errorMessage += string.Concat(Environment.NewLine, GetBrokenRuleString(businessBase));
                                }
                            }
                        }
                    }
                }
            }

            return errorMessage;
        }

And that’s all folks.

Enjoy!

Written by brettedotnet

April 10, 2009 at 9:28 pm

Posted in C# emm like totally!, CSLA

Tagged with

Follow

Get every new post delivered to your Inbox.

Join 40 other followers