Copying a row from one table to another using LINQ and C# - c#

I have two tables that are pretty much exact clones of one another (identical columns, just different columns set as primary keys). Basically the second table is just for keeping a history of the first table. What I need to do is, when a user updates a record in table 1 I need to insert the original copy of that record into table 2.
I am using a LinqDataSource object and utilizing the LinqDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e) event so I have access to e.OriginalObject and that will be perfect for inserting the original row in table 2. My problem is that I don't want to have to set every property manually because there are about 50 of them, so I want to use Reflection but am not sure how to properly go about it.
Consider the following code:
INSTRUMENT_DATA_SHEET _original = (INSTRUMENT_DATA_SHEET)e.OriginalObject;
INSTRUMENT_DATA_SHEET_HISTORY _history = new INSTRUMENT_DATA_SHEET_HISTORY();
How can I go about copying all of the _original's property values to _history's? I have tried using the solution from this question, however it isn't working for me. It throws the error:
Property DATE has an incompatible type in E_and_I.INSTRUMENT_DATA_SHEET_HISTORY
My guess is that it's because the DATE column is part of the primary key in table 2, but not table 1. As I said, the only difference between the two tables are the primary keys. Here they are for your reference:

The problem I see is that your History type Date field is DateTime and your Original one is DateTime? (same problem with REV in History, it can't be null). You'll have to decide what happens if there is a null DateTime of Date in your original version. Then you should be able to modify Skeets code (oh dear!) to handle specifically these fields differently then the rest of the fields.

Ok I've managed to figure it out :) Here's what I did:
INSTRUMENT_DATA_SHEET _original = (INSTRUMENT_DATA_SHEET)e.OriginalObject;
INSTRUMENT_DATA_SHEET_HISTORY _history = new INSTRUMENT_DATA_SHEET_HISTORY();
foreach (PropertyInfo pi in _original.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
_history.GetType().GetProperty(pi.Name).SetValue(_history, pi.GetValue(_original, null), null);
}
Not very elegant but it gets the job done!

Related

Can I access entire DataTable if all I have is a single DataRow?

DataRow contains a Table property, which seems to return the entire Table for which this row belongs.
I'd like to know if I can use that table safely, or if there are gotcha's.
In http://msdn.microsoft.com/en-us/library/system.data.datarow.table.aspx documentation, it says "A DataRow does not necessarily belong to any table's collection of rows. This behavior occurs when the DataRow has been created but not added to the DataRowCollection.", but I know for a fact my row belongs to a table.
In terms of pointers, if each Row from DataTable points to original DataTable, than I'm good to go. Is that all 'Table' property does?
Just to explain why I'm trying to get entire Table based on a single DataRow:
I'm using linq to join two (sometimes more) tables. I'd like to have a generic routine which takes the output of linq (var), and generate a single DataTable with all results.
I had opened another question at stackoverflow (Join in LINQ that avoids explicitly naming properties in "new {}"?), but so far there doesn't seem to be a generic solution, so I'm trying to write one.
if you know the row is part of table than yes you can access it without any problem. if the possibility exists where the row may not be associated to a table than check if the property is null.
if(row.Table == null)
{
}
else
{
}
As long as it's not null, you can use it freely.

InsertAllOnSubmit only inserts one item

I am trying to use InsertAllOnSubmit to do multiple inserts, but only 1 item ever gets inserted into the table. The only reason I can think that this is happening is something to do with the generation of the Primary Key field, I am currently allowing the code to auto generate this field, and it doesn't seem to be doing that. Can anybody help?
List<rewardsClaimed> lstRewardsClaimed = new List<rewardsClaimed>();
for (int i = 0; i < ticket.delivery.quantity; i++)
{
rewardsClaimed claim = result;
lstRewardsClaimed.Add(claim);
}
dc.rewardsClaimeds.InsertAllOnSubmit(lstRewardsClaimed);
dc.SubmitChanges();
EDIT :
I have found out what the problem is....the ID field gets set to 0 by default, so that when the insert is done, since all rows have the same ID, they are treated as 1 row. How would I prevent this?
The problem is that List and Table behave completely different.
A List<T> allows you to add the same instance more than once without checking the reference in other words in your list you have multiple references to the same object.
Table<T> in the other hand when you call InsertAllOnsubmit method checks every item in the collection to see if its already "marked" to be inserted in the table. If the object its already "marked" skips it.
What is happening with your code is that you are passing the List of claims that make reference n number of times to the same object. When is passed to Table<T> the first ocurrence is marked for insertion. Since the other items in the list are the same object, they are already marked for insertion.
Hope this clarifies.
Everyone is asking to create new instance of the class RewardsClaimed. But I thought that it is not the correct reason. Hence I tried the following sample code and found that 5 different instances are added in the list.
Part 1: Showing that same item can be added to local list without creating new instance (Its not the answer)
Code:
Results:
Part 2: Creation of new primary key will be the solution. (This is the issue and solution.)
I would suggest that, please check the data in your PrimaryID Column is not repeated. By this I mean, if your primary key is not automatically generated and you generate it manually, chances are it gets rows with same primarykeys n number of times. If it is repeated then it will only insert single instance of it.
Edit: How to auto generate Field Value
If you want to auto-generate primary key refer this.
you are not creating new instance of claim.
rewardsClaimed claim = new rewardsClaimed();
List<rewardsClaimed> lstRewardsClaimed = new List<rewardsClaimed>();
for (int i = 0; i < ticket.delivery.quantity; i++)
{
rewardsClaimed claim = new rewardsClaimed();
claim.property = result.property; // do this foreach property except id field
lstRewardsClaimed.Add(claim);
}
dc.rewardsClaimeds.InsertAllOnSubmit(lstRewardsClaimed);
dc.SubmitChanges();

How do you add a field to an rfcTable using SAP Nco 3.0?

I can't understand the documentation and really need a concrete example.
I've already created the destination. Here I define my BAPI:
IRfcFunction BapiIncomingInvoiceGetDetail = SapRfcRepository.CreateFunction("BAPI_INCOMINGINVOICE_GETDETAIL");
Set my imports, invoke it, and then get my exports - one of which is a table:
IRfcTable ITEMDATATable = BapiIncomingInvoiceGetDetail.GetTable("ITEMDATA");
I now want to add a field to each item in the table ITEMDATATable and set its' value so I can reference it later as if it were one of thefields returned by the BAPI. Can anyone tell me how?
EDIT: Okay, I've made some progress:
RfcFieldMetadata newField = new RfcFieldMetadata("SKU_AMT",0,0,0);
ITEMDATATable.CurrentRow.Metadata.AddField(newField);
ITEMDATATable.SetValue("SKU_AMT",myItemData.SKU_AMT);
However, when I try to set the value, I get RfcInvalidStateException "Cannot add an element to locked STRUCTURE BAPI_INCINV_DETAIL_ITEM".
Any way around this?
you can't append columns to the table, the fields are already defined. You need to add a row to the table and populate the fields of that row. This should work (although i can't test it right now):
IRfcTable ITEMDATATable = BapiIncomingInvoiceGetDetail.GetTable("ITEMDATA");
ITEMDATATable.Append();
ITEMDATATable.SetValue("SKU_ATM",myItemData.SKU_AMT);

