
Explanation

App1: DataReader
App2: DataReader
App3: DataAdapter
Need to use a DataAdapter since the data could be modified.
Note:
You can use the ADO.NET DataReader to retrieve a read-only, forward-only stream of data from a database.
Results are returned as the query executes, and are stored in the network buffer on the client until you request
them using the Read method of the DataReader. Using the DataReader can increase application performance
both by retrieving data as soon as it is available, and (by default) storing only one row at a time in memory,
reducing system overhead.
A DataAdapter is used to retrieve data from a data source and populate tables within a DataSet. The
DataAdapter also resolves changes made to the DataSet back to the data source. The DataAdapter uses the
Connection object of the .NET Framework data provider to connect to a data source, and it uses Command
objects to retrieve data from and resolve changes to the data source.
References: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/dataadapters-and-datareaders