0

This seems to be very strange.

While we can use Owner relationship explicitly for many standard objects like Account and run this code without problems

Account a = [ select Owner.Id from Account limit 1 ];
User u = a.Owner;

while we try to execute the same code for Case

Case a = [ select Owner.Id from Case limit 1 ];
User u = a.Owner;

or for custom object

CustomObject__c a = [ select Owner.Id from CustomObject__c limit 1 ];
User u = (User)(SObject) a.Owner;

then we receive the same error

Line: 2, Column: 1

System.TypeException: Invalid conversion from runtime type Name to User

Is there a workaround or fix for this problem?

Note that CustomObject__c is not a detail in master-detail relationship and it has OwnerId field and implicit Owner relationship which can be either Queue or User so if we comment the second line the code doesn't fail. Also Case is not a detail in master-detail relationship and it has OwnerId field and implicit Owner relationship which can be either Queue or User so if we comment the second line the code doesn't fail.

1

As the error suggests, the Owner field is a Name object in these scenarios. This occurs for Case and Lead Owner, any custom objects that are enabled for at least one Queue, as well as the Activity field WhoId, which can be related to a Lead or Contact. Ideally, you should get a list of the OwnerId values, then query the User or Group tables to get the parent record information if you need fields that are not available on a Name relationship.

  • Thanks for shedding some light on what does it mean, however, I disagree that additional query is required, I would prefer some direct solution like that we found to serialize and deserialize the owner record. In our case we know for sure that we have only User owners – Patlatus May 8 '18 at 14:41
  • @Patlatus In your particular case, it may make sense, but once you need to access a custom User field, you'll need the extra query anyways. Do you need it to be a user? Would it harm your code if you wrote Name u = a.Owner? – sfdcfox May 8 '18 at 14:45
  • Let me check... – Patlatus May 8 '18 at 15:04
  • Yeah, looks like that would work Case a = [ select Owner.FirstName from Case Where OwnerId = :UserInfo.getUserId() ]; Name s = a.Owner; System.debug(LoggingLevel.ERROR, '@@@ v: ' + s ); System.debug(LoggingLevel.ERROR, '@@@ v: ' + s.FirstName ); – Patlatus May 8 '18 at 15:05
  • 1
    @Patlatus You can do that; you just need to use the generic SObject methods. String firstName = someRecord.getSObject('Owner').get('FirstName') I think you're overthinking it. Many generic algorithms can be written generically. – sfdcfox May 8 '18 at 15:34
2

Because on those object Owner field is polymorphism means it can be user or Queue.

So you can't directly convert it due to that limitation because system can't decide that it is user or Queue.

While on Account only user can make Owner so there you can directly access it.

Here is how you can access this field

        // Select records based on the Owner polymorphic relationship field
        List<Merchandise__c> merchandiseList = [SELECT TYPEOF Owner WHEN User THEN LastName WHEN Group THEN Email END FROM Merchandise__c]; 
        // We now have a list of Merchandise__c records owned by either a User or Group
        for (Merchandise__c merch: merchandiseList) {
            // We can use instanceof to check the polymorphic relationship type
            // Note that we have to assign the polymorphic reference to the appropriate
            // sObject type before passing to a method
            if (merch.Owner instanceof User) {
                User userOwner = merch.Owner;
                processUser(userOwner);
            } else if (merch.Owner instanceof Group) {
                Group groupOwner = merch.Owner;
                processGroup(groupOwner);
            }
        }

Working with Polymorphic Relationships in SOQL Queries

  • Yes, this is obvious that Apex has some problem with polymorphism but this answer doesn't suggest either workaround or fix for this problem. I believe System should not throw such an misleading and ambiguous error but behave like a charm and shouldn't throw exception in this case. – Patlatus May 8 '18 at 13:36
  • @Patlatus code added to handle this case. – Tushar Sharma May 8 '18 at 13:50
  • The code you provided doesn't work. When I try to execute the similar code Case a = [ select Owner.FirstName from Case Where OwnerId = :UserInfo.getUserId() ]; if ( a.Owner instanceof User ) { User s = a.Owner; } else { System.assert(false); } I receive Line: 5, Column: 1 System.AssertException: Assertion Failed – Patlatus May 8 '18 at 14:30
  • @Patlatus You are missing few important thing in your query. You need to add TypeOf owner to get expected result. Here is a code sample for you case a = [ SELECT TYPEOF Owner WHEN User THEN LastName WHEN Group THEN Email END FROM Case Where OwnerId = :UserInfo.getUserId() LIMIT 1]; if ( a.Owner instanceof User ) { system.assert(true);system.debug('USER'); } else { System.assert(false); } – Tushar Sharma May 9 '18 at 6:59
-1

We have found solution.

Event despite we can actually convert Owner to User, we could instead of this convert it to SObject

Case a = [ select Owner.FirstName from Case limit 1 ];
SObject u = a.Owner;
System.debug(LoggingLevel.ERROR, '@@@ v: ' + u.get('FirstName') );

Also if we serialize and deserialize this record then it converts without any problems:

Case a = [ select Owner.FirstName from Case limit 1 ];
User u = (User) JSON.deserialize(JSON.serialize(a.Owner), User.class  );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.