I think I am missing the absolute obvious.

I think I am missing the absolute obvious.

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


rxelizondo posted on Wednesday, January 13, 2010

Hi guys,

I am kind of embarrassed to ask this because I must be missing the absolute obvious but here it goes.

Please look at the small example below and tell what would you expect to see printed out on the screen?

*********************************

using System;
using System.IO;
using Csla;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MemoryStream ms = new MemoryStream();
Console.WriteLine(ms.GetType());

Console.WriteLine("-----------------------------");

Foo.FetchChildTest();
Console.Read();
}
}

class Foo : BusinessBase<Foo>
{
public static Foo FetchChildTest()
{
return DataPortal.FetchChild<Foo>("SomeString");
}

private void Child_Fetch(System.IO.MemoryStream ms)
{
Console.WriteLine(ms.GetType());
}
}
}

*********************************


What I am getting is:

System.IO.MemoryStream
-----------------------------
System.String


But according to me, I should be getting:

System.IO.MemoryStream
-----------------------------
System.IO.MemoryStream


What kind of obvious thing am I missing?

Thanks.

RockfordLhotka replied on Wednesday, January 13, 2010

I don't understand why that works at all. I'd have expected .NET to throw some cast exception as it tries to cast the string to a memorystream.

RockfordLhotka replied on Wednesday, January 13, 2010

You can get it to work this way too

using System;
using System.IO;
using Csla;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      MemoryStream ms = new MemoryStream();
      Console.WriteLine(ms.GetType());

      Console.WriteLine("-----------------------------");

      Foo.FetchChildTest();

      Console.WriteLine("-----------------------------");

      var t = typeof(Program);
      var foo = t.GetMethod("foo");
      Csla.Reflection.MethodCaller.CallMethod(new Program(), foo, "hi there");
      Console.Read();
    }

    public void foo(MemoryStream ms)
    {
      Console.WriteLine(ms.GetType());
    }
  }

  class Foo : BusinessBase<Foo>
  {
    public static Foo FetchChildTest()
    {
      return DataPortal.FetchChild<Foo>("SomeString");
    }

    private void Child_Fetch(System.IO.MemoryStream ms)
    {
      Console.WriteLine(ms.GetType());
    }
  }
}

I think it may be because CSLA doesn't actually use reflection. It uses dynamic method invocation, and so creates IL code that directly calls the method. Perhaps some of these cast restrictions don't exist at the .NET runtime level itself, but are constructs of our 3GL programming languages and their compilers.

rxelizondo replied on Wednesday, January 13, 2010

Ok,

I figured it had something to do with the CSLA because no matter what I did, I was not able to recreate a similar behavior otherwise. I would always get a compile error or a runtime exception.

The debugger threw me off because you are actually able to debug the code inside the method until you eventually hit a line that throws the exception.

Anyway, not sure what is going inside the CSLA but I thought that this was also kind of an unexpected behaviors.


using System;
using Csla;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Foo.FetchChildTest();
Console.Read();
}
}

class Good
{
public void DoIt()
{
Console.WriteLine("Good code");
}
}

class Evil
{
public void DoIt()
{
Console.WriteLine("Evil code");
}
}

class Foo : BusinessBase<Foo>
{
public static void FetchChildTest()
{
DataPortal.FetchChild<Foo>(new Evil());
}

private void Child_Fetch(Good g)
{
Console.WriteLine(g.GetType());
g.DoIt();
}
}
}

RockfordLhotka replied on Wednesday, January 13, 2010

While this is academically interesting, it doesn't block normal usage of the data portal, so trying to address it is far from a high priority.

But I did refer this to my colleague Jason Bock, who's always interested in clever IL behaviors, so I'll be interested to see what his thoughts are when he has time to look into it.

If there's an easy tweak to the IL we're creating for the dynamic method invocation that'd be fine. But if CSLA has to do a bunch more reflection or something expensive to detect and block this sort of obscure scenario I'm less interested in solving it.

Though I suppose we could accept a broader breaking change that would be a cheap fix. The data portal tries to find an exact match for the method you call. But if it can't, it falls back to a method with the same number of parameters. This avoids a more expensive operation where I'd have to walk each parameter type to see if the types are convertable, which would almost certainly cause serious perf issues.

But what I could do instead, is just throw an exception if no exact match is found. That would prevent the use of base types in the target methods - which might easily break some existing code - but would block the scenario you are seeing here.

rxelizondo replied on Thursday, January 14, 2010

Unfortunately, my experience is simply not broad enough to offer a meaningful opinion regarding this issue so I will just wait it out and see what solution you come up with.




My simplistic view tells me that we should take whatever performance hit we need to take in order for things to function the way they are supposed to function to be able to avoid these types of scenarios.




Quite honestly, I am surprised that others have not run into this scenario before, seems to me like it would be a somewhat easy mistake to make……




Thanks.

RockfordLhotka replied on Thursday, January 14, 2010

rxelizondo:

My simplistic view tells me that we should take whatever performance hit we need to take in order for things to function the way they are supposed to function to be able to avoid these types of scenarios.

If this wasn't about data access I might agree. Like at the UI layer I have no problem using reflection, because data binding already uses reflection and saving a millisecond here and there rarely matters.

But when it comes to data access perf and efficiency are much more important. Those milliseconds lost are not only lost to the user getting the data, but to other users waiting to leverage the same app server, so the cost is much higher.

Decreasing perf in the data portal is risky - enough decrease and people (rightly so) would question whether to use CSLA at all. So I could make the framework "correct" but entirely useless - and that would certainly not be a win :)

If there's a way to make it correct without a perf cost, that's the way to go. Or maybe lose some features to be correct - again assuming those features don't make the framework useless for a measurable percentage of the user base.

But you must admit, the fact that it is even possible to get .NET to (successfully) invoke a method with the wrong parameter type is really kind of cool :)

rxelizondo replied on Thursday, January 14, 2010

RockfordLhotka:

But when it comes to data access perf and efficiency are much more important. Those milliseconds lost are not only lost to the user getting the data, but to other users waiting to leverage the same app server, so the cost is much higher.


Decreasing perf in the data portal is risky - enough decrease and people (rightly so) would question whether to use CSLA at all. So I could make the framework "correct" but entirely useless - and that would certainly not be a win :)




This is a good argument and I totally agree with you.

See, this is why I decided to keep my mouth shut instead of going on with my standard senseless ranting like I normally do, I have learned my lesson :)

RockfordLhotka replied on Thursday, January 14, 2010

Though you'll be happy to know that Jason generally agrees with you :)

Hopefully we can find some better IL to use in the dynamic proxy so the CLR throws the expected exception at this point.

Copyright (c) Marimer LLC