No db location?

  • Thread starter robbie mcgregor
  • Start date
R

robbie mcgregor

Hello,

Thanks for posting the alternative option. One question though, if the connection string is amended in the settings file, it won't build as the database doesn't exist (hasn't been placed in that location).

For example, using your logic, the connection string would read:

[global::System.Configuration.DefaultSettingValueAttribute("Data Source=\\ourCompanyName\\myDB.mdb")]

Which is available. Any ideas how to hard code this or create it at runtime?

Thanks.



RobinS wrote:

Re: Click-once app does not run
04-Sep-08


Here is my experience with deploying data files... A lot of this information
is from Brian Noyes's book on ClickOnce, which is excellent. I have an
alternative method at the end that we use for our homespun configuration
files as well, and will probably use for SQLCE when we get to that point

This is Brian Noyes' book on ClickOnce Deployment
http://www.amazon.com/Smart-Client-...f=sr_1_1?ie=UTF8&s=books&qid=1206469474&sr=8-

Deploy the SQL database with the application as a data file. It goes into
the folder defined by ApplicationDeployment.DataDirectory, or
Application.UserAppDataPath. (Assuming you have it in the top level of your
project, otw it puts it in a relative folder under that base path)

The database is only placed there when actually deployed, so when you test
out of VS, you have to set the "copy to output directory" to "copy always"
or "copy if newer", so the files will be copied to the same (relative)
folder location under the build output folder for your project

Then you have to add code that checks to see if you're running the deployed
version (ApplicationDeployment.IsNetworkDeployed = true), and if not, adjust
the path programmatically for your debugging (it will be under the
Application.ExecutablePath + relative-folder-if-you-have-one)

string dataPath
if (ApplicationDeployment.IsNetworkDeployed
dataPath = ApplicationDeployment.CurrentDeployment.DataDirectory
els
dataPath = System.Windows.Forms.Application.StartupPath
dataPath = System.IO.Path.Combine(dataPath, "yourdatabasename.sdf")

When you deploy your app, it deploys the database in the DataDirectory. When
you publish an update to the application, it copies the database forward to
the folder for the new version of the application, assuming nothing about
the server-side database has changed in any way

But if you change the database, that's where it gets complicated. It will
copy the new database from the server to the new version's DataDirectory
folder

It will copy the old database to a .\pre subfolder under the DataDirectory
folder for the new version

Then you have to write code to migrate the old data to the new database. If
you do nothing, it will ignore the previous version's database and use the
new database

The general process to migrate the data is to run a SQL script or a set of
SQL queries to retrieve the data from the old database and insert the data
into the appropriate tables in the new database. You might write this code
as part of a separate migration utility that is invoked when the new version
gets deployed

If you look at the code from Brian Noyes' book (available at
http://www.softinsight.com/clickoncebook), there is a sample of this called
SQLCompactScriptedMigration. It includes v1.0.0.0 and 2.0.0.0 of the same
application, but 2.0.0.0 contains the code in the main form's constructor to
migrate the data. The difference between 1.0 and 2.0 in his case is that a
new table was added to the database with a fk relation to the old table

Here's the example from v2.0

public Form1(

InitializeComponent()
if (ApplicationDeployment.IsNetworkDeployed

if (ApplicationDeployment.CurrentDeployment.IsFirstRun

MigrateData()




// This method takes advantage of the fact that for this simple
example, the typed data set
// Customers table and table adapter is compatible with the new schema
private void MigrateData()
{
string preFile =
Path.Combine(ApplicationDeployment.CurrentDeployment.DataDirectory,@".\.pre\Northwind.sdf");
if (!File.Exists(preFile)) // nothing to migrate
return;

// Get a connection to the old data in the \.pre folder
string oldDataConnectionString = @"Data
Source=|DataDirectory|\.pre\Northwind.sdf";
SqlCeConnection oldConnection = new
SqlCeConnection(oldDataConnectionString);
// Get a connection to the new data
string newDataConnectionString = @"Data
Source=|DataDirectory|\Northwind.sdf";
SqlCeConnection newConnection = new
SqlCeConnection(newDataConnectionString);
// Fill a dataset with the migration data
NorthwindDataSet oldData = new NorthwindDataSet();
CustomersTableAdapter oldAdapter = new CustomersTableAdapter();
oldAdapter.Connection = oldConnection;
oldAdapter.Fill(oldData.Customers);
// Create a table adapter for the new database and a compatible
data set
CustomersTableAdapter newAdapter = new CustomersTableAdapter();
newAdapter.Connection = newConnection;
NorthwindDataSet newData = new NorthwindDataSet();
// Fill the new data set with default data
newAdapter.Fill(newData.Customers);
// Merge the old data into the new schema
// Assumes compatible schemas - this is the hard part for
// Real apps with significant schema changes
foreach (DataRow row in newData.Customers)
{
row.Delete();
}
newData.Merge(oldData);
newAdapter.Update(newData);
}


About connection strings: If you include the |DataDirectory| placeholder in
your connection string, the path will be filled in automatically by the
runtime. For example, for Northwind, it would be

Data Source = "|DataDirectory|\Northwind.sdf".

Be sure your database is added to the project as a file with a Build Action
of "content", and marked as a Data File in the Application Files dialog.

Another problem to be aware of is that the migration of a data file is
triggered by *any* change to the data file in a newly published version of
your application. If you connect to your SQLServer Express or SQLCE database
in VS just to check the schema or contents of the default values, you will
have modified the date timestamp of the file, so the next time you publish
your application it will be treated as a new version of the database, and
you need to have your data migration code in place to handle that.

Basically, if you never change your database structure, ClickOnce will
handle the data migration for you by copying the database forward. OTW you
have to handle it yourself in code.

**AN ALTERNATIVE**

For databases that are just files, like Access databases or SQLCE, you can
do the following:

1) Include the database in the main project. In the ApplicationFiles dialog
in the Publish tab, set it to Include or Include(Required) instead of
Include(Data).

2) In the application's startup, check to see if the folder you're going to
put the database in exists, and if there's already a database there. If
there's not, create the folder and copy the database over there.

In Vista, you can only write to MyDocuments and the user's profile. I
wouldn't put it in MyDocuments -- that's just asking for trouble. I put my
files in LocalApplicationData, as recommended for Windows Vista.

userFilePath =
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string userFilesGoHere = Path.Combine(userFilePath, "ourCompanyName");
string ourDatabase = Path.Combine(userFilesGoHere, "myDB.mdb");
string whereTheDBStarts =
Path.Combine(System.Windows.Forms.Application.StartupPath, "myDB.mdb");

So in your startup, you can check for it, and copy it over if it's not
there:

if (!Directory.Exists(userFilesGoHere))
{
Directory.CreateDirectory(userFilesGoHere);
File.Copy(whereTheDBStarts, ourDatabase);
}
else if (!File.Exists(ourDatabase))
File.Copy(whereTheDBStarts, ourDatabase);

And then just set the connection string to point at the location defined by
ourDatabase.

So the first time the user runs the application, it will set up the
directory and copy the database over there. On subsequent updates or
installations, it will just use the database that is already there.

The advantage of this is you don't have to deal with migration if you just
open the database and close it. You just have to handle it when you know
something has changed.

Also, if the user deinstalls your application and reinstalls it, the data is
left behind. This is bad (the data is left behind and not cleaned up) and
good (the user reinstalls the application and doesn't lose his data).

And one last extra helpful comment about SQLCE: If you're deploying this,
you can include the dll's as local references instead of pushing SLQCE as a
prerequisite. This is way cool, because it removes one step from the
installation process.

To do this, you add the dll's to your project and set "copy local" to true
and include them in the deployment. Then you can remove the prerequisite.
Here are the dll's you need to add (version may change):

sqlceca30.dll
sqlcecompact30.dll
sqlceer30en.dll
sqlceme30.dll
sqlceoledb30.dll
sqlceqp30.dll
sqlcese30.dll


I hope this helps someone. This is a frequently asked question in the
ClickOnce forum, so hopefully it will also help someone here.

RobinS.
GoldMail.com

Previous Posts In This Thread:

Re: Click-once app does not run

If you include it as an application file, it gets replaced when you issue
updates. Just be aware of that.

If you include it as a Data File, it gets put in a different location that
you can reach programmatically, and updates are handled differently.

If you need to retain the database between updates, post back and I will
give more info.

RobinS.
GoldMail.com

Click-once app does not run
I built a small app with VS C# Express 2008, and published it as a
click-once app. I tried installing it on my local machine, and when I
click on its icon from the Start menu, nothing happens. Everything
works fine when I run the code from VisualStudio. I get this problem
if I use Debug or Release build config.
I'm sure it must be something rather simple, but I just can't seem to
find it.

Re: Click-once app does not run
Lord Zoltar wrote:

Ok I think I've isolated the problem: the SQLite database file is not
getting added to the deployment. I've tried marking the file as
"resource" or "embedded resource" but I can't seem to get it to ge
deployed with the app. It also never appears in Project Properties -
Publish tab -> Application Files.

Re: Click-once app does not run
Lord Zoltar wrote

Never mind... Had to set the file's Build Action to "Content", and i
Application files, set it to "Include" not "Data File".

Re: Click-once app does not run
Response to "RobinS" <[email protected]>

<snip

I realize this thread is not my own, but I'd definitely
appreciat
you elaborating further.

I am currently researching the best methods for deployment
of one o
my applications. My thought was that adding them as
resources migh
prove easier or more manageable at least.

One of the main problems I ran across was the file
disappearing o
my embedded projects* not building anew.

* Whatever the proper terminology is for a single solutio
containing other solutions ("Add Project").

Thanks

--
-Los
Remove the extra words to reply by e-mail. Don't e-mail
me. I a
kidding. No I am not.

Re: Click-once app does not run
RobinS wrote

Thanks for that bit of detail. I WOULD like this data file to be kep
when the app is updated. If there are ever schema changes, I will hav
my app handle those internally.

Re: Click-once app does not run

Here is my experience with deploying data files... A lot of this information
is from Brian Noyes's book on ClickOnce, which is excellent. I have an
alternative method at the end that we use for our homespun configuration
files as well, and will probably use for SQLCE when we get to that point

This is Brian Noyes' book on ClickOnce Deployment
http://www.amazon.com/Smart-Client-...f=sr_1_1?ie=UTF8&s=books&qid=1206469474&sr=8-

Deploy the SQL database with the application as a data file. It goes into
the folder defined by ApplicationDeployment.DataDirectory, or
Application.UserAppDataPath. (Assuming you have it in the top level of your
project, otw it puts it in a relative folder under that base path)

The database is only placed there when actually deployed, so when you test
out of VS, you have to set the "copy to output directory" to "copy always"
or "copy if newer", so the files will be copied to the same (relative)
folder location under the build output folder for your project

Then you have to add code that checks to see if you're running the deployed
version (ApplicationDeployment.IsNetworkDeployed = true), and if not, adjust
the path programmatically for your debugging (it will be under the
Application.ExecutablePath + relative-folder-if-you-have-one)

string dataPath
if (ApplicationDeployment.IsNetworkDeployed
dataPath = ApplicationDeployment.CurrentDeployment.DataDirectory
els
dataPath = System.Windows.Forms.Application.StartupPath
dataPath = System.IO.Path.Combine(dataPath, "yourdatabasename.sdf")

When you deploy your app, it deploys the database in the DataDirectory. When
you publish an update to the application, it copies the database forward to
the folder for the new version of the application, assuming nothing about
the server-side database has changed in any way

But if you change the database, that's where it gets complicated. It will
copy the new database from the server to the new version's DataDirectory
folder

It will copy the old database to a .\pre subfolder under the DataDirectory
folder for the new version

Then you have to write code to migrate the old data to the new database. If
you do nothing, it will ignore the previous version's database and use the
new database

The general process to migrate the data is to run a SQL script or a set of
SQL queries to retrieve the data from the old database and insert the data
into the appropriate tables in the new database. You might write this code
as part of a separate migration utility that is invoked when the new version
gets deployed.

If you look at the code from Brian Noyes' book (available at
http://www.softinsight.com/clickoncebook), there is a sample of this called
SQLCompactScriptedMigration. It includes v1.0.0.0 and 2.0.0.0 of the same
application, but 2.0.0.0 contains the code in the main form's constructor to
migrate the data. The difference between 1.0 and 2.0 in his case is that a
new table was added to the database with a fk relation to the old table.

Here's the example from v2.0.

public Form1()
{
InitializeComponent();
if (ApplicationDeployment.IsNetworkDeployed)
{
if (ApplicationDeployment.CurrentDeployment.IsFirstRun)
{
MigrateData();
}
}
}

// This method takes advantage of the fact that for this simple
example, the typed data set
// Customers table and table adapter is compatible with the new schema
private void MigrateData()
{
string preFile =
Path.Combine(ApplicationDeployment.CurrentDeployment.DataDirectory,@".\.pre\Northwind.sdf");
if (!File.Exists(preFile)) // nothing to migrate
return;

// Get a connection to the old data in the \.pre folder
string oldDataConnectionString = @"Data
Source=|DataDirectory|\.pre\Northwind.sdf";
SqlCeConnection oldConnection = new
SqlCeConnection(oldDataConnectionString);
// Get a connection to the new data
string newDataConnectionString = @"Data
Source=|DataDirectory|\Northwind.sdf";
SqlCeConnection newConnection = new
SqlCeConnection(newDataConnectionString);
// Fill a dataset with the migration data
NorthwindDataSet oldData = new NorthwindDataSet();
CustomersTableAdapter oldAdapter = new CustomersTableAdapter();
oldAdapter.Connection = oldConnection;
oldAdapter.Fill(oldData.Customers);
// Create a table adapter for the new database and a compatible
data set
CustomersTableAdapter newAdapter = new CustomersTableAdapter();
newAdapter.Connection = newConnection;
NorthwindDataSet newData = new NorthwindDataSet();
// Fill the new data set with default data
newAdapter.Fill(newData.Customers);
// Merge the old data into the new schema
// Assumes compatible schemas - this is the hard part for
// Real apps with significant schema changes
foreach (DataRow row in newData.Customers)
{
row.Delete();
}
newData.Merge(oldData);
newAdapter.Update(newData);
}


About connection strings: If you include the |DataDirectory| placeholder in
your connection string, the path will be filled in automatically by the
runtime. For example, for Northwind, it would be

Data Source = "|DataDirectory|\Northwind.sdf".

Be sure your database is added to the project as a file with a Build Action
of "content", and marked as a Data File in the Application Files dialog.

Another problem to be aware of is that the migration of a data file is
triggered by *any* change to the data file in a newly published version of
your application. If you connect to your SQLServer Express or SQLCE database
in VS just to check the schema or contents of the default values, you will
have modified the date timestamp of the file, so the next time you publish
your application it will be treated as a new version of the database, and
you need to have your data migration code in place to handle that.

Basically, if you never change your database structure, ClickOnce will
handle the data migration for you by copying the database forward. OTW you
have to handle it yourself in code.

**AN ALTERNATIVE**

For databases that are just files, like Access databases or SQLCE, you can
do the following:

1) Include the database in the main project. In the ApplicationFiles dialog
in the Publish tab, set it to Include or Include(Required) instead of
Include(Data).

2) In the application's startup, check to see if the folder you're going to
put the database in exists, and if there's already a database there. If
there's not, create the folder and copy the database over there.

In Vista, you can only write to MyDocuments and the user's profile. I
wouldn't put it in MyDocuments -- that's just asking for trouble. I put my
files in LocalApplicationData, as recommended for Windows Vista.

userFilePath =
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string userFilesGoHere = Path.Combine(userFilePath, "ourCompanyName");
string ourDatabase = Path.Combine(userFilesGoHere, "myDB.mdb");
string whereTheDBStarts =
Path.Combine(System.Windows.Forms.Application.StartupPath, "myDB.mdb");

So in your startup, you can check for it, and copy it over if it's not
there:

if (!Directory.Exists(userFilesGoHere))
{
Directory.CreateDirectory(userFilesGoHere);
File.Copy(whereTheDBStarts, ourDatabase);
}
else if (!File.Exists(ourDatabase))
File.Copy(whereTheDBStarts, ourDatabase);

And then just set the connection string to point at the location defined by
ourDatabase.

So the first time the user runs the application, it will set up the
directory and copy the database over there. On subsequent updates or
installations, it will just use the database that is already there.

The advantage of this is you don't have to deal with migration if you just
open the database and close it. You just have to handle it when you know
something has changed.

Also, if the user deinstalls your application and reinstalls it, the data is
left behind. This is bad (the data is left behind and not cleaned up) and
good (the user reinstalls the application and doesn't lose his data).

And one last extra helpful comment about SQLCE: If you're deploying this,
you can include the dll's as local references instead of pushing SLQCE as a
prerequisite. This is way cool, because it removes one step from the
installation process.

To do this, you add the dll's to your project and set "copy local" to true
and include them in the deployment. Then you can remove the prerequisite.
Here are the dll's you need to add (version may change):

sqlceca30.dll
sqlcecompact30.dll
sqlceer30en.dll
sqlceme30.dll
sqlceoledb30.dll
sqlceqp30.dll
sqlcese30.dll


I hope this helps someone. This is a frequently asked question in the
ClickOnce forum, so hopefully it will also help someone here.

RobinS.
GoldMail.com

Thanks
Extremely useful information! Thanks for sharing.


Submitted via EggHeadCafe - Software Developer Portal of Choice
EggHeadCafe Chat Chaos in Silverlight Released Today
http://www.eggheadcafe.com/tutorial...6-54f31bdede5d/eggheadcafe-chat-chaos-in.aspx
 
R

robbie mcgregor

P.S. I know I can put connection strings within the respective classes, but I am looking to manage it via settings.settings.

Just trying another alternative, copying the db back from ./.Pre to the data directory... Maybe that will solve my prob?



robbie mcgregor wrote:

No db location?
04-Mar-10

Hello,

Thanks for posting the alternative option. One question though, if the connection string is amended in the settings file, it won't build as the database doesn't exist (hasn't been placed in that location).

For example, using your logic, the connection string would read:

[global::System.Configuration.DefaultSettingValueAttribute("Data Source=\\ourCompanyName\\myDB.mdb")]

Which is available. Any ideas how to hard code this or create it at runtime?

Thanks.

Previous Posts In This Thread:


Submitted via EggHeadCafe - Software Developer Portal of Choice
Dr. Dotnetsky's Cool .Net Tips and Tricks No. 26
http://www.eggheadcafe.com/tutorial...be30-cde5d852348d/dr-dotnetskys-cool-net.aspx
 
R

robbie mcgregor

Yes, copying forward from .pre worked, connection string remains as is. Downside is that data is removed if app is uninstalled.

Thanks for giving me the idea though! Appreciate your post.

Rgds,

Robbie.



robbie mcgregor wrote:

Edit:
04-Mar-10

P.S. I know I can put connection strings within the respective classes, but I am looking to manage it via settings.settings.

Just trying another alternative, copying the db back from ./.Pre to the data directory... Maybe that will solve my prob?

Previous Posts In This Thread:


Submitted via EggHeadCafe - Software Developer Portal of Choice
Dr. Dotnetsky's Cool .Net Tips and Tricks No. 26
http://www.eggheadcafe.com/tutorial...be30-cde5d852348d/dr-dotnetskys-cool-net.aspx
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top