Committing changes to the database using Entity Framework

I have a situation where I pull data from a table by date. If no data is supplied for a given date I create a record using default values and display it all to the user. When the user is done manipulating the data I need to commit the changes.
So my question is how do I handle in Entity Framework submitting a table where there could be both updates and adds that need to be done. This is in C# using MVC3 and Entity Framework.
So here's what the data might look like to start,
Table A
NAME AGE PHONE_NUM
Jim 25 555-555-5555
Jill 48 555-551-5555
After the users done with the data it could look like this,
Table A
NAME AGE PHONE_NUM
Jim 25 555-555-5555
Jill 28 555-551-5555
Rob 42 555-534-6677
How do I commit these changes? My problem is there are both updates and inserts needed?
I've found some code like this but I don't know if it will work in this case.
For adding rows of data
entities.TABlEA.AddObject(TableOBJECT);
entities.SaveChanges();
or for updating data
entities.TABLEA.Attach(entities.TABLEA.Single(t => t.NAME == TableOBJECT.NAME));
entities.TABLEA.ApplyCurrentValues(TableOBJECT);
entities.SaveChanges();
Will any of this work or do I need to keep track of whats there and what was added?
Ideas?
More or less you already have the solution. You just need to check if your Single call which tries to load the object from the DB has an result or not (use SingleOrDefault instead). If the result is null you need to insert, otherwise update:
foreach (var TableOBJECT in collectionOfYourTableOBJECTsTheUserWorkedWith)
{
var objectInDB = entities.TABLEA
.SingleOrDefault(t => t.NAME == TableOBJECT.NAME);
if (objectInDB != null) // UPDATE
entities.TABLEA.ApplyCurrentValues(TableOBJECT);
else // INSERT
entities.TABLEA.AddObject(TableOBJECT);
}
entities.SaveChanges();
(I'm assuming that NAME is the primary key property of your TableOBJECT entity.)
I think you have to keep track of what is new and what is modified. If you do that, that the two code examples you provided are going to work.
A simple workaround which I used is to check if an entity's primary key property is set to anything. If it is set to a value, then that is an updated object, otherwise it's new.
Another solution would be to use Entity Framework's Self Tracking Entities, but I do not think that's the right direction to go in a web application (maybe it is in a distributed WCF app).

Add custom field to linq to sql auto generated entity

I have a table with a varbinary(max) column for an image. I have dropped the table on the LinqToSql designer and have set "Delay load" to true, since I don't want to load the actual image data.
Is it possible to just know if the column is null or not, without getting the actual data and still only doing one query from the database?
I would also like to use the automated entity created by Linq.
Something like a new bool HasImage {get;} property would be just what I'm looking for.
The only way for Linq to SQL to "automatically" know whether or not the column has a value is to actually ask the database for it. You can extend the partial class with fields/properties, but that's not going to eliminate the lookup.
One of the things you could do is created a computed column (assuming SQL 2005+ here, otherwise you'll have to try to adapt this to your DBMS). If your table looks like this, for example:
CREATE TABLE Foo
(
FooID int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
FooName varchar(50) NOT NULL,
FooImage varbinary(max) NULL
)
You would add the computed column this way:
ALTER TABLE Foo
ADD FooHasImage AS CASE
WHEN FooImage IS NULL THEN 0
ELSE 1
END
Then you can add the FooHasImage column to your Linq to SQL entity, don't delay load it, and check that property instead of explicitly checking the FooImage property.
Also, I feel obligated to point out that storing images in a database this way is sub-optimal. It may be necessary, I don't know much about your environment, but if you're using SQL Server 2008 then consider using FILESTREAM instead, as it will use the file system for cheap "offline" BLOB storage instead of stuffing the entire thing in the database.
Create a partial class
public partial class MyTableObject
{
public bool HasImage { get { return MyColumn.HasValue; } }
}
this will probably trigger a database hit, though
I would suggest adding a new column to the database "HasImage" bit that you set when an image is uploaded or deleted
don't know the actual answer to your Q, but in case you don't get an answer: how about doing the change yourself in the DB. (that is of course if you have control over the DB design).
and put the HasImage (or HasContent) column straight in the table, with a default "false" and when you add the image you make it "true" and than you can consult that column to see if you have an image or not.

Categories

Resources