Empty String -> Null

Posts   
1  /  2
 
    
Posts: 65
Joined: 07-Dec-2005
# Posted on: 31-Dec-2005 16:17:29   

So if someone goes into a generated entity and modifies the SetNewFieldValue overload that's specified in there NOT to call your base method (which is entirely possible), is this somehow functionally different from overriding it and not calling it?

I didn't intend to lecture you on the difference between null and "", but you seemed to imply that I shouldn't care which it was. I explained to you that I do.

Yes, calling the property will give me "", not null. My issue doesn't have to deal with a bug in my PropertyDescriptors, it deals with the fact that because my descriptors know if the actual value is null, I can handle things differently. You're coming back with the argument that because this capability doesn't currently exist in your framework in a binding scenario, it is irrelevant (or at least that is how it is being communicated).

The fact of the matter is that I requested a change that would not break a single person's code. You know that, I know that. If you were to choose the route of making an elaborate class schema for hooking to value assignments, yeah, that could break someone's code if not tested sufficiently. But a virtual method? Sorry, no. That's not going to break anyone's code.

Like I said, we disagree, and you're the one with the library, so you win. I'm just a customer simple_smile

I'm not trying to be a pain in the rear here, though it's clear that is what I have become, so I'll drop it. I think you have an excellent product in LLBLGen, and I continue to be impressed by its extensibility (we have done some killer things here with it without a whole lot of effort). I just simply wanted something that I considered (and still do) to be simple and not-a-big-deal, but you feel differently, and that's OK. I have a solution that works now in the way that I want it to. I just wanted to make it "legit", but it works just as well even if it isn't.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 31-Dec-2005 17:37:34   

AdamRobinson wrote:

So if someone goes into a generated entity and modifies the SetNewFieldValue overload that's specified in there NOT to call your base method (which is entirely possible), is this somehow functionally different from overriding it and not calling it?

Say you have 3 overloads of a method Foo. One is the Foo method which does the work, the other 2 are handy overloads with lesser parameters.

If the one which does all the work is virtual, IMHO gives that the signal to the developer: we build in some stuff by default, but if you want a different approach, simply override this method in a subclass and your code will be called instead.

In the case of SetNewFieldValue, this isn't the scenario, the base class' method HAS to be called. Before you say: "I disagree", this is the case because the not virtual worker method performs multiple tasks. If it was a single task, then yes, overriding it and calling the base class would be appropriate. If the method performs multiple tasks, overriding it to tap into the sequence of actions is not useful, because in between which tasks do you want to place your own code? so the architecture of the method has to be changed, in such a way that the actual value can be modified right before it's entered into the CurrentValue property of the field object to set.

I've put on v2.0's todo list this requirement.

Yes, calling the property will give me "", not null. My issue doesn't have to deal with a bug in my PropertyDescriptors, it deals with the fact that because my descriptors know if the actual value is null, I can handle things differently. You're coming back with the argument that because this capability doesn't currently exist in your framework in a binding scenario, it is irrelevant (or at least that is how it is being communicated).

See the template below

The fact of the matter is that I requested a change that would not break a single person's code. You know that, I know that. If you were to choose the route of making an elaborate class schema for hooking to value assignments, yeah, that could break someone's code if not tested sufficiently. But a virtual method? Sorry, no. That's not going to break anyone's code.

Of course making it virtual won't break any current code. It however can break code written today if I make it nonvirtual tomorrow or in a next version. Because if it's virtual I have to assume this method might have overrides in customer code, so if possible I should avoid making it non-virtual or change its signature.

I'm not trying to be a pain in the rear here, though it's clear that is what I have become, so I'll drop it. I think you have an excellent product in LLBLGen, and I continue to be impressed by its extensibility (we have done some killer things here with it without a whole lot of effort). I just simply wanted something that I considered (and still do) to be simple and not-a-big-deal, but you feel differently, and that's OK. I have a solution that works now in the way that I want it to. I just wanted to make it "legit", but it works just as well even if it isn't.

What's a big deal for us is of course not necessarily a big deal for you, the customer and vice versa. Making it virtual takes 1 second. However it has / can have huge impact in what we can do with the code in future versions, hence the reluctance here to make such a change. It's not to nag you, personally I have no problem with changing anything for any customer, but the thing is that we learned the hard way that going in and changing things can have side effects you didn't anticipate when you made the change simple_smile (thanks for the compliments btw simple_smile )

As you want "" to be stored as null and due to the propertydescriptors you have to (as you don't have necessarily access directly to the Field object) have null there, (I now understand why, so I can move on to avoid explaining persistence-time workarounds simple_smile ), I think a proper workaround is an include template which in the derived entity class of the 2 entity classes being generated, overrides all string properties for all fields which are strings (and doesn't override other types). In the override, it performs the check on "", and changes it to null when calling the base setter. That's basicly it, because then null is entered into the flow code calls and it will end up as null in the field.

You can also alter the get to receive null if "", that's up to you.

Save the following code to a file, for example StringPropertyInclude.template. Bind it in a custom template set to the templateID: Custom_EntityUsingEntityBaseTemplate


