Apex class to be used for both with and without sharing

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


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.