Is there a way to expose the SqlDataAdapter within a TableAdapter as a Public property even after re-generating the TableAdapter
I can't use TableAdapters, because they don't expose the SqlDataAdapter as a Public property since I need to pass down the DataAdapter to a separate DB I/O method. Not a generated DB I/O method within the TableAdapter. I'm forced to use the Component Designer and create a SqlDataAdapter like I have been doing in VS.Net 2003.
The following code is generated when creating a TableAdapter:
<code>
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Private ReadOnly Property Adapter() As System.Data.SqlClient.SqlDataAdapter
Get
If (Me._adapter Is Nothing) Then
Me.InitAdapter()
End If
Return Me._adapter
End Get
End Property
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Public Property Connection() As System.Data.SqlClient.SqlConnection
Get
If (Me._connection Is Nothing) Then
Me.InitConnection
End If
Return Me._connection
End Get
Set
Me._connection = value
If (Not (Me.Adapter.InsertCommand) Is Nothing) Then
Me.Adapter.InsertCommand.Connection = value
End If
If (Not (Me.Adapter.DeleteCommand) Is Nothing) Then
Me.Adapter.DeleteCommand.Connection = value
End If
If (Not (Me.Adapter.UpdateCommand) Is Nothing) Then
Me.Adapter.UpdateCommand.Connection = value
End If
Dim i As Integer = 0
Do While (i < Me.CommandCollection.Length)
If (Not (Me.CommandCollection(i)) Is Nothing) Then
CType(Me.CommandCollection(i),System.Data.SqlClient.SqlCommand).Connection = value
End If
i = (i + 1)
Loop
End Set
End Property
</code>
Notice that you can set the Connection property as a Public property in the DataSet Designer, but there is no such option for the DataAdapter.
Just for kicks, I modified the code to make the DataAdapter a Public property (changed the Boldfaced Private to Public above), and was able to access it in code as follows:
<code>
Dim ta As New DataSet2TableAdapters.CustomersTableAdapter
Dim dst As New DataSet2
DataAccess.Data.SQLServerDataAccess.GetSQLData(ta.Connection, ta.Adapter, dst)
</code>
The Adapter contains the associated Command objects and passes it down to the DB I/O layer performing the appropriate FILL or UPDATE command. In this case, it's obviously the FILL command.
So, Microsoft should have made an option not to tie the TableAdapter to DB I/O code from within the generated code itself.
They also should include an option to make the generated DataAdapter a Public object (as seen above).
I hope this gets fixed in a service pack release. Barring this, does anybody know how I can consistently make the DataAdapter appear as a Public object
Thanks,
Bill

