5

I have created a selector class on Campaign object to be used in with sharing context like below:

public with sharing class CampaignSelector {

    public static Map<Id, Campaign> getCampaigns() {
        Map<Id, Campaign> campaignsByIds = new Map<Id, Campaign>([
                SELECT Id,
                        Name
                FROM Campaign
                WHERE Start_Date__c <= TODAY
                AND End_Date__c >= TODAY
        ]);
        return campaignsByIds;
    }
}

Now there is a situation where I have to use the same query in without sharing context. I can create a new class defined as without sharing and then add the selector query. But is there any other cleaner approach?

2 Answers 2

4

I had the same requirement once. So what we did is we had a class that executed the query to be abstract and we created instances of its successors, that determined the sharing model. This way you still only one query to maintain and a single entry point, but you're free to switch the sharing context on flight.

For your case this will look something like this:

public class CampaignSelector {

    private static CampaignDataService dataService {
        get {
            if (null == dataService) {
                // use with sharing by default
                dataService = new WithSharing();
            }
            return dataService;
        }
        set;
    }

    public static Map<Id, Campaign> getCampaigns() {
        return dataService.getCampaigns();
    }

    public static void switchSharing(Boolean useSharing) {
        // this method is used to switch sharing model
        if (useSharing) {
            dataService = new WithSharing();
        } else {
            dataService = new WithoutSharing();
        }
    }

    private abstract class CampaignDataService {
        public Map<Id, Campaign> getCampaigns() {
            Map<Id, Campaign> campaignsByIds = new Map<Id, Campaign>([
                    SELECT Id,
                            Name
                    FROM Campaign
                    WHERE Start_Date__c <= TODAY
                    AND End_Date__c >= TODAY
            ]);
            return campaignsByIds;
        }
    }

    private with sharing class WithSharing extends CampaignDataService {}
    private without sharing class WithoutSharing extends CampaignDataService {}
}

The query is still can be executed by calling CampaignSelector. getCampaigns(). By default it will be executed with sharing. To switch the sharing context you need to call CampaignSelector.switchSharing(false) - this will init dataService as an instance of WihtoutSharing and execute the query in without sharing context. You can switch back to with sharing in the same transaction by calling CampaignSelector.switchSharing(true).

The problem with static methods is that they block you from using inheritance and other cool OOP stuff

12
  • 1
    This is good approach. I think I have used this as well. Commented May 12, 2020 at 7:04
  • The thing is it boils down to same principal of using inner classes to solve the issue Commented May 12, 2020 at 7:19
  • Yup. It just makes it easy to maintain by reducing code duplication
    – nchursin
    Commented May 12, 2020 at 7:23
  • @snl before calling CampaignSelector.getCampaigns() call CampaignSelector. switchSharing(false). This will intialize dataService as WithoutSharing context and the query will be executed in without sharing context. Updated the answer with that info
    – nchursin
    Commented May 12, 2020 at 7:28
  • 3
    @snl Also, see my example on this technique as well. It may give you some other ideas of what's possible.
    – sfdcfox
    Commented May 12, 2020 at 13:17
4

One way would be to use inherited sharing instead of specifying the class using with or without sharing.

public inherited sharing class CampaignSelector {

    public static Map<Id, Campaign> getCampaigns() {
        Map<Id, Campaign> campaignsByIds = new Map<Id, Campaign>([
                SELECT Id,
                        Name
                FROM Campaign
                WHERE Start_Date__c <= TODAY
                AND End_Date__c >= TODAY
        ]);
        return campaignsByIds;
    }
}

Now if you call the class from without sharing class it will run in system mode.

And if you call the class from the with sharing it will execute in with sharing mode.

If you have a same class that needs to run in with sharing and without sharing , create an inner class using without sharing. Call your logic from the inner class to run in system context.

3
  • Well, the class from where I am calling this selector class is in 'with sharing' context.
    – nica
    Commented May 12, 2020 at 6:18
  • You will need another inner class which uses without sharing for your scenario and call from there Commented May 12, 2020 at 6:21
  • 1
    This approach is harder to maintain. You will need to have inner classes in every place where you need to use without sharing context. It is better to have a single point which determines the sharing context at the selector class itself
    – nchursin
    Commented May 12, 2020 at 6:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .