Entity Framework 6 Navigation Property Collections - vb.net

I am developing a database first application in VB.NET using entity framework version 6.1.1 and sql server 2008. I have some pure join tables that link many to many relationships between two tables. My entities are untracked.
Here are basic examples of the classes (generated from the EF tt files) that I am using:
Public Class Part
Public Property id As Long
Public Property name As String
Public Overridable Property CarModels As ICollection(Of CarModel) = New HashSet(Of CarModel)
End Class
Public Class CarModel
Public Property id As Long
Public Property name As String
Public Overridable Property Parts As ICollection(Of Part) = New HashSet(Of Part)
End Class
When I am updating fields for an entity, I set the values, and then include code like this:
obj.Name = "New Name"
context.Entry(obj).State = EntityState.Modified
context.SaveChanges
That will save the Name value to the database as I expect. My problem is trying to add a new CarModel to an existing part, or remove an existing CarModel from a part. I've tried several things but have not found a solution. Here's an example of my code:
Dim p As Part = context.Parts.where(Function(it) it.id.equals(1)).first 'Part I am working with
Dim c As CarModel = context.CarModels.where(Function(it) it.id.equals(1)).first 'Car Model I want to associate to the part
p.CarModels.Add(c) 'Add the Car Model to the part collection
context.Entry(p).State = EntityState.Modified
context.SaveChanges
No error is thrown. When I am debugging the CarModel is Added to the Part.CarModel collection. However, the changes are not committed to the database. If I add a NEW Part and use similar code it work, but I cannot add or remove from an existing collection and get it to commit to the database.

I haven't used VB in 6 years, so this might not be exactly right, but this will give you a general idea on how it works.
Dim p As Part = context.Parts.where(Function(it) it.id.equals(1)).first 'Part I am working with
Dim c As CarModel = context.CarModels.where(Function(it) it.id.equals(1)).first 'Car Model I want to associate to the part
Dim newPart As Part = New Part()
newPart.id = p.id
newPart.name = p.name
newPart.CarModels = c
context.Add(p)
context.SaveChanges()

I took a look at the context itself, and this line was in the constructor of the context:
Configuration.AutoDetectChangesEnabled = False
That is what was causing my particular problem. I read somewhere (after I found that line) that it is recommended to not turn set AutoDetectChangesEnabled to false unless there is a very long running process, and in that case to set it back to true after the process had completed. Removing that line from the context constructor solved my problem.

Related

Pass user input to new instance of class

Alright, I have looked through 20 pages on here and can't find what I'm looking for... I've seen it in C# and other languages.. but not Visual Basic..
Say I have a class:
Public Class Cars
Private doors as integer
Private Liters as double
Private otherStuff as string
' more code'
end class
Say I also have a Form.. an inputForm we'll call it that has numerous textboxes for users to input these characteristics. The first textbox is labeled nameTextBox. Is there any way to assign the string value of that textbox as a new car?
something to the likes of..
dim nameTextBox.value as new car
??
The fields in your class are private, so they arent of much use - no other code will be able to see those values.
Public Class Car
Public Property Make As String
Public Property Model As String
Public Property Year As Integer
' something the class may use/need but doesnt expose
Private DealerCost As Decimal
' the constructor - called when you create a NEW car
Public Sub New(mk As String, md As String)
Make = mk
Model = md
End Sub
...
End Class
By specifying only a constructor which takes params, I am saying that you cannot create a new car without specifying those properties. If the constructor takes no params, then you can create an "empty" car object and set each property individually. You can do both - called overloading - so you can create a car with or without the Make and Model at the outset.
As Public Properties, other code can examine them to see what kind of car this is.
Dim myCar = New Car("Toyata", "Corolla")
myCar.Year = 2013
myCar.Color = Color.Blue
The text used of course can come from user input:
Dim myCar = New Car(tbMake.Text, tbModel.Text)
Dim yr As Int32
If Integer.TryParse(tbYear.Text, yr) Then
myCar.Year = yr
Else
' ToDo: scold user
End If

Can I concatenate 2 columns in my query when using SELECT in ado.net?

VS2013, vb.net
For this class (only the relevant properties are displayed):
Public Class UserPost
Public Property Title As String
Public Property Topic As String
Public Property Type As ChannelType 'ChannelType is an Enum
End Class
The following query returns a simple list(of string) holding the titles of the UserPosts with Topic = topic:
Dim rtnList As New List(Of String)
rtnList = db.UserPost.Where(Function(x) x.Topic = topic).Select(Function(x) x.Anchor.Title).ToList()
But it would be useful to also report the ChannelType as a prefix to the Title. I could create a more complicated object to receive 2 columns and combine them later, but I wondered if there is a way to concatenate the columns in the query so that the rtnList receives the result of:
ChannelType.tostring() & Title
without having to code that afterword.
Of course there is. You just do pretty much exactly what you said. Instead of returning x.Anchor.Title you return x.Anchor.ChannelType.ToString() & x.Anchor.Title.

VB.NET Add row data to public class / public field of type List(Of T) Object reference not set to an instance of an object