TableAdapters.....
zeffy
Hi Bill,
Sorry for the slow response... I don't make it out to the forums every day.
In your particular case, you would want to place the partial class inside of the namespace CustomersTableAdapterTableAdapters. From looking at your posted code, I think you also need to make the class declaration be Partial Class CustomersTableAdapter1. Both the namespace and the class name have to match exactly -- that's how the compiler figures out which class you are extending.
If that still doesn't work, post back here and I'll get the zipped project from you. I'm sure that we can make this work.
huyanan
Jay, I tried that and it didn't help. I'm still getting the exact same design time compilation error as in my previuous post.
My partial class now looks like this:
<code>
Namespace NorthwindDataSetTableAdapters
Partial Public Class CustomersTableAdapter
Public ReadOnly Property DataAdapter() As SqlClient.SqlDataAdapter
Get
Return Me.Adapter
End Get
End Property
End Class
End Namespace
</code>
Part of the generated code looks like this:
<code>
Namespace CustomersTableAdapterTableAdapters
<System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "2.0.0.0"), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.ComponentModel.ToolboxItem(true), _
System.ComponentModel.DataObjectAttribute(true), _
System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.DataSource.Design.TableAdapterDesigner, Microsoft.VSDesigner"& _
", Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), _
System.ComponentModel.Design.HelpKeywordAttribute("vs.data.TableAdapter")> _
Partial Public Class CustomersTableAdapter1
Inherits System.ComponentModel.Component
Private WithEvents _adapter As System.Data.SqlClient.SqlDataAdapter
Private _connection As System.Data.SqlClient.SqlConnection
Private _commandCollection() As System.Data.SqlClient.SqlCommand
Private _clearBeforeFill As Boolean
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Public Sub New()
MyBase.New
Me.ClearBeforeFill = true
End Sub
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Private ReadOnly Property Adapter() As System.Data.SqlClient.SqlDataAdapter
Get
If (Me._adapter Is Nothing) Then
Me.InitAdapter()
End If
Return Me._adapter
End Get
End Property
</code>
I tried encapsulating it inside the CustomersTableAdapterTableAdapters namespace as well which also didn't help.
I can zip up and email the whole project if you like. It's not big at all.
If I can expose that data adapter, I'd be able to use TableAdapters. Otherwise, I'm forced to code the way I have been in my initial post.
Any help would be appreciated.
Monkey Boy
Hi Bill,
I'm not sure I understand your scenario but here's generally how it works with TableAdapters...
We actually do add the table mappings to the underlying DataAdapter, but it's done automatically for you. In Visual Studio 2005, when you change a column name on the DataTable (that's the top part of the object on the designer) we create a table mapping that maps your DataTable column to the column in the database. You can manually change that mapping by changing the Source property for the column in the DataSet designer.
Any queries you add to the TableAdapter (by right-clicking on it in the designer and selecting Add Query) will correctly fill the DataTable using this mapping. The underlying DataAdapter should have the TableMappings property set after initializing. From the code you posted above, were you expecting more than just the "SrchID" column to be mapped
Hans Yadav
Hi again Bill,
You're right that wiring up classes to your data source requires some extra work. This is one of the scenarios that we've spent some time thinking about for future versions of Visual Studio. Hopefully the new DLinq features along with the DLinq Designer will go a long way toward removing this burden -- although it won't be within the typed DataSet model. Currently there is a plan to release a CTP of these new features in late April. If you have time, you should check it out and send us feedback on whether you find it helpful.
I tried your scenario with the ObjectBindingSource.Updated event and I do see that it returns -1 for AffectedRows. Interestingly, I am able to call the same Update method directly on the DataSet and I do get the correct return value. I'm not a web expert as my team deals mostly with client apps, but it looks like you've found a bug. Could I ask you to you file a bug at http://lab.msdn.microsoft.com/productfeedback/ That way it will be routed to the correct team, and they can provide you information about whether this is a known issue and if there are easy workarounds. The repro information you posted here should help the web team to see the issue.
Thanks,
Mathias Holmgren
Jay, I tried that, but I still am getting a design-time compile error saying that "Adapter is not a member of NorthwindDataSetTableAdapters.CustomersTableAdapter"
I have the following code in a Partial class which hangs off the CustomersTableAdapter.xsd file:
<code>
Partial Public Class CustomersTableAdapter
Public ReadOnly Property DataAdapter() As SqlClient.SqlDataAdapter
Get
Return Me.adapter
End Get
End Property
End Class
</code>
Inside the generated file is the following code:
<code>
<System.Diagnostics.DebuggerNonUserCodeAttribute()> _
Private ReadOnly Property Adapter() As System.Data.SqlClient.SqlDataAdapter
Get
If (Me._adapter Is Nothing) Then
Me.InitAdapter()
End If
Return Me._adapter
End Get
End Property
</code>
The project name is called "NorthwindDataSetTableAdapters" and the TaqbleAdapter is called "CustomersTableAdapter"
It still appears that the dataadapter property still needs to be generated as a Public object in order for me to access it.
Isn't this true or am I doing something incorrectly defining the Partial class
NTCin Bham
Hi Bill,
You could expose the private Adapter member by writing a partial class that returned it through a public property. For example, if you were using the Northwind Customers table, the code would look like this:
Namespace NorthwindDataSetTableAdapters
Public Class CustomersTableAdapter
Public ReadOnly Property DataAdapter() As SqlClient.SqlDataAdapter
Get
Return Me.Adapter
End Get
End Property
End Class
End Namespace
As long as you put this in a separate class file, it will never be overwritten by the generator.
I hope this helps.
peachy4
Jay, I've progressed where I've written all my code in my project and testing was going flawlessly except for one major sticking point I see about TableAdapters...
There is not a TableMapping property I can easily modify to sync up columns in the datatable with a parameter list.
For instance, while using the Component Designer and creating a dataadapter, there is a TableMapping collection where you can set the table columns with the associatged datasource columns. This creates a parameter list where you just need to pass down the contents of the datatable and when the "Update" statement of the dataadapter takes place, the appropriate updates inside the datatable will take place in accordance with the rows' rowstate.
<code>
'generated code
Me
.dapSrchBatch.TableMappings.AddRange(New System.Data.Common.DataTableMapping() {New System.Data.Common.DataTableMapping("Table", "SrchBatch", New System.Data.Common.DataColumnMapping() {New System.Data.Common.DataColumnMapping("BatchID", "BatchID"), New System.Data.Common.DataColumnMapping("SrchID", "SrchID"), New System.Data.Common.DataColumnMapping("EngineID", "EngineID"), New System.Data.Common.DataColumnMapping("RecrdSrchd", "RecrdSrchd"), New System.Data.Common.DataColumnMapping("RecrdsFound", "RecrdsFound"), New System.Data.Common.DataColumnMapping("SrchTime", "SrchTime")})})'non-generated cocde
Dim
intRecdsAffected As Int16 = SQLServerDataAccess.ExecuteSQLNonQuery(Connection, dapSrchBatch, DstSrchBatch1)</code>
On the contrary, the TableAdapter has no associated TableMapping property that is easily editable like the one in the Component Designer. See below generated code:
<code>
tableMapping.SourceTable =
"Table"tableMapping.DataSetTable =
"SrchBatch"tableMapping.ColumnMappings.Add(
"SrchID", "SrchID") Me._adapter.TableMappings.Add(tableMapping)Me
._adapter.InsertCommand = New System.Data.SqlClient.SqlCommand Me._adapter.InsertCommand.Connection = Me.Connection Me._adapter.InsertCommand.CommandText = "dbo.InsertSrchBatch" Me._adapter.InsertCommand.CommandType = System.Data.CommandType.StoredProcedure Me._adapter.InsertCommand.Parameters.Add(New System.Data.SqlClient.SqlParameter("@RETURN_VALUE", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.ReturnValue, 10, 0, Nothing, System.Data.DataRowVersion.Current, false, Nothing, "", "", ""))</code>
As you can see, there is no column collection inside the tablemapping property. I suppose I could expose the TableMapping property of the TableAdapter (like we discussed before), and add the column collection to the TableAdapter, but that amounts to a lot of manual coding that I thought the TableAdapter would have been able to handle through generated code.
Is this the only way to go with it Will it be enhanced in a future release
Thanks in advance,
Bill
jdiazbejJuan
Jay, it just so happened that the datatable I was dealing with, didn't have some of the columns mapped to the datasource, so I manually mapped them like you suggested.
The rest of tableadapters automatically generated the appropriate datasource columns.
I did see in the generated code, that it did place them in the tablemapping column collection, so I'll try my test again, but I'm pretty sure it will work this time based on the change I made.
My mistake....
LaBomba
Hi Bill,
First, there's a small error in my code above. The class CustomersTableAdapter should have been declared partial -- but it looks like you already caught that and corrected it in your code, so on to the more important issue...
You still need to wrap your partial class declaration inside a namespace declaration:
Namespace NorthwindDataSetTableAdapters
<Your partial class code>
End Namespace
I believe that otherwise you end up with the TableAdapter classes inside a nested namespace (NorthwindDataSetTableAdapters.NorthwindDataSetTableAdapters) while your partial class is in the root namespace (NorthwindDataSetTableAdapters).
siwi
Jay, any ideas yet on my latest post
Thanks so much....
codecamel
Jay, that did it!
Here's a sample of the code I created for the partial class hanging off the xsd file
<code>
Namespace CustomersTableAdapterTableAdapters
Partial Public Class CustomersTableAdapter1
Public ReadOnly Property DataAdapter() As SqlClient.SqlDataAdapter
Get
Return Me.Adapter
End Get
End Property
End Class
End Namespace
</code>
The following code is just a sample of what I'm doing in a winform:
<code>
Imports DataAccess.Data
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'retrieve data
Dim ta As New CustomersTableAdapterTableAdapters.CustomersTableAdapter1
Dim tblCustomers As New CustomersTableAdapter
SQLServerDataAccess.GetSQLData(ta.Connection, ta.DataAdapter, tblCustomers.Customers)
'update data
Dim intrecdsaffected As Int16 = SQLServerDataAccess.ExecuteSQLNonQuery(ta.Connection, ta.DataAdapter.UpdateCommand)
'update data with a transaction
Dim trns As SqlClient.SqlTransaction
trns = ta.Connection.BeginTransaction(IsolationLevel.Serializable)
Dim bln As Boolean = SQLServerDataAccess.SaveSQLData(ta.Connection, ta.DataAdapter, tblCustomers.Customers, SQLServerDataAccess.UpdateType.ErrorsOccurred, intrecdsaffected, trns)
trns.Commit()
End Sub
End Class
</code>
Now, I'll be able to use TableAdapters in my project with the DataAdapter exposed as an object to be passed down to a DB I/O layer.
Thanks so much...
noobie
although it is not recommended nor supported by MS...you can go into the designer generated code and change the scope yourself!
(as I now see you did
)
troff
Jay, everything works fine now... Thanks a lot for your insight.
However, there are a couple of things I wish to note and get your feedback on.
When using a STD (strongly typed dataset), the SELECT functionality works as expected as evidenced in the code below:
<code>
<DataObjectMethod(DataObjectMethodType.Select)> _
Public Function SelectCustomers() As dstCustomer Try Dim DstCustomers1 As New dstCustomerdapCustomer.SelectCommand = cmdSelectCustomers
SQLServerDataAccess.GetSQLData(Connection, dapCustomer, DstCustomers1.Customers)
Return DstCustomers1 Catch ex As Exception Throw ex FinallydapCustomer.Dispose()
End Try End Function</code>
However, when using the other update methods, you still must use a "Helper" class in order for the ObjectDataSource to interact with. You simply can't use the STD by itself, which I feel would make it a lot more efficient if the ObjectDataSource was able to interact with a STD. Check out the code below of what I needed to do to get an UPDATE to work:
<code>
Namespace
dstCustomerTableAdaptersPartial
Public Class CustomersTableAdapter.
.
.
<DataObjectMethod(DataObjectMethodType.Update)> _
Public Function UpdateCustomer(ByVal clsCustomer As dstCustomer) As Boolean Try Dim intRecdsAffected As Integer Dim DstCustomers1 As New dstCustomer Dim dr As dstCustomer.CustomersRow = DstCustomers1.Customers.NewCustomersRowdr.Address = clsCustomer.Address
dr.City = clsCustomer.City
dr.CompanyName = clsCustomer.CompanyName
dr.ContactName = clsCustomer.ContactName
dr.ContactTitle = clsCustomer.ContactTitle
dr.Country = clsCustomer.Country
dr.CustomerID = clsCustomer.CustomerID
dr.Fax = clsCustomer.Fax
dr.Phone = clsCustomer.Phone
dr.PostalCode = clsCustomer.PostalCode
DstCustomers1.Customers.AddCustomersRow(dr)
DstCustomers1.Customers.AcceptChanges()
DstCustomers1.Customers.Item(0).SetModified()
Dim blnOK As Boolean = SQLServerDataAccess.SaveSQLData(Connection, dapCustomer, DstCustomers1.Customers, SQLServerDataAccess.UpdateType.ErrorsOccurred, intRecdsAffected, Nothing) Return blnOK Catch ex As Exception Throw ex End Try End FunctionEnd
ClassEnd
NamespacePartial
Public Class dstCustomer Private sCustomerID As String Private sCompanyName As String Private sContactName As String Private sContactTitle As String Private sAddress As String Private scity As String Private sRegion As String Private sPostalCode As String Private sCountry As String Private sPhone As String Private sFax As String Public Property CustomerID() As String Get Return sCustomerID End Get Set(ByVal value As String)sCustomerID = value
End Set End Property Public Property CompanyName() As String Get Return sCompanyName End Get Set(ByVal value As String)sCompanyName = value
End Set End Property Public Property ContactName() As String Get Return sContactName End Get Set(ByVal value As String)sContactName = value
End Set End Property Public Property ContactTitle() As String Get Return sContactTitle End Get Set(ByVal value As String)sContactTitle = value
End Set End Property Public Property Address() As String Get Return sAddress End Get Set(ByVal value As String)sAddress = value
End Set End Property Public Property City() As String Get Return scity End Get Set(ByVal value As String)scity = value
End Set End Property Public Property Region() As String Get Return sRegion End Get Set(ByVal value As String)sRegion = value
End Set End Property Public Property PostalCode() As String Get Return sPostalCode End Get Set(ByVal value As String)sPostalCode = value
End Set End Property Public Property Country() As String Get Return sCountry End Get Set(ByVal value As String)sCountry = value
End Set End Property Public Property Phone() As String Get Return sPhone End Get Set(ByVal value As String)sPhone = value
End Set End Property Public Property Fax() As String Get Return sFax End Get Set(ByVal value As String)sFax = value
End Set End PropertyEnd
Class</code>
While interacting with a STD (as sampled below in a 1.1 code snippet):
<code>
Public Function UpdateCustomer(ByVal DstCustomer1 As dstCustomer) As Boolean
</code>
the presentation layer code simply created the datarow and passed it down to the middle tier logic. This could be achieved because you could set a STD as the datasource to a datagrid. Now, I know you can still do that with the gridview, but if you do, you would lose all the "new" functionality that the objectdatasource coupled with the gridview supports. You would have to do this all manually.
Thus, you need to use the Helper class in order to populate the STD datarow (which already has its parameters set up via the tablemapping).
It would be great if you didn't need this Helper class and the objectdatasource/gridview would simply interact with the STD.
Another separate issue is in the code below I use to update the data:
<code>
Dim
blnOK As Boolean = SQLServerDataAccess.SaveSQLData(Connection, dapCustomer, DstCustomers1.Customers, SQLServerDataAccess.UpdateType.ErrorsOccurred, intRecdsAffected, Nothing)</code>
The intRecdsAffected comes back with a value of 1 (as expected) from my DB I/O layer. However it is not properly reflected in the prestation layer code with the ObjectDataSource as evideneced below:
<code>
Protected Sub ObjectDataSource1_Updated(ByVal sender As Object, _ ByVal e As System.Web.UI.WebControls.ObjectDataSourceStatusEventArgs) _ Handles ObjectDataSource1.Updatede.AffectedRows =
CType(e.ReturnValue, Integer) End Sub</code>
The e.AffectedRows comes back as a -1 (e.ReturnValue is True). If I use the above code without a STD and simply with the use of Generics and the Helper class, it comes back as a 1 (as expected).
I could check my intRecdsAffected on my middle tier function and throw and error if it's not a 1.
Anyway, the e.AffectedRows property is not coming back correctly.
Thanks so much for your help and insight. I wanted to point this out as the only "sticking" points left with using STD's and the ObjectDataSource.
Any comments would be appreciated.
skrymsli