StrataFrame Forum

Database Connection Problems

http://forum.strataframe.net/Topic13030.aspx

By Bill Cunnien - 12/14/2007

After installation, I was able to successfully create a business object and map it to a table on my remote database.  A new form was created, the business object attached and some fields slapped on for testing.  No errors upon the build; however, if I run the debugger, the application fails nearly immediately with some such statement that my DataLayer is out of whack.  If I goof around with the program.cs file, I can get a database connection popup to occur when I start the app...it is looking for the "missing" connection information:  server name, authentication, database name.  If I provide all of that, then I get an unathentication error.  What am I doing wrong?  I am going through the tutorial, but any kind of hint would be helpful on this...thanks!

Bill

By Bill Cunnien - 12/14/2007

As I fiddle with the settings, I regularly get this error message:

DataLayerException
  An error occurred while creating a new DataLayer for the business object.
DataLayerException
  The DataSourceKey [] is not defined.

Source     : MicroFour StrataFrame Business

Stack Trace:
   at MicroFour.StrataFrame.Data.DataLayer.Create(BusinessLayer BusinessObject, ISynchronizeInvoke SyncObject, Boolean CheckDesignTime)
   at MicroFour.StrataFrame.Business.BusinessLayer.get__DataLayer()
   at MicroFour.StrataFrame.Business.BusinessLayer.FillDataTable(DbCommand CommandToExecute)
   at MicroFour.StrataFrame.Security.BusinessObjects.SFSPreferencesBO.FillAll(Int32 ProjectPK)
   at MicroFour.StrataFrame.Security.BusinessObjects.SFSPreferencesBO.RetrieveSecurityPreferences()
   at AspireSF.Program.InitApplication(InitializingApplicationEventArgs e) in C:\Aspire Projects\AspireSF\AspireSF\Program.cs:line 161
   at MicroFour.StrataFrame.Application.StrataFrameApplication.Raise_InitializingApplication(InitializingApplicationEventArgs e)
   at MicroFour.StrataFrame.Application.StrataFrameApplication.RunApplication()
   at AspireSF.Program.Main() in C:\Aspire Projects\AspireSF\AspireSF\Program.cs:line 40
   at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

By Trent L. Taylor - 12/14/2007

You are not setting up the connection string to which the business object needs to communicate with your data correctly.  First, you need to understand the DataSourceKey, there is a lot of documentation to give you more details, but in short, each connection string will have a unique DataSourceKey.  You can then set the property of the business object (DataSourceKey) to the data source to which it needs to connect.  By default, all BOs will assume default (empty) which is fine in most cases.

You can use the ConnectionStringWIzard or manually set the connection string.  The ConnectionStringWizard requires that you setup the connection that are required (RequiredDataSourceItem).  This is done in the program.cs (or Appmain.vb).  When the SetConnections method is called in the program.cs, it looks to see if an active connection string has been established for the application using the ApplicationKey (this will basically uniquely identify your application from another and pull the connection string for that application).  This too is set in the program.cs in the SetDataSources method.  The second option is to manually set your connection string through the DataBasics.DataSources collection (this is the same thing that gets set through the Connection wizard).

More than likely you have a connection string that is pointing to the wrong database.  Make sure that the AddRequiredDataSourceItem has the correct database name supplied.  If the connection string is already setup then the wizard will not appear automatically but you can re-display it manually.  Again, refer to the docs on the Connection String Manager as it will show you how all of this works.

Finally, AND MOST IMPORTANTLY it appears the you are changing the connection string settings for your development environment.  The Database Connection from the StrataFrame menu is NOT the connection used when you run your application.  This is what StrataFrame uses during the design-time....so once you get this set and working just leave it be as it has nothing to do with your run-time connections.  All of this is set in the SetDataSources of the program.cs or AppMain.vb file.

By Bill Cunnien - 12/14/2007

Ok...I blew away that first application attempt.  I thought I would start over, just in case I missed something on the first pass.  So far, I have not even created a business object.  Hitting F5...alas!  I am getting this message now, after entering the valid connection information:

SqlException
  Invalid object name 'dbo.SFSPreferences'.

Source     : .Net SqlClient Data Provider

Stack Trace:
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
   at MicroFour.StrataFrame.Data.DbDataSourceItem.GetDataTable(DbCommand Command, OnChangeEventHandler CallBack)
   at MicroFour.StrataFrame.Data.SqlDataSourceItem.GetDataTable(DbCommand Command, OnChangeEventHandler CallBack)
   at MicroFour.StrataFrame.Data.DataLayer.GetDataTable(DbCommand Command, Boolean RegisterNotification)
   at MicroFour.StrataFrame.Business.BusinessLayer.FillDataTable(DbCommand CommandToExecute)
   at MicroFour.StrataFrame.Security.BusinessObjects.SFSPreferencesBO.FillAll(Int32 ProjectPK)
   at MicroFour.StrataFrame.Security.BusinessObjects.SFSPreferencesBO.RetrieveSecurityPreferences()
   at AspireSF.Program.InitApplication(InitializingApplicationEventArgs e) in C:\Aspire Projects\AspireSF\AspireSF\Program.cs:line 161
   at MicroFour.StrataFrame.Application.StrataFrameApplication.Raise_InitializingApplication(InitializingApplicationEventArgs e)
   at MicroFour.StrataFrame.Application.StrataFrameApplication.RunApplication()
   at AspireSF.Program.Main() in C:\Aspire Projects\AspireSF\AspireSF\Program.cs:line 40
   at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

By Greg McGuffey - 12/14/2007

It looks like you are using the SF security project and you haven't setup the security db/connection. To do this you'd need to deploy the security tables/views (from DDT, if you have that...the easiest way...by far) and then setup the security key in the SetDataSources event handler...and maybe some other stuff I can't remember off the top of my head. Unsure



However, I'm guessing that right now, you just need to start simple. So, blow away this attempt too Pinch then start over, but use the normal SF project (the without security). I think you'll be on your way then.
By Bill Cunnien - 12/14/2007

Blow it away?! w00t  After all that non-work that I did?!!?  BigGrin

No prob.  I will try that out.  I guess I can add the security later (after I figure out the important stuff).

Thanks,
Bill

By Bill Cunnien - 12/14/2007

Does the BO database(s) have to reside on the same server as the StrataFrame (design-time) database?
By Trent L. Taylor - 12/14/2007

Does the BO database(s) have to reside on the same server as the StrataFrame (design-time) database?

No.  The two are unrelated.  If you are referring to the database to which your BOs will communicate, this database can reside wherever you may need it.  The StrataFrame database is only for design-time and has no effect on the run-time stuff.

By Bill Cunnien - 12/14/2007

This line seems important to the accessibility of the data for the business object:

ConnectionManager.AddRequiredDataSourceItem("", "SQL Connection", DataSourceTypeOptions.SqlServer, "MyDatabase", "This connection is used by AspireSF.");

Even if I changed the "MyDatabase" to the proper name, how does this RequiredDataSourceItem know where the server is?

In addition, how does the following line play into all of this:

DataLayer.DataSources.Add(new SqlDataSourceItem("", "myconnectionstring"));

Sorry for my confusion on this...I have messed around with these settings, but have not found a successful combination, yet.

Thanks!
Bill

 

By Bill Cunnien - 12/14/2007

I copied the connection string from my Business Object Mapper project properties window into the DataLayer.DataSources.Add method.  It seems to be working at the moment...but, I am not sure why.  The customersBO1 object on the form has an empty string in the DataSourceKey field.  Also, I had no dialog this time asking me to connect to the SQL Server.  Somehow, this change made a difference.  For the time being, it is a mystery to me.
By Greg McGuffey - 12/14/2007

There are two ways to manage your connection(s):

- using the ConnectionManager. This is the dialog you see (sometimes) when you open the app that prompts you for the connection information. This results in a data source(s) being set.

- directly setting the data sources. In this case you manually setup the data source, including the key used by the BOs to reference the appropriate data source.



So, this line is used to tell the ConnectionManager that you need the connection information for a data source. The ConnectionManager then uses this to either prompt the user for the connection information (via the dialog) or it uses the last connection set by the user (it saves the connection information on disk in an encrypted xml file).

ConnectionManager.AddRequiredDataSourceItem("", "SQL Connection",DataSourceTypeOptions.SqlServer,"MyDatabase", "This connection is used by AspireSF.");


