maonet technotes

IOC(SM):MOC(RM):TDD(NU):SCC(TFS):ORM(L2S):JSL(Jq):CIS(CC)

Archive for the ‘CSLA’ Category

IRepository

Posted by Frank Mao on February 11, 2009

I was trying to write a similar post as Mike did. Because I’m currently using LINQ DynamicQuery, so my IRepository looks like this:


    public interface IDynamicQueryable<T>
    {
        IEnumerable<T> FindAllByCriteria(string criteria);
        IEnumerable<T> FindAllByCriteria(Func<T, bool> criteria);
    }

    // Most simple repository implement this one.
    public interface IRepository<T> 
    {       
              T FindById(int id);
              T Insert(T data);
              T Update(T newData, T oldData);
              void Delete(T data);
     }

    // Some fancy entities implement this one instead.
    public interface IDynamicQueryableRepository<T> : IDynamicQueryable<T>, IRepository<T>{}

I spilt the dynamic part from IRepository, just trying not annoy developer create too many never used FindAll() method in their entities. And also I think this is closer to Interface segregation Principle (not really sure). But this indeed gave me flexibility to control the function diversity on different entities, e.g., some entities might never need list-editing/retrieval function.

I do like this LinqDynamicQuery very much and don’t mind to make my repository coupled to Func<T, bool> (what’s this indeed? And what’ the difference than Expression<Func<T, bool>> shown in Fluent NHibernate’s IRepository?)  Here is a good example, if no DynamicQuery:


    public interface ICityRepository : IDynamicQueryableRepository<CityBO>
    {
        IEnumerable<CityBO> FindAllByProvinceId(int provinceId);
        IEnumerable<CityBO> FindAllByCountryId(int countryId);
    }

By using DynamicQuery,


    public class CityListFactory : BusinessListBaseServerFactory<CityBOCollection, CityBO, ICityRepository>, ICityListFactory
    {
        public CityBOCollection Fetch(CountryBO parent)
        {
            // Don't need FindAllByCountryId() any more.
            return Fetch(x => x.CountryId == parent.CountryId);
        }
        public CityBOCollection Fetch(ProvinceBO parent)
        {
            return Fetch(x => x.ProvinceId == parent.ProvinceId);
        }
    }

Posted in CSLA, Linq | Tagged: | Leave a Comment »

Extract Validation rules out from CSLA BO

Posted by Frank Mao on February 6, 2009

The first priciple of SOLID rules is SRP, it would be nice if we can extract validataion rules out from BO. And I couldn’t belive it was so easy to do implement.

The classic way:


private void AddCustomRules()
        {
            // Custom validation
            ValidationRules.AddRule<EmailBO>(EmailRoleValidValues<EmailBO>, EmailRoleProperty, 1);

        }

The revised version:


// EmailBO
   protected override void AddBusinessRules()
   {
      EmailRuleFactory.CreateOn(ValidationRules);
   }

public class EmailRuleFactory
{
   public static void CreateOn(ValidationRules rules)
   {
      rules.AddRule<EmailBO>(EmailRoleValidValues<EmailBO>, "EmailRole", 1);
   }

   private static bool EmailRoleValidValues<T>(T target, Csla.Validation.RuleArgs e) where T : EmailBO
   {...}

All the validation tests still passed!

Note: I had to expose protect Parent field from BusinessBase. And PropertyInfo is not available either, fortunately ValidationRules.AddRule() can still take string literal.

This might also be helpful is user wants to switch between different sets of valudation rule based on certain instance value.

We could do the same thing to AuthorizationRules which will be more meaningful. Security should definitely be built as a separated common service, instead of building into Domain Object.

Posted in CSLA | Leave a Comment »

Enable DataGrid Header-Clicking Sorting

Posted by Frank Mao on January 30, 2009

Most search results  said the header-clicking-sorting feature comes internally with dotNet Framework. But for some reason I just could make this happen, until I read an old article on MSDN about custom data binding, which kept mentioning about Rocky. And the reason is very simple, programmer should bind a SortableBindingList<T> to dataGrid to make use of those internal sorting features.

Csla.EditableRootList has to be wrapped in Csla.SortedBindingList then to bind to dataGrid.

SortedBindingList has most of the functions embeded including: Add, Insert, Delete, but not Save. I need to convert SortedBingList back to EditableRootList by using it’s internal property: SourceList.

Rocky didn’t make SortedBindingList serializable for some reason. Anyway, the UI code is very simple:


        public CountryBOCollection List
        {
            get
            {
                return (CountryBOCollection)
                  ((SortedBindingList)this.countryBOCollectionBindingSource.DataSource)
                      .SourceList;
            }
            set
            {
                this.countryBOCollectionBindingSource.DataSource
                    = new Csla.SortedBindingList(value);
            }
        }

I tried to move this type conversion to presenter, then I realized I need two lists in presenter: one sortable list for displaying and editing, one savable list just for saving. Presenter code is polluted just by this limitation in SortedBindingList. It looks much better by leaving type conversion still in UI, and who knows someday this limitation might go away?

Posted in CSLA, WinForm | Leave a Comment »

Csla.ObjectFactory III – What’s wrong with UpdateChildren()

Posted by Frank Mao on December 19, 2008

Derick Bailey’s post about Csla vs DDD brought out a simple but very interesting question :

Which one is correct?
Person.AddCertificate(), or Person.Certificates.Add()?

Almost the same? But it turns out the 2nd one is NOT “correct”, at least, not in DDD way.

That’s the problem in CSLA. Always thinking in table, data-centric design/dev. An extreme example, FieldManager.UpdateChildren().

How about save? Person.UpdateEmail() or Person.Email.Update(). [Email.Child_Update() in classic CSLA way.]

CSLA is not an ORM tool/framework, but BO carries all the dirty flags, in fact it’s DataPortal doing all the relationship maintenance work. RootBO.Save() is handling all the updates of its children, through FieldManager.UpdateChildren() jumping into each childBO’s internal private Child_XYZ methods. Handy, “clean”, but it’s not “correct”.This is the exactly same problem when I was trying to switch to the new Csla.ObjectFactory way: I had to keep an extra set of Protal methods in child BO, those child portal method,  to work with Parent BO’s FieldManager.UpdateChildren() method.

What if we ignore FieldManager completely? Just let Person.Save() directly call DataPortal.Update<EmailBO>(person.Email) ? Or DataPortal.Update<TelephoneBOCollection>(person.Telephones) ? Then child BO can still use  the general AbstractFactory which only have root level CRUD methods.

The direction Rocky made in ObjectFactory looks right, child CRUD should be done by root BO.  The thing is, it’s not backwards completable. It’s trying to retire FieldManager.UpdateChildren()!

The update route should be RootBO.Save -> Root Factory.Update -> Child Factory.Update, don’t call child BO’s internal Child_XYZ anymore.

Currently for legacy reason I still keep the following code in my ChildBO to bump calls from parent to child’s own factory, those UpdateChildren() in root BO can be alive for awhile, until I move them to root factory, which should be the newly strongly-typed customized UpdateChildren().


private void Child_DeleteSelf(){ DataPortal.Delete(this);}

private void Child_Insert(IParent parent){
     this.ParentId = parent.Id;
     DataPortal.Update(this);
}

private void Child_Update(){
     var newBO = DataPortal.Update(this);
     DataMapper.Map(newBO, this);
}

It might just looks like a wrapper trick, but those DataPortal calls are actually in Factory class now which indeed is in an external true service layer.

In my app, it’s one AbstractFactory for all BOs, both root and child.

I do have a problem now, where should I put transaction scope attribute? It shouldn’t be a big deal, possible solutions:

  1. Create some extra methods in factory not in transaction Scope dedicated for child methods,
  2. Unit of Work pattern?

Look back Ryan’s ProjectTrackerHybrid code, all the child propertis are not registered! Which means they are not in fieldmanager at all. It’s ORM’s job to handle the realationship UpSet (Update/Insert).

Posted in CSLA, DDD | 1 Comment »

Csla.ObjectFactory Part II – Difficulties on Child BO

Posted by Frank Mao on December 15, 2008

I was wondering  why CslaFactory only needs to implement CRUD, no child methods at all. Other developers post the  same question on forum.

Rocky’s answer made sense, it your own Facotory’s responsibility to handle all those relationship. My specific problem on this is, FiledManager.UpdateChildren() is not available any more.

First, the FieldManager property in BO is protected, this can be fixed by adding new method in our BaseBO to make it public. The root BO changed to CslaFactory then. But, next challenge is, this UpdateChild is not going through DataPortalSelector->DataPortal_XYZ route, it still uses Reflection to look up the ‘Child_XYZ” method right in your child BO, not in CslaFactory class at all!

You have to manage your own update route by looping through all the children from your root BO! Forget the powerful FieldManager.UpdateChildren().

Relationship is built in DAL. ?? When start using DTO, I know it is the dead weakness. The word around is pulling relationship up to Csla BO and rely on those magic UpdateChildren methods. Now, updateChildren is gone, I have to create a real business explicit UpdateChildren.


