When using a Linq-to-SQL class, how can I make a simple copy of an entity and save it?
My entity has a guid for a unique ID that gets automatically generated in the SQL Server.
I don't require a "deep clone".
I tried to use some clone methods that are out there but I couldn't figure out how to get everything serialized that needed to be serialized (got stuck on the DataContext not being serializable).
Can I just get an entity, detach it from the DataContext, null out the unique ID and InsertOnSubmit in a new DataContext? If so, how would I do this?
VB.net code preferred but not required.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UPDATE:
Public Shared Function ReIssue(RequestID As Guid) As Guid
Dim req As Request
Dim new_req As Request
Using dc1 As New MBDataContext()
req = (From r In dc1.Requests Where r.ID = RequestID).Single()
End Using
new_req = req
new_req.ID = Guid.Empty
new_req.CreateDate = Nothing
Using dc2 As New MBDataContext()
dc2.Requests.InsertOnSubmit(new_req)
dc2.SubmitChanges()
End Using
End Function
I get an error: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
on this line: dc2.Requests.InsertOnSubmit(new_req)
Nulling out the unique id and then calling InsertOnSubmit is the right way to go. Some things you have to consider though:
What is the type of the id? Is it an int? A Guid? Is it nullable? If it is nullable, make sure to set it to null, if it is an int, then to 0, or a Guid, then to Guid.Empty.
Does the type have a timestamp of some kind? If so, then you have to reset/set it to null as well, depending on the type.
Once you've done that, you can call InsertOnSubmit and then SubmitChanges and the change should take place.
Note, if you are doing this for a large number of records, you are better off writing a stored procedure which will perform the insert into the table using a select from the other table. It will be much faster that way (you won't be loading the data from the database into memory then pushing it back, inserting the records one at a time).
This method seems to have worked perfectly.
Making the final code look like this:
Public Shared Function ReIssue(RequestID As Guid) As Guid
Using dc As New MBDataContext()
Dim req As Request
req = (From r In dc.Requests Where r.ID = RequestID).Single()
Dim new_req As Request = DirectCast(Entity.Copy(req, New Request()), Request)
dc.Requests.InsertOnSubmit(new_req)
dc.SubmitChanges()
req.ActiveRequestParentID = new_req.ID
dc.SubmitChanges()
Return new_req.ID
End Using
End Function
Public NotInheritable Class Entity
Private Sub New()
End Sub
Public Shared Function Copy(source As Object, destination As Object) As Object
Dim sourceProps As System.Reflection.PropertyInfo() = source.[GetType]().GetProperties()
Dim destinationProps As System.Reflection.PropertyInfo() = destination.[GetType]().GetProperties()
For Each sourceProp As System.Reflection.PropertyInfo In sourceProps
Dim column As ColumnAttribute = TryCast(Attribute.GetCustomAttribute(sourceProp, GetType(ColumnAttribute)), ColumnAttribute)
If column IsNot Nothing AndAlso Not column.IsPrimaryKey Then
For Each destinationProp As System.Reflection.PropertyInfo In destinationProps
If sourceProp.Name = destinationProp.Name AndAlso destinationProp.CanWrite Then
destinationProp.SetValue(destination, sourceProp.GetValue(source, Nothing), Nothing)
Exit For
End If
Next
End If
Next
Return destination
End Function
End Class
Related
I need to check a mysql table periodically and if there are any rows found, I need to loop through them and perform some actions.
My SQL string is nice and simple: "SELECT * FROM 'dbcpman_jobs'".
There could be 1 returned row, or there could be 20 returned rows.
For each returned row, I need to assign some of the data to variables...
Dim job_id As String = jobrow.Item("id")
Dim job_jobid As String = jobrow.Item("jobid")
Dim job_status As String = jobrow.Item("status")
Dim job_dbxid As String = jobrow.Item("dbxid")
Then i need to make an API call using the information that's just been split out...
Try
Dim jobapicall As New System.Net.WebClient
jobcheckresult = jobapicall.DownloadString(fulljobapicheckurl)
Catch ex As Exception
Console.WriteLine("Error 'DBX-Err-1' - Error during API call")
End Try
Can someone point me in the right direction to loop through all rows found?
My code currently checks the first item found only, which is not ideal as if that job fails, everything else gets held up.
Thanks
FOR EACH loop was the answer.
something like this...
For Each jobRow As DataRow in dtJobResults.Rows
Try
what I want to do for each returned row.
Catch Ex as exception
messagebox.show("Oh no, something went wrong!")
End Try
Next
I'm having some issues getting my data flow to do what I want it to.
I'm using an OLEDB Source that calls a stored procedure that uses a table variable to show me the data that I need to use.
It looks like this:
ClientID TimeStamp IsStart
pic#psdfj 2013-08-28 14:22:59 1
bsd#fjskk 2013-08-28 14:43:21 1
pic#psdfj 2013-08-28 15:23:01 0
..and so on
I need to create two new columns, one with the timestamp, and the other with the IsStart column addded up. (I'm keeping track of when users are online and capturing the timestamp.)
So I have a currently empty table in my SQL Server db called tblUserUsage, with the columns:
tblUserUsage (example of what the data would look like)
TimeStamp NumberOfUsers
2013-08-28 14:22:59 1
2013-08-28 14:43:21 2
2013-08-28 15:23:01 1
I'm having issues getting my data flow to output to those columns. Right now I have the storedprocedure call connected to a Derived Column, then to a script. But that isn't working as it's not letting me output to the outputbuffer in the script when I create output columns. Just for clarification - the script will be in VB.NET
This is what my script looks like so far. I have a NumberOfUsers column added to the output. But I still have no Output0buffer
Anyone have any ideas?
You only get an OutputBuffer generated in your code for asynchronous transforms (where the SynchronousInputID property of one or more outputs is set to zero). For synchronous transforms, your output columns will be in the InputBuffer.
Compare the MSDN articles Creating an Asynchronous Transformation with the Script Component and Creating a Synchronous Transformation with the Script Component.
Did you add a column to your output like this?
Then your output buffer would look something like this:
TableOutputBuffer.OutputColumnName = "hello";
Take a look a this sample I found here and maybe compare it to your code.. I usually write my scripts in c# so I can tell you if you're missing anything off the top of my head, but it might be worth a try.
Public Class ScriptMain
Inherits UserComponent
Dim connMgr As IDTSConnectionManager100
Dim sqlConn As SqlConnection
Dim sqlReader As SqlDataReader
Public Overrides Sub AcquireConnections(ByVal Transaction As Object)
connMgr = Me.Connections.MyADONETConnection
sqlConn = CType(connMgr.AcquireConnection(Nothing), SqlConnection)
End Sub
Public Overrides Sub PreExecute()
Dim cmd As New SqlCommand("SELECT AddressID, City, StateProvinceID FROM Person.Address", sqlConn)
sqlReader = cmd.ExecuteReader
End Sub
Public Overrides Sub CreateNewOutputRows()
Do While sqlReader.Read
With MyAddressOutputBuffer
.AddRow()
.AddressID = sqlReader.GetInt32(0)
.City = sqlReader.GetString(1)
End With
Loop
End Sub
Public Overrides Sub PostExecute()
sqlReader.Close()
End Sub
Public Overrides Sub ReleaseConnections()
connMgr.ReleaseConnection(sqlConn)
End Sub
End Class
I want to initialize a class with data coming from a MySql db. Some fields can be null:
Dim dr As MySqlDataReader = ...
Dim item As New Item(dr.GetInt16(0), dr.GetString(1), dr.GetString(2))
Suppose the last two fields could be NULL In the db, so that calling GetString on that field causes an exception.
I could certainly write code to test for NULLs before I get each field:
dim field1 as String
if ( dr.IsDbNull(1) )
field1 = Nothing ' or even ""
else
field1 = dr.GetString(1)
But if you have many fields this is an "ifs" nightmare.
To this purpose I rewrote the IIf VB function to make it more typed, thus to avoid casts:
Namespace Util
Public Shared Function IIf(Of T)(ByVal condition As Boolean, ByVal iftrue As T, ByVal iffalse As T) As T
If condition Then Return iftrue Else Return iffalse
End Function
So that I could write something like:
Dim item As New Item(
dr.GetInt16(0),
Util.IIf(dr.IsDbNull(1), "", dr.GetString(1),
Util.IIf(dr.IsDbNull(2), "", dr.GetString(2))
The typed IIf works well in other cases, but unfortunately it doesn't in this instance, because being it a normal function and not a language keyword, each inpout parameter is evaluated during the call, and when the field is NULL the exception is raised.
Can you think of an elegant if-less solution?
First off, I'd recommend you to use an ORM mapper - there are very few cases nowadays when you have to do manual "mapping".
If this is one of these cases, I'd recommend you to use field names instead of indexes while accessing Data Reader.
And to answer your original question: try extension methods. Sorry for C#, but VB.NET syntax drives me nuts:
public static class DbDataReaderExtensions
{
public static T GetField<T>(this DbDataReader dbDataReader, string fieldName,
T defaultValue)
{
if(dbDataReader.IsDBNull(fieldName))
return defaultValue;
return (T)dbDataReader[fieldName];
}
}
Thanks.
I have looked at many ORMs and I don't like them for one reason or another, so I decided to call plain stored procedures to get data. Can you advice something powerful yet simple?
You're right about to use field names, it is safer, even if a little bit slower.
I had just arrived to the same conclusion with the method, but what I still don't like is the type conversion:
Public Shared Function IfNull(Of T)(ByVal dr As MySqlDataReader, ByVal index As Integer, ByVal _default As T) As T
If dr.IsDBNull(index) Then
Return _default
Else
Return CType(dr.GetValue(index), T)
End If
End Function
I would like to do something more elegant to get the "real" data type from the reader.
I have added a table(ViolationsDataSourceConfig) to the dbml file.
The context name is ViolationsDataContext.
I am trying to write a function that should return employee object but it is throwing errors. Below is the code. Is there any easy way for achieving this. I just want the ViolationsDataSourceConfig.
Public Shared Function GetDataSourceDetails(ByVal ApplicationID As Integer) As ViolationsDataSourceConfig
Dim _db As New ViolationsDataContext
Dim appSource As New ViolationsDataSourceConfig
Dim application As Table(Of ViolationsDataSourceConfig) = _db.GetTable(Of ViolationsDataSourceConfig)()
Try
appSource = From a In application Where a.ApplicationID = ApplicationID And a.Status = 1 _
Select a
Catch ex As Exception
End Try
Return appSource
End Function
It's a little hard without more information regarding your data structures or the errors you're getting, could you provide the error at least?
Also, you say your LINQ statement should "return employee" but you are typing it as "ViolationsDataSourceConfig", how does that work?
My first thought would be the LINQ statement will return an IEnumerable by default so it probably won't be the correct type.
ppSource = (From a In application Where a.ApplicationID = ApplicationID And a.Status = 1 _
Select a).FirstOrDefault()
Might be closer to your goal...
I'm using VBA in an Access application and I would like to have a n-tuple object that contains values of different data types. Then I would like a collection of those objects.
If I was doing this in javascript it would look like:
var myStructure = {
name: "blah"
age: 33
moreStuff : "test"
};
And then I would need a collection of myStructure. How can I best accomplish this in VBA?
You can define your own variable type with code such as:
Public Type T_Person
name as string
dateOfBirth as date
....
email() as string (*)
....
End type
You can then declare a T_person type in your code with:
Dim currentPerson as T_Person
currentPerson.name = myName
currentPerson.dateOfBirth = myDate
currentPerson.email(1) = myFirstEmail
....
(*) I do not remember the details for declaring arrays in such circumstances. You might have to determine array's length when defining the variable. Please check help.
The same result can also be reached by declaring a class module named, for example, "Person". In this class module, you'll be not only able to follow the objet properties (such as name, dateOfBirth, etc), but also object events (initialisation and deletion). You'll be also able to create methods on this object. You code would then look like:
Dim myPerson as Person
set myPerson = New Person
myPerson.name = myName
myPerson.dateOfBirth = myDate
if myPerson.age > 18 then (*)
'the guy is an adult'
myPerson.createAccount
Else
'the guy is not ...'
Endif
(*) Here, age is a calculated proerty of your object, available when dateOfBirth is not null. Please google "VBA class module" to find different examples for the implementation of a class module in VBA.
Now, if you want to manage a collection of similar "objects" (here, Persons), you will have to go through the creation of a class module for your objects collection (a "Persons" class module for example) and make use of the "Collection" objet available in VBA. You will then end with 2 different class modules: Person (will hold each person's detail), and Persons (will hold the collection of Persons). You'll then be able to have code like this:
Public myPersons as Persons 'at the app level, 1 main collection'
myPersons.add .... 'adding a new person to your collection'
myPersons.count ... 'counting number of persons in myPersons'
Please google on "VBA collection object" for examples on Collection management in VBA. Check my code proposal, as this was written on the fly, and without VBA help file.
The "Class" solution is clearly more powerfull, but more complex than the "Type". Go for it if you need it. It is definitely worth the pain!
PS: I am not very happy with my namings here, as this can lead to very confusing code when dealing with the myPersons collection and myPerson instance of o Person object. I'd advise you to find a more obvious one, like "PersonCollection" and "Person", or even "Individual"
You can use Variant multidimensional arrays to store your collections. Variants can store any data type making it very versatile.
Const Name as Integer = 0
Const Age as Integer = 1
Const moreStuff as Integer = 2
Dim myStructure as Variant
Redim myStructure(0 to 2, 0 to n)
myStructure(Name, 0) = "Blah"
myStructure(Age, 0) = 33
myStructure(moreStuff, 0) = "test"
Note: You can only expand the last dimension of a multidimensional array in VBA and preserve the values, so make sure that it is for the dimension you want to scale.
That is the basic data structure and you can develop a class or functions to wrap everything up to suit your needs.
You may also want to look into the Scripting.Dictionary object, though I have read that it is considered unsafe. It is more dynamic than using Type definitions, and unlike Collection, it gives you access to the keys.
Here's a sample:
Public Function TestDictionary()
Dim d As New Scripting.Dictionary 'need reference to Microsoft Scripting Runtime
'Dim d As Object
'Set d = CreateObject("Scripting.Dictionary")
Dim k As Variant
d.Add "Banana", "Yellow"
d.Add "Apple", "Red"
d.Add "Grape", "Green"
For Each k In d.Keys
Debug.Print k; ": "; d.Item(k)
Next k
End Function
The Collection class is an option.
Dim col As New Collection
col.Add("blah", "name")
col.Add(33, "age")
col.Add("test", "moreStuff")
This gives you the most flexibility. However it isn't very efficient and the Collection class has no way to get a list of keys.