3

Example

Have a look at the following code:

private void DeDuplicateOrganisations()
{
     var profileOrgs = _organisations.Where(o => o.ExistsInProfile).ToList();
     var kvkOrgs = _organisations.Where(o => !o.ExistsInProfile).ToList();

     profileOrgs.ForEach(o =>
         {
             var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
             if (duplicate != null)
             {
                  o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
                  o.ExistsInBoth = true;
                  kvkOrgs.Remove(duplicate);
              }
           });

      _organisations = profileOrgs.Concat(kvkOrgs).OrderBy(o => o.Title).ToList();
}

In this example the property CompanyInfoOrganisation (simply a get; set; property) is copied when an organisation is considered a duplicate. This all works as expected, duplicates are nicely deduplicated.

Also this is true inside this message:
_organisations.First(o => o.ExistsInBoth).CompanyInfoOrganisation != null;

Problem

Now I bind the _organisations list to a listbox

lbxCompanies.DataSource = null;
lbxCompanies.DataSource = _organisations;
lbxCompanies.DisplayMember = "Title";
lbxCompanies.SelectedIndex = -1;

and later on get the selected value:

 var org = lbxCompanies.SelectedValue as Organisation;
 gbxCompanyInfo.Visible = org != null;
 if (gbxCompanyInfo.Visible)
    if (org.CompanyInfoOrganisation != null)
          // NEVER GETS HERE (but gbxComanpyInfo is visible)

If I try to read the CompanyInfoOrganisation property I always get null while I know the property was set.

Question

What is happening here? How come the property reference is destroyed? How can I prevent this from happening?

4 Answers4

4

The reference you're using only has immediate scope and as soon as the query ends it exits scope and your reference disappears. So when you bind later, the reference is exactly right -- null.

profileOrgs.ForEach(o =>
{
    // Right here -- var duplicate has scope ONLY within your query. 
    // As soon as the query is executed it leaves scope and the reference
    // pointer will be null
    var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
    if (duplicate != null)
    {
        o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
        o.ExistsInBoth = true;
        kvkOrgs.Remove(duplicate);
    }
});

Because you're using a class, you need to perform a deep MemberwiseClone on it to get a NEW copy of the object:

o.CompanyInfoOrganisation = (YourInfoType)duplicate.CompanyInfoOrganisation.MemberwiseClone();
Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
  • Thank you for your answer! I was afraid I'd have to do something like that. Somehow I expected the reference to remain existing outside the scope. I was wrong... – Corné Hogerheijde Jul 11 '12 at 11:58
  • @CornéHogerheijde did this actually fix your issue? I don't understand how a reference doing out of scope would affect any other references to the same object. – Eren Ersönmez Jul 11 '12 at 12:05
  • 1
    @ErenErsönmez: In the manner being used in OPs question, the local variable `duplicate` is declared. When the assignment is made: `o.CIO = duplicate.CIO` there is no actual copying being done. The only assignment used is a pointer to the original object `duplicate`. When the query exits, `duplicate` becomes null, but the ADDRESS does not. So now the address, which sits inside the still in scope object `o`, will now point to null instead of a valid memory object. – Joel Etherton Jul 11 '12 at 12:11
  • Thanks, but _after_ `o.CIO = duplicate.CIO` , `o.CIO` has _nothing to do_ with the variable `duplicate`. It doesn't matter if it becomes null or not. `duplicate` was only used for a member access and the "reference" resulting from that member access was writen to `o.CIO`. Now, you have two (or possibly more elsewhere) references to the same object. You set `duplicate` to null, or `duplicate.CIO` to null, it won't affect the actual CIO object they are referencing, so the `o.CIO` would not be affected. – Eren Ersönmez Jul 11 '12 at 12:30
  • Consider this: `var a = new MyClass(); a.Val = (object)1; var b = a.Val; a = null;` ... `b` would remain non-null. – Eren Ersönmez Jul 11 '12 at 12:34
  • @ErenErsönmez You are right! Although I thougth this makes sence, it seems if I make a deep copy of the property (using http://stackoverflow.com/questions/78536/cloning-objects-in-c-sharp) it doesn't solve the issue... So what could be the problem? Also if I comment out the kvkOrgs.Remove() part, the reference remains null... – Corné Hogerheijde Jul 11 '12 at 13:07
  • Joel and @ErenErsönmez Thank you for the help! Sorry the problem was somewhere else! – Corné Hogerheijde Jul 11 '12 at 13:41
0

When you load the data, load the CompanyInfoOrganisation property along with the root entity; that way it will be already loaded into memory. If using LINQ to SQL, you load via DataLoadOptions, and pass this to the context. If using Entity Framework, you use the Include method in the LINQ query.

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
0

It might have to do with capturing of variables inside the lambda. Try substituting the .ForEach to a regular foreach(). Or maybe the CompanyInfoOrganisation in duplicate was null to begin with.

AlexDev
  • 4,049
  • 31
  • 36
0

The problem was I used string.Join() to show the values, and the first value to join was null (which is really annoying), resulting in an empty string, leaving me thinking the property was null. However it turned out the property was not null, but has a perfectly valid reference to the object needed. Using the debugger with a little more care would have saved me an hour or so...

Sorry!