    public class AbstractObjectFactory<BO, TRepository, DTO> : ObjectFactory, IBusinessBaseServerFactory<BO>
        where BO : MyBusinessBase<BO>
        where TRepository : IRepository<DTO>
        where DTO : class, new()
    {
        protected IRepository<DTO> _repository = StructureMap.ObjectFactory.GetInstance<TRepository>();

        public virtual BO NewBO()
        {
            return (BO)Activator.CreateInstance(typeof(BO), true);
            //            return new BO();
        }

        public virtual void InsertChildren(BO bo){}
        public virtual void UpdateChildren(BO bo){}
        public virtual void DeleteChildren(BO bo){}

        public virtual BO Create()
        {
            var obj = NewBO();
            MarkNew(obj);
            obj.GetValidationRules().CheckRules();
            return obj;
        }

        public virtual BO Insert(BO bo)
        {
            var data = _repository.Insert(bo.BuildDTO<DTO>());
            if (data == null) throw new DataException("Repository Insert return null.");
            bo.LoadFromDTO<DTO>(data);

            InsertChildren(bo);
            // FieldManagar().UpdateChildren(bo) is not through CslaFactory.
//            bo.GetFieldManagar().UpdateChildren(bo);

            return bo;
        }

...

    public class StakeholderFactory : AbstractObjectFactory<StakeholderBO, IStakeholderRepository, StakeholderDTO>
        , IStakeholderFactory
    {
        public override void InsertChildren(StakeholderBO bo)
        {
            // Update Alias Name Child
            var factory = new StakeholderAliasNameFactory();
            for (int i = 0; i < bo.AliasNames.Count; i++)
            {
                bo.AliasNames[i] = factory.Insert(bo.AliasNames[i]);
            }
        }

This looks weird. And my solution of mocking Parent for testing update Child is useless then, because that assume root BO is calling FiledManager.UpdateChildren. One solution is to  re-write those update children code into my CslaFactory class. More coding, does it look more clear? I’m not sure. One of my root BO has 7+ children, I don’t think it’s worth to move.

Another quick dirty fix might be, still leave those Child_XYZ methods in Child BO for legacy purpose, inside put DataPortal_XYZ to redirect call to CslaFactory. This solution can keep parent update code same as before, still using UpdateChildren(), also the Child BO will look more like a switchable BO.

Enough, now I fully understand Ryan’s statement: Csla is great, but we need relationship.

Look how Ruby and NH dealing with relationship: has_many, belongs_to. Simple and clear.

Posted in CSLA | 6 Comments »

Csla.ObjectFactory

Posted by Frank Mao on December 10, 2008

This is  a new feature from Csla 3.6, which is trying to make Csla more TDD friendly.

Benefits:

DataPortal_XYZ methods can be moved into your own ObjectFactory class, BO will look a little bit thinner. Yes, you can now make BO contructor to be public, ( in standard csla way BO’s ctor should be private), if you are tired of using Activator.CreateInstance.

Because now you have full control to this ObjectFactory, and this class is not static, you can mock it when unit-testing UI/Presenter. But, the default usage of ObjectFactory doesn’t support Interface. Fortunately,  Rocky allow you to create your own FactoryLoader, by setting it into your app.conifg to redirect:

<appSettings>
<add key=”CslaObjectFactoryLoader” value=”YourNameSpace.YourFactoryLoader, YourAssembly” />

Ryan Kelley is using this tech to create his GenericFactoryLoader in his ProjectTrackerHybrid demo.

We have lots of Stored Procedures to deal with, so create a base AbstactObjectFactory is very hard. So I changed Ryan’s GenericFactoryLoader to make it not taking generic interface.

Here is what I tried:


        public virtual Type GetFactoryType(string factoryConnectionString)
        {
            if (factoryConnectionString == null)
                throw new ArgumentNullException("factoryConnectionString");

            parser.Parse(factoryConnectionString);

            var factoryTypeName = parser.FactoryType;

            // parser doesn't work , use csla default ObjectFactoryLoader
            if (string.IsNullOrEmpty(factoryTypeName))
                return new ObjectFactoryLoader().GetFactoryType(factoryConnectionString);

            return Type.GetType(factoryTypeName);
        }

        public virtual object GetFactory(string factoryName)
        {
            var factoryType = GetFactoryType(factoryName);
            if (factoryType.IsInterface)
                return StructureMap.ObjectFactory.GetInstance(factoryType);

            return new ObjectFactoryLoader().GetFactory(factoryName);

        }

[CslaFactory("Factory Type=MyFactory.ICityFactory")]
// Classic way to declare ObjectFactory
//    [CslaFactory("MyFactory.CityFactory, MyFactory")]
[Serializable()]
public class CityBO :BusinessBase<CityBO>
{...}

Posted in CSLA | 2 Comments »

Should always ignoreArgument when AssertWasCalled

Posted by Frank Mao on November 28, 2008

I should have added this option in my test earlier, then I won’t waste 2+ hours today. The reason causing problem was very simple, AssertWasCalled() method in current verion RhinoMocks (3.5.0.1337 RC) doesn’t action consistently. I think it’s because comparing the object reference equalty. I had to add options=>options.IgnoreArguments() to get around from this.

Here is a piece of my MVP + CLSA test. Struggled for a while with Property setter, thanks God, it isn’t that hard.


    [TestFixture]
    public class When_initializing_view : EstablishContext
    {
        [Test]
        public void should_call_view_setter_if_id_exists()
        {
            // Arrange
            _mockOrganizationRepository.Expect(x => x.FindById(1)).Return(_stubOrganizationDTO);

            _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();

            _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);

            // Action
            _presenter.InitViewFor(1);

            // Assert
            _mockView.AssertWasCalled(x => x.CurrentBO = null,
                options =>options.IgnoreArguments() );
        }
    }

I then created a generic EditDetailsPresenter, worked as I expected with one concern left: How to mock DataPortal directly. Currently I am still mocking repository. Ideally I should mock DataPortal instead, otherwise I have to make sure there is at least one BO is fully functional.

Mocking DataPortal is very hard, because those Fecth, Create, Update and Delete are all static methods, I had to create an adapter to wrap those static methods, then using StructreMap again inside adapter enable switching between DataPortal and mock DomainDAO, here is what I have tried:

  1. Create DomainDAO and IDomainDAO,
    
    public class DomainDAO<BO> : IDomainDAO<BO>
    {
    public BO Fetch(object criteria)
    {
    return DataPortal.Fetch<BO>(criteria);
    }
    
    public BO Create()
    {
    return DataPortal.Create<BO>();
    }
    
    public BO Update(BO bo)
    {
    return DataPortal.Update<BO>(bo);
    }
    
    public void Delete(object criteria)
    {
    DataPortal.Delete<BO>(criteria);
    }
    }
    
    public interface IDomainDAO<T>
    {
    T Fetch(object criteria);
    T Create();
    T Update(T bo);
    void Delete(object criteria);
    }
    
  2. Create DAOManager to Get DomainDAO:
    
    public static class DAOManager
    {
    public static IDomainDAO<BO> DomainDAO<BO>()
    {
    return ObjectFactory.GetInstance<IDomainDAO<BO>>();
    }
    }
    
  3. Change classic factory method in BO from DataPortal to this new DAOManager.
    
    public static OrganizationBO GetOrganizationBO(int stakeholderId)
    {
    return DAOManager.DomainDAO<OrganizationBO>().Fetch(new SingleCriteria<OrganizationBO, int>(stakeholderId));
    //            return DataPortal.Fetch<OrganizationBO>(new SingleCriteria<OrganizationBO, int>(stakeholderId));
    }
    
  4. The presenter testing code can be changed to:
    
    [SetUp]
    public void SetUp()
    {
    _mockService = MockRepository.GenerateMock<IDomainDAO<OrganizationBO>>();
    ObjectFactory.Inject<IDomainDAO<OrganizationBO>>(_mockService);
    }
    
    [TearDown]
    public void TearDown()
    {
    ObjectFactory.ResetDefaults();
    }
    
    [TestFixture]
    public class When_initializing_view : EstablishContext
    {
            [Test]
            public void should_call_view_setter_if_id_exists()
            {
                // Arrange
                // We don't need to set return value here.
                _mockService.Expect(x => x.Fetch(new SingleCriteria<OrganizationBO, int>(68190)));
    
                _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();
    
                _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);
    
                // Action
                _presenter.InitViewFor(68190);
    
                // Assert
                _mockView.AssertWasCalled(x => x.CurrentBO = null,
                    options => options.IgnoreArguments() );
            }
    
            [Test]
            public void should_set_error_msg_in_view_if_id_not_exists()
            {
                // Arrange
                Exception ex = new Exception("Data Not Found", new DataNotFoundException());
                _mockService.Expect(x => x.Fetch(new SingleCriteria<OrganizationBO, int>(1)))
                    .IgnoreArguments()
                    .Throw(new DataPortalException("DataPortal.Fetch failed.",
                        ex,
                        null)); // See, we don't have to mock BO here, unless you try to read BO through exception in UI.
    
                _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();
    
                _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);
    
                // Action
                _presenter.InitViewFor(1);
    
                // Assert
                _mockView.AssertWasCalled(x => x.ShowError(null),
                    options => options.IgnoreArguments() );
            }
    

Fetch can be done this way, but I can’t mock Save() because it’s deep in Csla, unless I can override BusinessBase.Save() which is another mission impossible (too many internal classes involved). To make Csla TDD friendly, let ask for TypeMock to save us out.