I am trying to add rows to a public class that has public fields and am getting an error: Object reference not set to an instance of an object
Public Class EmailRecipient
EmailAddress As String = ""
FullName As String = ""
End Class
Public Class EmailDetails
Public FromEmail As String = ""
Public ToEmails As List(Of Emails) = nothing
End Class
Public Sub SetEmailDetails
'Populate EmailRecipient Class
Dim er As New EmailRecipient
er.EmailAddress = "rodney#norespect.com"
er.FullName = "Rodney Dangerfield"
'Populate EmailDetails Class
Dim ed As New EmailDetails
ed.FromEmail = "sender#danger.com" 'This works fine
ed.ToEmails.Add(er) 'Here error happens
End Sub
I'm guessing I need to create an instance of the EmailRecipient class before I can add an item to it.
Not sure how to do that with a Public Field in a Public Class??
It's been a rough day. I got up this morning, put a shirt on and a button fell off. I picked up my briefcase and the handle came off. I'm afraid to go to the bathroom.
Thanks for the help :-)
I'm guessing I need to create an instance of the EmailRecipient class
You already have an instance of the EmailRecipient class. That's your er variable. You actually have two errors here. First, you explicitly set ToEmails to Nothing:
Public ToEmails As List(Of Emails) = nothing
This means that your ToEmails variable is a Null Reference. It doesn't have an actual object yet.
The second issue is that you shouldn't get that excpetion, because this shouldn't even compile. You define ToEmails as a List(Of Emails), but tried to add an object of type "EmailRecepient" to it. That should be a compiler error. If it's not, you need to turn Option Strict or Option Infer back on.
So what you really need is an instance of a the List(Of EmailRecipient) type. Fix the bad line of code like this:
Public ToEmails As New List(Of EmailRecipient)

Linq-to-SQL Database Update Fails

Very simple update. It simply fails, no error, no change gets made to the database.
Dim db As New BarClassesDataContext
Dim foo = (From a In db.articles Where a.id = 14 Select a).Single
Response.Write("<h3>" & foo.title & "</h3>")
foo.title = "This is my new, updated title for article ID #14"
db.SubmitChanges()
Here is the relevent portion of my article class. Also, this is a web form so I have no console. Is there another way to view the T-SQL output?
<Table(Name:="dbo.article")> _
Partial Public Class article
Private _id As Integer
Private _issueid As Integer
Private _dateadded As Date
Private _title As String
Private _titlelink As String
Private _description As String
Private _image As String
Private _imagelink As String
Private _type As Integer
Public Sub New()
MyBase.New
End Sub
<Column(Storage:="_id", AutoSync:=AutoSync.Always, DbType:="Int NOT NULL IDENTITY", IsDbGenerated:=true)> _
Public Property id() As Integer
Get
Return Me._id
End Get
Set
If ((Me._id = value) _
= false) Then
Me._id = value
End If
End Set
End Property
If you have any invalid fields definitions you may have an issue where 0=1 is added to the WHERE clause. Check that all of your non-nullable fields are set. (I fought with this for about two hours one night while watching the SQL profiler add the extra 0=1 for no reason.)
Post on Social MSDN
Another post about this issue
(If I can find the issue on connect.microsoft.com I will post it as well)
At first glance, I would say one (but not the main) problem is '='. I think it needs to be:
Dim foo = (From a In db.articles Where a.id == 14 Select a).Single
The main problem I see is the lack of an UpdatetOnSubmit() statement. How does L2S know you want to do an Update?
Try
db.Articles.UpdateOnSubmit(foo);
db.SubmitChanges();
or something close to this.
My suspicion is that you don't have a properly defined primary key in your database and/or in your System.Data.Linq.Mapping attributes. Show us the Article class - that will likely be where your problem is. Make sure you have an IDENTITY field in your database, and be sure your linqed up class has got IsPrimaryKey:=True and IsDbGenerated:=True in the <Column> attribute.
Also, it would be wise to set the .Log property on your DataContext to see what SQL is being executed. I like the debug window logger technique, which I have mentioned before here: DataContext SubmitChanges in LINQ

Populate an Array of Object Based on DataReader Data

I am not sure how to phrase my question properly but I want to achieve something like this.
I have a class named Products
public class Products
private ID as Integer
private Name as String
Public Property ProductID()
Get
Return ID
End Get
Set(ByVal value)
ID = value
End Set
End Property
In one of my code behind pages, I am retrieving data from an SQL Command and placing the same into a datareader object.
How would I be able to declare the class so that each row in my datareader would actually be an instance of the said class?
Like for example:
Dim myProduct() as New Product
Dim intCnt as Integer
While datareaderData.read()
intCnt += 1
myProduct(intCnt) = new Product
myProduct(intCnt).ID = datareaderData("ID")
myProduct(intCnt).Name = datareaderData("Name")
End While
When I do the same, I am getting an error "Object Reference Not Set to an Instance of an Object.
I am quite stumped on this one. Any tips greatly appreciated. Thanks.
You should use an Arraylist or -better- a generic List(of Product).
Besides i would strongly recommend to set Option Strict On in your project's Compiler Settings.
Dim products As New List(Of Product)
While datareaderData.read()
Dim nextProduct As New Product
nextProduct.ProductID = CType(datareaderData("ID"), System.Int32)
nextProduct.Name = datareaderData("Name").ToString
products.add(nextProduct)
End While