I'm a total newbie at the .net c# business and dove in this last week creating a form application to shuffle data around for SSIS configurations. The geniuses at MS decided not to apply a key to the table I'm working with - and generating a composite key of the two candidate fields will not work due to their combined length being too long. I don't particularly want to mess with the [ssis configurations] table schema by adding an autoincrement field.
So I've been having alot of trouble getting an update from a DataGridView control to work with a TableAdapter.
I need the update statement to be update table set a = x where b = y and c = z.
Can I set the update method of the TableAdapter, and if so, how. If not, what to do?
I see this autogenerated code:
this._adapter.InsertCommand = new global::System.Data.SqlClient.SqlCommand();
this._adapter.InsertCommand.Connection = this.Connection;
this._adapter.InsertCommand.CommandText = "INSERT INTO [dbo].[SSIS Configurations Staging] ([ConfigurationFilter], [Configur" +
"edValue], [PackagePath], [ConfiguredValueType]) VALUES (#ConfigurationFilter, #C" +
"onfiguredValue, #PackagePath, #ConfiguredValueType)";
But in my form code, the UpdateCommand is not available. I'm assuming this is because the above code is a class definition which I cannot change after creating the object. I see this code has a recommendation not to be changed since it is autogenerated by VS.
Thanks for your most excellent advice.
From your code i assume you are using a typed Dataset with the designer.
Not having a primary key is one of the many reasons the designer will not generate Insert, Update or Delete commands. This is a limitation of the CommandBuilder.
You could use the properties window to add an Update Command to the Apdapter but I would advice against that, if you later configure your main query again it will happily throw away all your work. The same argument holds against modifying the code in any *.Designer.cs file.
Instead, doubleclick on the caption with the Adaptername. The designer will create (if necessary) the accompanying non-designer source file and put the outside of a partial class in it. Unfortunately that is how far the code-generation of the designer goes in C#, you'll have to take it from there. (Aside: The VB designer knows a few more tricks).
Edit:
You will have to provide your own Update(...) method and setup an UpdateCommand etc.
var updateCommand = new SqlCommand();
...
Related
I have a DataGridView. It uses a BindingSource, a DataTable in a DataSet, and a TableAdapter to add/change/delete data in a table. It worked OK, but stopped working when I added a field/column, and I can't figure out what I did or how fix it.
The user can add a new row at the bottom of the DataGridView, but when he goes to save, the row disappears and is not saved. In addition, if he tries to type a second new row, the first new row disappears.
Existing Rows can be changed and saved back to the database successfully.
I've been asked for code. OK, here is code. (I've eliminated some error checking done by scanning dtDep) The point that after the third line is executed, there are no rows in dtDep even though a new row had been entered into the DataGridView. If a row had been retrieved, it would be in dtDep and the database table updated by the last statement.
this.Validate();
bsBelkDep.EndEdit();
DataTable dtDep = dsBelk.Tables["belk_elig_dep"];
int n = belk_elig_depTableAdapter.Update(this.dsBelk.belk_elig_dep);
It was a problem with the DataGridView, but I don't know what. I started deleting and re-creating the various object, and after the I recreated the DataGridView, it worked OK. Which was a pain because I have to do significant reformatting, but at least it works.
This is a very old question and I have no way of knowing if it was the OP's original problem, but I had the exact same scenario and this is how I resolved it.
For background: I have a WinForms application built using datasets and an Access database. I migrated that to use Sqlite and anything but datasets. To avoid destroying the application completely, first I copied the strongly typed data tables out, tweaked them to account for changes in the schema and then used PetaPoco to perform the data operations. That worked fine for a single test conversion.
The trouble arose when I wanted to move on and convert all data tables - I wasn't happy manually writing the logic for converting to and from typed data rows and POCOs, so I fell back to writing old school T4 templates to generate typed DataTable, DataRow classes and the necessary remapping code.
Worked a treat - for editing or removing data. But new rows disappeared on "creation", the binding navigator count didn't increment, and of course, when saving, I didn't detect any rows with the RowState of DataRowState.Added. The grid at start up was subtly different - a blank value in all columns instead of a negative number in the ID column. In hindsight, that should have been a big clue.
On reverting the behaviour back to the manually extracted typed class the grid started working again so it was clearly an error in the new code.
End of background; tldr;
The cause of the issue, in my case, was that the my Id column didn't have the AutoIncrement property set. As soon as I configured that to be true (along with setting AutoIncrementSeed and AutoIncrementStep to -1, although neither are required) new rows started being correctly added to the table.
I'm using Entity Framework and SQL Server 2008 with the Database First approach.
My problem is :
I have some tables that holds many many columns (~100), and when I try to retrieve a lot of rows it takes a significant time before it returns the results, even if sometimes I need to use just 3 or 4 columns from that table.
I passed half a day in Stackoverflow trying to find a way to solve this problem, and I came up with two solutions :
Using stored procedures to retrieve data with the columns I want.
Edit the .edmx (xml) and the .cs files to remove the columns that I won't use.
My problem again is :
If I use stored procedures to retrieve the data with the columns that I want, Entity Framework loose it benefit and I can use ADO.NET instead of it and call directly the stored procedures ...
I can't take the second solution, because every time I make a change in the database, I'm obliged to regenerate the .edmx file and I loose the changes I made before :'(
Is there a way to do this somehow in Entity Framework ? Is that possible !
I know that other ORMs exist like NHibernate or Dapper, but I don't know if they can offer this feature without causing a lot of pain.
You don't have to return every column each time. You can specify which columns you need.
var query = from t in db.Table
select new { t.Column1, t.Column2, t.Column3 };
Normally if you project the data into a different poco it will do this automatically in EF / L2S etc:
var slim = from row in db.Customers
select new CustomerViewModel {
Name = row.Name, Id = row.Id };
I would expect that to only read 2 columns.
For tools like dapper: since you control the SQL, only specify columns you want - don't use *
You can create a second project with a code-first DbContext, POCO's and maps that return the subset of columns that you require.
This is a case of cut and paste code but it will get you what you need.
You can just create classes and project the data into them but I'm not sure you can make updates using this method. You can use anonymous types within a single method but you'll need actual classes to pass around between methods.
Another option would be to move to a code first development.
This is my first time working with DataSets and BindingSources, so please be gentle on me.
As part of a more complicated reporting system (I've distilled it down to a basic incarnation, but it still won't run correctly), I'm trying to pull data from a database using a DataSet problematically (that is, not set up via the designer). Here is the code I have so far:
// pull the data set
var dsReportData = new Reports2.ReportTest2();
dsReportData.BeginInit();
dsReportData.SchemaSerializationMode = SchemaSerializationMode.IncludeSchema;
// bind tha data set
BindingSource bsReportBinding = new BindingSource();
((ISupportInitialize)bsReportBinding).BeginInit();
bsReportBinding.DataMember = dsReportData.Tables[0].TableName;
bsReportBinding.DataSource = dsReportData;
bsReportBinding.ResetBindings(true);
// test this stuff
dgvTester.DataSource = bsReportBinding;
dsReportData.EndInit();
((ISupportInitialize)bsReportBinding).EndInit();
I based this on the code I saw in a .designer.cs file after setting up binding through the designer. dgvTester is just a DataGridView with the default properties created in the designer.
The ReportTest2 dataset has just one TableAdapter in it, added via designer.
Now, if I went to Data -> Preview Data in VS and previewed the ReportTest2.payments.Fill,GetData () object, it returns data just fine, same as if I ran the query I used to crate the TableAdapter in SQL Server Management Studio.
However, running the actual code results in the DataGridView getting the column names from the query result, but not the actual data. The debugger reveals that dsReportData.payments.Rows.Count == 0 (and that, yes, dsReportData.Tables[0] is payments).
I ultimately intend to use the BindingSource to provide data to a ReportViewer, but first things first is making sure there's no problems with retrieving the data before going onto debug the report.
Hopefully I'm missing something obvious here. I hope so...
Figured it out after some trial and error. This is the part I was missing:
var TableAdapter = new Reports2.ReportTest2TableAdapters.paymentsTableAdapter();
TableAdapter.Fill(dsReportData.payments);
I didn't see it in the code I was referencing because the designer snuck it into the .cs file instead of the .designer.cs file. This made the data appear.
Before insert new value to table, I need change one field in all rows of that table.
What the best way to do this? in c# code, ore use trigger? if C# can you show me the code?
UPD
*NEW VERSION of Question*
Hello. Before insert new value to table, I need change one field in all rows of that table with specific ID( It is FK to another table).
What the best way to do this? in c# code, ore use trigger? if C# can you show me the code?
You should probably consider changing your design this doesn't sound like it will scale well, i would probably do it with a trigger if it is always required, but if not, id use ExecuteCommand.
var ctx = new MyDataContext();
ctx.ExecuteCommand("UPDATE myTable SET foo = 'bar'");
Looking at your comment on Paul's answer, I feel like I should chime in here. We have a few tables where we need to keep a history of each entry in that table. We implement this by creating a separate table for each. For example, we may have a Comment table, and then a CommentArchive table with a foreign key reference to the CommentId in the Comment table.
A trigger on the Comment table ensures that each time certain fields in the Comment table are updated, the "old" version (which is accessible via the deleted table in the trigger) gets pushed to the CommentArchive table. Obviously, this means several CommentArchive entries may exist for each Comment, but if you're only looking for the "active" comments, you just look in the Comment table. And if you need information about the history of a comment, you can easily use LINQ to SQL to jump from the Comment you're interested in to the CommentArchives that reference it.
Because the triggers we use in the above example only insert a single value into the Archive table for each update, they run very quickly and we get good performance. We had issues recently where I tried making the triggers more complex and we started getting dead-locks with as few as 15 concurrent transactions. So the lesson is that you should make these triggers simple, and make them touch as few rows in as few tables as possible.
I'm currently doing the following to use typed datasets in vs2008:
Right click on "app_code" add new dataset, name it tableDS.
Open tableDS, right click, add "table adapter"
In the wizard, choose a pre defined connection string, "use SQL statements"
select * from tablename and next + next to finish. (I generate one table adapter for each table in my DB)
In my code I do the following to get a row of data when I only need one:
cpcDS.tbl_cpcRow tr = (cpcDS.tbl_cpcRow)(new cpcDSTableAdapters.tbl_cpcTableAdapter()).GetData().Select("cpcID = " + cpcID)[0];
I believe this will get the entire table from the database and to the filtering in dotnet (ie not optimal), is there any way I can get the tableadapter to filer the result set on the database instead (IE what I want to is send select * from tbl_cpc where cpcID = 1 to the database)
And as a side note, I think this is a fairly ok design pattern for getting data from a database in vs2008. It's fairly easy to code with, read and mantain. But I would like to know it there are any other design patterns that is better out there? I use the datasets for read/update/insert and delete.
A bit of a shift, but you ask about different patterns - how about LINQ? Since you are using VS2008, it is possible (although not guaranteed) that you might also be able to use .NET 3.5.
A LINQ-to-SQL data-context provides much more managed access to data (filtered, etc). Is this an option? I'm not sure I'd go "Entity Framework" at the moment, though (see here).
Edit per request:
to get a row from the data-context, you simply need to specify the "predicate" - in this case, a primary key match:
int id = ... // the primary key we want to look for
using(var ctx = new MydataContext()) {
SomeType record = ctx.SomeTable.Single(x => x.SomeColumn == id);
//... etc
// ctx.SubmitChanges(); // to commit any updates
}
The use of Single above is deliberate - this particular usage [Single(predicate)] allows the data-context to make full use of local in-memory data - i.e. if the predicate is just on the primary key columns, it might not have to touch the database at all if the data-context has already seen that record.
However, LINQ is very flexible; you can also use "query syntax" - for example, a slightly different (list) query:
var myOrders = from row in ctx.Orders
where row.CustomerID = id && row.IsActive
orderby row.OrderDate
select row;
etc
There is two potential problem with using typed datasets,
one is testability. It's fairly hard work to set up the objects you want to use in a unit test when using typed datasets.
The other is maintainability. Using typed datasets is typically a symptom of a deeper problem, I'm guessing that all you business rules live outside the datasets, and a fair few of them take datasets as input and outputs some aggregated values based on them. This leads to business logic leaking all over the place, and though it will all be honky-dory the first 6 months, it will start to bite you after a while. Such a use of DataSets are fundamentally non-object oriented
That being said, it's perfectly possible to have a sensible architecture using datasets, but it doesn't come naturally. An ORM will be harder to set up initially, but will lend itself nicely to writing maintainable and testable code, so you don't have to look back on the mess you made 6 months from now.
You can add a query with a where clause to the tableadapter for the table you're interested in.
LINQ is nice, but it's really just shortcut syntax for what the OP is already doing.
Typed Datasets make perfect sense unless your data model is very complex. Then writing your own ORM would be the best choice. I'm a little confused as to why Andreas thinks typed datasets are hard to maintain. The only annoying thing about them is that the insert, update, and delete commands are removed whenever the select command is changed.
Also, the speed advantage of creating a typed dataset versus your own ORM lets you focus on the app itself and not the data access code.