 [TestFixture]
    public class When_view_firing_save_event : EstablishContext
    {
        [Test]
        public void should_set_success_msg_and_set_new_BO_in_view_if_save_new_bo_succeed()
        {
            // Arrange
            OrganizationBO orgBo = DataPortal.Create<OrganizationBO>();
            orgBo.Name = "test Org";
            Assert.That(orgBo.IsValid, orgBo.GetValidationMessage());

            _mockView = MockRepository.GenerateMock<IEditDetailsView<OrganizationBO>>();
            _mockView.Stub(x => x.CurrentBO).Return(orgBo).Repeat.Any();

            _presenter = new EditDetailsPresenter<OrganizationBO>(_mockView);

            // This won't work, because BusinessBase.Save() is tightly coupled to DataPortal.
            //_mockService.Expect(x => x.Update(orgBo)).IgnoreArguments();

            // Use TypeMock to mock DataPortal.Update() inside BusinessBase.Save()
            using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
                  // CAUTION: ALL calls here are mocked!!!
                  recorder.DefaultBehavior.IgnoreArguments();
                  recorder.ExpectAndReturn(DataPortal.Update(orgBo), orgBo);
            }

            // Action
            _mockView.Raise(x => x.RequestSave += null, this, EventArgs.Empty);

            // Assert
            _mockView.AssertWasCalled(x => x.CurrentBO = null,
                                      options => options.IgnoreArguments());