The first parameters, "", is the key used to access this data source. This is the default key for BOs (set with the DataSourceKey property of individual BOs). The second is a name used within the connection manager to identify the connection (you can have any number of connections...though I don't recommend that when just learning!). The third identifies the type of data source. SF has SQL, Oracle, FoxPro, Access and I believe Db2 data sources (there is also an enterprise datasource, but that has to be done manually). If you need others you can actually create them (again, not in the beginning BigGrin ). The next is a description describing the data source, so the user is clear on what connection they are entering. The ConnectionManager really just facilitates creating the connection string to the data source, so the DataSource can be added, as if you had done it manually via....



The DataLayer.DataSources collection is the collection of defined data sources that is available to the application. You can define a data source manually using a line like this one.

DataLayer.DataSources.Add(new SqlDataSourceItem("", "myconnectionstring"));


DataLayer.DataSources is a collection of data source items. The data sources are the code that will actually interact with the data source, usually a database, but it can be other types as well (with a bit of coding). You add data source items (of the types mentioned above) to the collection. Each data source item has a key and a connection string. The key is the same as you'd enter using the ConnectionManager for the first parameter. The connection string is an ADO.NET connection string that is used to connect to the data source.



Now for the important part...you only do one or the other, but not both for a connection. I.e. you either use the ConnectionManager to setup a datasource XOR you use the DataLayer.DataSources collection. (OK, you could use both, but for the love of warm pajamas on cold nights, don't even think about it). You can mix them if you are setting up multiple connections, but I'd keep it simple at first...one connection, use one technic (of course you can experiment with the two types, switch back and forth...just comment out the other one so only one is active at a time).



Now, when you create a SF project, the SetDataConnections event handler will actually be all setup to connect to a data source, via the ConnectionManager. If you touch nothing, when you hit F5, it will prompt you for the connection info. This is very handy when doing sample/learning projects. You just start creating BOs and whatever else you want, then when you run it, simply point to the database you build the BOs off of. The StrataFrameSample db is excellent for this sort of work.



You will also note that commented out is a section for creating manual data sources. There are examples of several different type of data sources. If you decide to try one of these, just comment out the ConnectionManager code above it.



Hope this explanation starts to clear up your confusion! w00t
By Bill Cunnien - 12/15/2007

Thanks, Greg...things are getting a bit clearer.  For DB development, I will have two database connections.  The first is handled via the ConnectionManager and is for the StrataFrame database used in development only.  There is always only one of these.  The second database connection for my application data is handled in several ways, but always associated with the run-time environment.  I can set it via the connection wizard on startup or through several options in the startup code.  I can have as many of these database connections as necessary.  Do I have it?
By Greg McGuffey - 12/16/2007

Your getting closer BigGrin



The connection to the StrataFrame db is used only at design time and only by the tools within VS, such as the BO Mapper, the security dialog, the localization dialog and by the SF addin within VS. Thus you never need to set that connection within the application itself. You set that connection on the SF menu within VS. The connection to the SF database is only needed on the development machine, never on any machine running your app.



Within the application, you setup any connections you need (as many as needed...I'm using two, one for security and one for the app data, I believe I remember seeing posts of users using many connections, one db for each region supported by the db and the connections can be to different kinds of databases, I.e. you could connect to a SQL Server, Oracle and Access all in the same app). For each connection you have (roughly) two choices: use the connection manager or set the connection manually using the DataLayer.DataSources() collection directly. The difference is that if you use the connection manager, the user needs to know how to log onto the database. This could be fine in many circumstances and not fine in many others. BigGrin If you use the DataLayer.DataSources() collection, the connection information can be set automatically and the user need not know anything about the connection or the database. Again, there are times when this is needed and times when it isn't.



For your initial learning, I'd skip this part, just use the connection manager (which is the code that is enabled within SetDataSources in AppMain when a SF project is used) and work through some of the other stuff first. Get a bit more comfortable with how BOs work, with data binding and with some of the SF controls. Then come back to this. Keep the questions coming too...it really helps as you work through each of these concepts. BigGrin
By StrataFrame Team - 12/17/2007

That's a pretty darn good explanation, Greg; you're on top of it.  Thanks.
By Greg McGuffey - 12/17/2007

Any time. I'm glad I'm starting to get a clue after...oh...8,000,000,000 posts asking questions just like this Blink
By Keith Chisarik - 12/17/2007

Hi,



I have also found it quite beneficial to code my application so that little or no changes are required to the code between development and production, for web applications my applications detects the domain space the app is using (localhost versus www.something) ** thanks again Trent for this idea ** and pulls the appropriate connection string from a shared class. I do something similar for desktops. I used to have to replace connection strings before compilation for production and naturally missed a few along the way, now I just write it up from to be aware of its surroundings and forget about it. The only time I have to change that logic is if a server changes frequently, which is not the case for me in most applications. In the one instance that was a consideration I still wrote the class, I just pulled the encrypted strings from an XML file.



Also two lines of code can plug the connection manager functionality into you application when an admin user might have to change the connection string.



Good luck.
By Peter Denton - 12/17/2007

G'day

This is a very informative discussion. I have a couple of questions to ask.

We have been developing our application with the intent of adding security later on, so we have not delved too deeply yet, but the time is fast approaching. There has been mention here of seperate connections for Application Data and Security, I've also seen in the documentation that setting the SecurityDataSourceKey is not required if the SFS* tables are in database specified by the default DataSource. It there a best way of handling the Security tables, or is it a case of using whatever approach is applicable to the situation at hand?

Our application is a factory production system, used by workers many of whom have never touched a computer before. The system runs as terminal server sessions on a dedicated application server, with the data residing on a dedicated dataserver. We currently use .udf files to set the database connection, when we install the system, and this is only changed when due to server failure we need to host the database on the application server as well, and after repair to set it back again. This is a process that is only done by us or under our direction.

The application will only ever be run on the application server and the data can only be on the dataserver or the application server. We don't want to hard code the connection data, but as importantly we NEVER want our users to be asked anything about a database connection. When (not if) dataserver fails we need a means to quickly swap the datasource to Appserver and get going again (there could be 50 or more workers standing around twidling thumbs at this stage).  Any thoughts on the best way to handle this?

We though we would be able to write a program that would use the Connection Wizard (or something similar) to set the connection strings for the system (presumably stored in "C:\Documents and Settings\All Users\Application Data\MicroFour\ConnectionData"), that the other programs would use. Will this work or will we have to use the DataLayer.DataSources() collection and find some other place to store the connection data?

Peter

By Greg McGuffey - 12/17/2007

It there a best way of handling the Security tables, or is it a case of using whatever approach is applicable to the situation at hand?




I split mine up because the db I'm converting uses Triggers extensively, and the DDT doesn't support them...and deploying the security tables/views is sooooooo easy with DDT it just made sense. Also, the next major version of SF is rumored to possibly have some significant enhancements to the RBS module that will require SQL Server 2005 to work. By splitting them I can easily convert the security db and not have to with my app db (on SQL Server 2000). Other reasons to split them might be for performance (put security on a separate server), or security reasons. I.e. it totally depends on the situations at hand! Wink



We though we would be able to write a program that would use the Connection Wizard (or something similar) to set the connection strings for the system (presumably stored in "C:\Documents and Settings\All Users\Application Data\MicroFour\ConnectionData"), that the other programs would use. Will this work or will we have to use the DataLayer.DataSources() collection and find some other place to store the connection data?




You might want to check out shared settings files in SF help, as another option to manage connection information.
By Trent L. Taylor - 12/18/2007

All of Greg's comments are very good.  I thought I might add a few things that we do with our medical application as well.

It there a best way of handling the Security tables, or is it a case of using whatever approach is applicable to the situation at hand?

We actually use a single database and include the SFS tables in that same database.  This just makes distribution easier in the long-haul for us.  But splitting these up is totally viable as well.  It really comes down to what would suit your needs best.

We don't want to hard code the connection data, but as importantly we NEVER want our users to be asked anything about a database connection. When (not if) dataserver fails we need a means to quickly swap the datasource to Appserver and get going again (there could be 50 or more workers standing around twidling thumbs at this stage).  Any thoughts on the best way to handle this?

On our medical application we got kindof fancy so that none of the client workstations would ever have to ask for a connection...it finds the server by itself.  When the server installation takes place we DO ask for a connection through the installer.  This only has to be done once and when an upgrade occurs in the next update, this information is remembered so that they do not have to enter it again.  We have a service that runs on the server that manages our licensing, amongst many other things, including handing out connections to the clients.  When a client workstation starts and the doesn't know the connection information or the connection fails, it sends out a UDP broadcast to the network which the server "hears" and in turn sends out a UDP broadcast back to the client with the information that directs it to the server.  Once it finds the server, it will authenticate and the server will send the connection information back to the workstation.  All of this without the end-user ever having to know or do anything.  It has really made a difference in support....this is no longer a support issue....now we are one to other problems BigGrin  If the connection needs to change, then we go to the server, open up the connection dialog (we have a UI interface on the server), change the connection string, then restart the service (all automated).  This may be more involved than what you are looking to do...but it may give you another idea.

By Peter Denton - 12/18/2007

Greg & Trent,

I suspect that for simplicity we will also include the security tables in the Application Database. Although we also use triggers we don't use DDT, and we are developing for SQL Server 2005 so we don't have your constraints Greg.

It sounds as though there are any number of means of setting connections, so when we get to it we can choose the most appropriate.

Thanks for the advice.

Peter