Corné Hogerheijde
Corné Hogerheijde

Reputation: 238

c# copied property loses reference when referenced object is removed from list

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?

Upvotes: 3

Views: 1273

Answers (4)

Corné Hogerheijde
Corné Hogerheijde

Reputation: 238

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!

Upvotes: 0

AlexDev
AlexDev

Reputation: 4717

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.

Upvotes: 0

Brian Mains
Brian Mains

Reputation: 50728

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.

Upvotes: 0

Joel Etherton
Joel Etherton

Reputation: 37533

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();

Upvotes: 4

Related Questions