Warm tip: This article is reproduced from serverfault.com, please click

How can I build my SQLite connection string in a UWP App?

发布于 2020-12-01 14:57:44

In my Windows Form app, I was able to build my SQLite connection string like this (after adding the .db file to my project via Add > Existing Item):

string connStr = string.Format(@"Data source={0}F4F.db", Application.StartupPath);

So I tried the same thing in my UWP project, with this code:

string connStr = string.Format(@"Data source={0}Cartographer.db", Application.StartupPath);
SqliteConnection conn = new SqliteConnection(connStr);

...but there is no "StartupPath" property. What should I use instead?

These are the available members of the Application object:

enter image description here

Should I use "Current"?

UPDATE

In response to Peter's answer, and as an add-on to my comment to that answer, here is the code I have now:

public sealed partial class MainPage : Page
{
        StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
        string connStrBase = @"Data source={0}Cartographer.db";
        string connStr = string.Empty;
        string path = string.Empty;    
        . . .
}

. . .

path = folder.Path;
connStr = string.Format(connStrBase, path);

...am I at least close to having it right?

Questioner
B. Clay Shannon
Viewed
0
Peter Torr - MSFT 2020-12-09 15:53:18

For read-write access to a file packaged as part of your app, you will need to copy it to your ApplicationData.LocalFolder directory on first-run of your app, and then you can update it from there. Then you can use the LocalFolder.Path property to initialize your connection string with the correct location. The original file will be located inside your Package.InstalledLocation.

Here is a basic sample, using a helper class that you can use to copy any file from an arbitrary place in your package into a mirrored place in your local folder, and then return the resulting filename.

The sample is async in case you're copying large files during startup etc. but it can trivially be made sync if you only ever need files from your package directory. Also, even though the API is async, is 100% safe to block on it if you want (as illustrated below).

class LocalDataUtils
{
    static readonly string destinationDirectory = 
        ApplicationData.Current.LocalFolder.Path;
    static readonly string sourceDirectory = 
        Package.Current.InstalledPath;

    static Dictionary<string, TaskCompletionSource<string>> fileCompletionTasks = 
        new Dictionary<string, TaskCompletionSource<string>>();

    public static Task<string> GetFilenameAsync(string relativePath)
    {
        TaskCompletionSource<string> completion;

        if (!fileCompletionTasks.TryGetValue(relativePath, out completion))
        {
            lock (fileCompletionTasks)
            {
                // Check again in case there was a race.
                if (!fileCompletionTasks.TryGetValue(relativePath, out completion))
                {
                    completion = new TaskCompletionSource<string>();
                    fileCompletionTasks.Add(relativePath, completion);

                    // This is async in case you ever need to copy from locations 
                    // other than your local folders or you are copying files
                    // that might take a while.
                    // You can simplify and make it not-async if you like.
                    Task.Run(() => CopyFileAndGetFilename(relativePath, completion));
                }
            }
        }

        return completion.Task;
    }

    static void CopyFileAndGetFilename(string relativePath, 
        TaskCompletionSource<string> completion)
    {
        try
        {
            // Also a fun movie :)
            var finalDestination = Path.Combine(destinationDirectory, relativePath);

            if (!File.Exists(finalDestination))
            {
                var originalLocation = Path.Combine(sourceDirectory, relativePath);
                Directory.CreateDirectory(Path.GetDirectoryName(finalDestination));
                File.Copy(originalLocation, finalDestination);
            }

            completion.SetResult(finalDestination);
        }
        catch (Exception ex)
        {
            completion.SetException(ex);
        }
    }
}

Using it is pretty simple:

var name = await LocalDataUtils.GetFilenameAsync(@"Database\test.db");
var text = File.ReadAllText(name);
Debug.Write(text);

// Note: no 'await' here for illustrative purposes; subsequent calls
// for the same file will always return immediately.
var otherNameTask = LocalDataUtils.GetFilenameAsync(@"Database\test.db");
Debug.Assert(otherNameTask.IsCompletedSuccessfully);

// Don't call 'await' and instead block on the 'Result'
// This is not safe in general for async methods, but is fine in this case.
var name2 = LocalDataUtils.GetFilenameAsync(@"Assets\StoreLogo.png").Result;
var size = new FileInfo(name2).Length;
Debug.WriteLine($"{Path.GetFileName(name2)} is {size} bytes long.");