            _mockView.AssertWasCalled(x => x.ShowMessage(null),
                                      options => options.IgnoreArguments());

    }

We can remove DomainDAO then if using TypeMock to mock DataPortal.

The new ObjectFactory in CSLA 3.6 seems can make this mock easier, looks like your own ObjectFactory is not static class anymore, different than DataPortal. By creating your own IDataPortal or IObjectFactory, as Ryan did,  mocking those service layer objects should become possible. Someday I will look into this CSLA.ObjectFactory thing if we aren’t going to TypeMock. Rocky’s statement indicates this will be a lot of work:

It is very important to realize that when using this factory model, the data portal does virtually nothing for you. It doesn’t automatically call MarkOld(), MarkNew() or MarkAsChild(). You assume total responsibility for creating and initializing the business object graph, and for setting the state of all objects in the graph.

But it seems Ryan’s ObjectFactroy sample code already finished most of them. One thing doesn’t fit our situation is, we need IRepository<> changed to IDomainObjectRepository in Factory.


        private readonly IRepository<T> _repository;
        public BusinessBaseServerFactory(IRepository<T> repository)
        {
            _repository = repository;
        }

So far, I think unless CSLA.ObjectFactory can provide an optional attribute something like “RepositoryType”, I have to create One ObjectFacotry per BO. Our libaray will become more messier.

Posted in CSLA, MVP, mock, test | Tagged: | Leave a Comment »

Seperate concern between parent and child

Posted by Frank Mao on November 21, 2008

Do we need to separate concern between parent object and its child? Say we have a stakeholder BO, which can have multiple email BOs, (an email BO collection). Then where should we place those specs/unit-tests for email BO?

Because emailBO is a child BO, the normal way to save it should be only through stakeholderBO.Save(). It’s possible that stakeholderBO might still be work-in-progress, or having problem caused by other childBO, e.g., telephoneBO. Or more likely, it’s not finish yet. Can we still work on EmailBO without worry too much about it’s parent – StakeholderBO?

So seperate concern is necessary. We need an interface to implement design-by-contract.

Create an IStakeholder interface, mock it in emailBO unit-test. (Thanks to Rhino, we don’t need worry about data portal any more. ) But we need to explicitly call SetParent() in emailBOCollection to simulate the LoadProperty behavior in stakeholderBO.

The test code:


            // Arrange
            var emailRepository = MockRepository.GenerateMock<IEmailRepository>();
            ObjectFactory.Inject<IEmailRepository>(emailRepository);

            EmailDTO OneValidTestDTO = new EmailDTO()
                                           {
                                               Address = "2@d.c", EmailRole = "Home",
                                               StakeholderId = 123, StakeholderEmailId = 456
                                           };

            emailRepository.Expect(x => x.Insert(null)).IgnoreArguments().Return(OneValidTestDTO);
            // or play the new inline constraints syntax
            emailRepository.Expect(x => x.Insert(Arg<EmailDTO>.Is.Anything)).Return(OneValidTestDTO); 

            IStakeholder mockStakeholderBO = MockRepository.GenerateMock<IStakeholder>();  

            // Action
            EmailBOCollection emails = EmailBOCollection.NewEmailBOCollection();

            // SetStakeholder is created in EmailBOCollection to call private SetParent through reflection.
            // This is optional because the second parm in DataPortal.UpdateChild() will be passed
            // to child_XYZ() as the parameter which is the real parent.
            // emails.SetStakeholder(mockStakeholderBO);   // IStakeholder should extend csla.IParent.

            var newEmail = emails.AddNew();
            // Make this new EmailBO valid.
            newEmail.Address = "1@dc.com";
            newEmail.EmailRole = "Home";

            Assert.That(emails[0].BrokenRulesCollection, Is.Empty, emails[0].BrokenRulesCollection.ToString());

            // this is to simulate the parent.Save() in the real world,
            DataPortal.UpdateChild(emails, mockStakeholderBO);
            // We don't want and can't call stakeholder.Save() here

            // Assert
            Assert.That(emails[0].StakeholderEmailId == OneValidTestDTO.StakeholderEmailId);
            Assert.That(emails[0].StakeholderId == OneValidTestDTO.StakeholderId);  

            // Reset to default
            ObjectFactory.ResetDefaults();

Unfortunately, SetParent(IParent) is a private method in IEditableObject and IEditableCollection. I had to create a public wrapper in my base class.