<[Foreach EntityField]>
<[ If IsStringField]>
        /// <summary> The <[EntityFieldName]> property of the Entity <[CurrentEntityName]><br/><br/>
        /// <[Foreach CustomProperty EntityField]>
        /// <[CustomPropertyName]>: <[CustomPropertyValue]><br/><[NextForeach]></summary>
        /// <remarks>Mapped on  <[ CaseCamel TargetType ]> field: "<[SourceObjectName]>"."<[SourceColumnName]>"<br/>
        /// <[ TargetType ]> field type characteristics (type, precision, scale, length): <[SourceColumnDbType]>, <[SourceColumnPrecision]>, <[SourceColumnScale]>, <[SourceColumnMaxLength]><br/>
        /// <[ TargetType ]> field behavior characteristics (is nullable, is PK, is identity): <[SourceColumnIsNullable]>, <[IsPrimaryKey]>, <[IsIdentity]></remarks>
        public override <[TypeOfField]> <[EntityFieldName]>
        {
            get { return base.<[EntityFieldName]>; }
            set 
            { 
                if(value.Trim().Length<=0)
                {
                    base.<[EntityFieldName]> = null;
                }
                else
                {
                    base.<[EntityFieldName]> = value;
                }
            }
        }
<[EndIf]><[NextForeach]>

This will override all string properties in the derived entity class (use 2-class scenario). I didn't compact the code to avoid empty lines, but that's straight forward. As you can see I simply set the value to null if necessary.

Now, I'll go back to my Oliebollen (dutch baked thingies we eat at newyearseve) and wish you a great 2006. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
JSobell
User
Posts: 145
Joined: 07-Jan-2006
# Posted on: 07-Jan-2006 07:21:10   

Andy, I don't know if I missed it somewhere in the midst of this Penguin novel, but what was the reason for wanting all empty strings to be set to NULL? There are performance and usage disadvantages in using NULLs, particularly when searching for data, so why do you want to do this?

When to perform this type function is an interesting question, as the requirement of setting "" to NULL is the same concept as asking "When should I capitalise the first letter of the surname in my Surname fields, as we require this as a business rule?". Should this be done in SetNewFieldValue, or is it a TypeConvertor function?

One problem with making a function Virtual is that the original developer may have made that function modify, call, or set some internal variables. If you override that function then you have to know the exact source code to replicate the functionality required, or you have to call the base class function. If you call it before your modifications, then have you internally broken the referential integrity of the entity? I.e. if you override the function, call base.SetNewFieldValue(), then assign the property value to be Null, how many internal variable did Otis set to track the fact it was originaly not a Null value? If you now look at TestOriginalFieldValueForNull() what will it return? Presumably false, yet the property contains NULL... This is just an example, but as I have no idea whether TestOriginalFieldValueForNull does a comparison of the property with NULL or uses a flag set at assignment, this is exactly the sort of reason functions are not made Virtual by default.

But... Otis, Andy mentioned the "On<EventName>" virtual methods in .NET, which effectively get around the issue you quite rightly describe of easily allowing accidental omission of calls or assignments to internal values. Going back to my original question of when and how you modify a property value in your business object, would it make sense to have a virtual OnSetNewFieldValue(string FieldName, object Value) method that normally does nothing, but would allow modificaton of the Value, hence inclusion of business rules in the Entity at assignment time? Would this have serious performance implications? Would it be simpler than assigning type converters?

I'm not arguing for or against any of the suggestions made in this thread, I'm simply curious...

Cheers, Jason

p.s. Andy, please do let me know why you use NULL for empty strings.

Posts: 65
Joined: 07-Dec-2005
# Posted on: 08-Jan-2006 03:52:34   

Just for the record, it's Adam, not Andy wink

There are several different reasons for the nulls rather than empty strings. Most of them revolve around other applications. In the context of my application, really the only reason is when I am scanning the distinct set of values for a field in an entity collection. Because these fields (in a logical sense) mean the same thing if they're null or empty string, I would rather the fields represent the same thing in a literal sense. I use custom properydescriptors that check the null value in order not to display default values in a binding scenario (since type safety is mostly irrelevant in a binding scenario anyway).

But my original point didn't really have anything to do with this business rule (or, rather, it was "bigger" than this particular BL). Franz and I had more of a philosophical discussion than anything else. I always had a working solution that worked the way I liked it. I can certainly understand the idea that making everything that's not private virtual could be dangerous. It could cause undesirable results. However, I tend to view this in the same way I view litigation. Don't tell me what to do when it doesn't infringe on your rights, and I won't complain to you when I screw something up wink . I realize that not everyone takes this approach.

I'm not sure what effect this could have on referential integrity, but just FYI the function that checks for null does something similar to the following...

if entity is new and field hasn't changed... null else if field has changed and currentvalue==null null else if field has not changed and dbvalue==null null else not null

So sticking myself in there wouldn't really have any effect on what this function performs so long as I call the base method. In my experience, overriding a function is used either because it's abstract (in which case you CAN'T call the base method), or it's used to preprocess the data that was originally passed in, then pass the processed data base to the base method. Of course there are exceptions, but I would be willing to wager that those two account for 95% of overridden methods. I could be wrong.

In any event, the issue's done from my perspective (and I think Franz's too); I have my solution that works, and Franz can rest easy knowing that his method is always being called.

1  /  2