        public void SetStakeholder(IParent parent)
        {
            object[] parm = new object[] { parent };

            var method = this.GetType().GetMethod("Csla.Core.IEditableCollection.SetParent", BindingFlags.Instance | BindingFlags.NonPublic);

            method.Invoke(this, parm);
        }

But, later I figured out it’s not necessary, only if I am doing some parent-related validation in emailBO, say, an Organization type stakeholder can not have home email role.

Dataportal.Update() method is key to simulate parent.Save(), this old (3.0) syntax works great in my mock/TDD solution to CSLA.

Posted in CSLA, mock, test | 1 Comment »

Static field disappears on WCF

Posted by Frank Mao on November 12, 2008

I spent 10+ hours on this problem, ‘Specified cast is not valid’ on a CSLA library deployed onto WCF host. I figured out this is caused by inherited properties in BO were lost. The propertyInfo list is different between server and client, one side (server) doesn’t contain those properties from baseBO.

More details, BO can be retrieved from WCF, change one of it’s property, save BO, then this problem occured. Debug turns out before saving, the client side still had a full list of properties including inherited ones, but after serilizing-deserilizing, the one transfered to server side magically lost those base properties should inherite from ancestor!

We know that by adding a dummy static field can fix the similar problem, but that only works in non-WCF mode. Why not in WCF mode? Somewhere CSLA and WCF/dotNet static fields doesn’t like each other, again.

I am tired of this kind of issues, fortunatelly, I found a work around.

Always keep a non-RunLocal DataPortal_Create method in BO, and create a NewYourBO() factory method calling this DataPortal_Create(). When you get this ‘Specified cast is not valid’ problem, just try to add this NewYourBO factory method right before your fetch operation. For a collectionBO, it should be in GetYourBOCollection()

internal static YourBOColleciton()
{
if (Csla.ApplicationContext.DataPortalProxy.Contains(”WcfProxy”)) YourBO.NewYourBO();
return DataPortal.FetchChild<YourBOCollection>();
}

For a non-collection BO, it should be in GetYourBO():

internal static GetYourBO()
{
if (Csla.ApplicationContext.DataPortalProxy.Contains(”WcfProxy”)) NewYourBO();
return DataPortal.FetchChild<YourBO>();
}

Looks like my extra code are doing a initialize work, setting up on both server and client side to make sure both side have the full property list ready.

Someday I will come back with a explaination.

Most of my problemic BOs are child type. I tried using DataPortal_CreateChild instead of DataPortal_Create, it failed. Perhaps the CreateChild one doesn’t return anything to client side.

There is a similar problem when moving multi-level inheritance to WCF host. The properties in middle level also be lost when fetching data down to client side. Similar workaround is to call the local constructor right before retrieve.

internal static GetYourBO()
{
if (Csla.ApplicationContext.DataPortalProxy.Contains(”WcfProxy”)) {
new YourBO(); // to trigger static field in local contrustctor
// Or Activator.CreateInstance(typeof(YourBO<T>), true);
NewYourBO(); // to trigger static field in remote constructor
}
return DataPortal.FetchChild<YourBO>();
}

Update:

Bought the newest eBook from Rocky, the solution is in OnDeserialized. We had to added base.OnDeserialized(context) because we are doing multi-level inheritance. Thanks Kevin, Thanks Rocky.

private static int _init; // This must be a private type, even protected type won’t work.
protected CommentBOBase()
{
_init = 0;
}

protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
base.OnDeserialized(context);
_init = 0;
}

My experience is, we only need to add this piece of hacking code to those base classes with static fields declared. The inherited class doesn’t need to do this trick.

Related posts:

http://forums.lhotka.net/forums/thread/24472.aspx
http://forums.lhotka.net/forums/post/26822.aspx
http://forums.lhotka.net/forums/thread/26849.aspx

Posted in CSLA, WCF | 4 Comments »

When Parent-Child meets Inheritance

Posted by Frank Mao on November 4, 2008

The right way to do inheritance in CSLA BO should be always adding generic in base/ancester’s class declaration.

e.g., instead of

class Car :BusinessBase {}

we should

class Car<T> :BusinessBase where T: BusinessBase<T> {}

Note: You will encounter the famous “dummy” staic field problem of CSLA.

The benefits of this includes,

  1. Don’t have to override base.Save().
  2. The registered property in base will get the correct type of inheritor.
  3. Child properties will always be aware the correct inheritor’s type T. (Maybe they shouldn’t)

But, this will cause the child_insert() much harder to write if you have child properties in base Car BO. This is invalid:

private void Child_Insert(Car<> parent)  {…}

And this doesn’t work either:

private void Child_Insert(object parent)  {
// Only check one level inheritance, to do recursive check, see details in this post.

if (!(parent.GetType().BaseType.GetGenericTypeDefinition()== typeof(Car<>)))
throw new NotSupportedException(”….”);

// Set parent id  , this line won’t compile.
LoadProperty<int>(CarIdProperty, ((Car<>)parent).CarId);

}

I can add generic to those child BO, but that looks too ugly, like Tire<T>, Seat<T>, they should be general, same to all the Cars.

Another solution is, creating an ICar interface.

private void Child_Insert(ICar parent)  {…}

It looks strange, but actually this can make child BO code much eaisier to write.

Posted in CSLA | Tagged: | Leave a Comment »