Setting a variable to linq query with no results - vb.net

I have a table that holds links to websites about particular theaters. I want to retrieve the first link for a given theater. My code to set the variable:
Dim link As String = TheaterLinks.Where(Function(x) x.TheaterID = TheaterID).FirstOrDefault().Link
If there are no results (some theaters won't have any links), then I get:
Object reference not set to an instance of an object.
How do I do this? I tried:
Dim link As String = Links.Where(Function(x) x.TheaterID = TheaterID.DefaultIfEmpty().First().Link
But I can't figure out what to put inside DefaultIfEmpty(). I tried DefaultIfEmpty("") and DefaultIfEmpty(blankstringvariable) but then I get:
Value of type 'String' cannot be converted to type 'TheaterLink'.

The problem is that FirstOrDefault() is allowed to return null, in which case accessing Link property would throw an exception.
If you use VB.NET 14, add question mark for automatic null checking:
Dim link As String = TheaterLinks.Where(Function(x) x.TheaterID = TheaterID).FirstOrDefault()?.Link
(see ?.Link instead of .Link)
Otherwise, do it in two stages: first, get the object using FirstOrDefault, then null-check it manually with an If statement.

For my original example, I just had to add a question mark:
Dim link As String = Links.Where(Function(x) x.ID = id).FirstOrDefault()?.Link
I tried doing this in my model and got a "Nullable object must have a value" error, so I did the two-stage approach:
Dim eventDate = Events.Where(Function(x) x.ID = id).FirstOrDefault()?.EventDate
If eventDate.HasValue Then
'some code
Else
'some code
End If

Related

SQLSTATE[22007]: Invalid datetime forma

I try to save some data that it brings me from my view, which is a table, but I don't know why it throws me that error with the insert.
result of insert
this is my view:
table of view
this is my controller:
$checked_array = $_POST['id_version'];
foreach ($request['id_version'] as $key => $value) {
if (in_array($request['id_version'][$key], $checked_array))
{
$soft_instal = new Software_instalacion;
$soft_instal->id_instalacion = $instalaciones->id;
$soft_instal->id_historial = $historial->id;
$soft_instal->id_usuario = $request->id_usuario;
$soft_instal->id_version = $_POST['id_version'][$key];
$soft_instal->obs_software = $_POST['obs_software'][$key];
$soft_instal->id_tipo_venta = $_POST['id_tipo_venta'][$key];
$soft_instal->save();
}
}
id_tipo_venta seems to be an empty string which is apparently not valid.
You can try debugging what you get in :
var_dump($_POST['id_tipo_venta'][$key]);
die;
Your database field expects to receive an integer. Therefore, using the intval() function can solve your problem.
Indeed, I think your code returns an alphanumeric string.
Therefore, the code below will return 0 in all cases if no version is returned (not set, string or simply null):
$soft_instal->id_tipo_venta = intval($_POST['id_tipo_venta'][$key]);
On the other hand, intval() will always convert to int, so a decimal will be converted, example :
intval("1.1") // returns 1
intval("v1.1") // returns 0
If this is not the desired behavior, maybe you should think about changing your database type.
EDIT :
Of course, you can also set the value as null if you prefer to 0. You must allow nullable values in your database.
id_tipo_venta can not be empty, try with some number or change type column to varchar in the database

adding/removing objects to a list in vb.net

i have a list of objects called gobletinv that i want to add and remove objects from
right now to add a new object I've just done this
gobletinv(gobletpointer).mainstattype = MST.Text
gobletinv(gobletpointer).mainstatvalue = MSV.Text
gobletinv(gobletpointer).substat1type = ST1.Text
gobletinv(gobletpointer).substat1value = SV1.Text
gobletinv(gobletpointer).substat2type = ST2.Text
gobletinv(gobletpointer).substat2value = SV2.Text
gobletinv(gobletpointer).substat3type = ST3.Text
gobletinv(gobletpointer).substat3value = SV3.Text
gobletinv(gobletpointer).substat4type = ST4.Text
gobletinv(gobletpointer).substat4value = SV4.Text
gobletpointer += 1
i currently have no idea how i would remove an object from this list
Let's assume that the type your collection holds is called Stat. Let's also assume that gobletpointer is an Integer with an initial value of 0.
Each line where you are referencing the collection, you start it off with:
gobletinv(gobletpointer)
What this does is get the item from the collection at a given index.
So right now when you set the various property values to their respective TextBox value, you are overwriting the existing item in the collection.
If you wanted to add a new item to the collection, you would use the Add method (documentation). For example:
gobletinv.Add(New Stat() With {
mainstattype = MST.Text,
mainstatvalue = MSV.Text
substat1type = ST1.Text,
substat1value = SV1.Text,
substat2type = ST2.Text,
substat2value = SV2.Text,
substat3type = ST3.Text,
substat3value = SV3.Text,
substat4type = ST4.Text,
substat4value = SV4.Text
})
Now if you wanted to remove the object from the collection, it depends on how you want to remove it. But here is an example of leveraging the RemoveAt method (documentation) to remove the first record from the collection:
gobletinv.RemoveAt(0)
Update: This is a fiddle demonstrating how to add/remove items to the collection. https://dotnetfiddle.net/c0W6yS

Calling a function within Entity Framework Select

A property from my object (oJobs) is as follows:
Private _brandlist As List(Of DAL.Brand)
Public Property PostBrandList() As List(Of DAL.Brand)
Get
Return _brandlist
End Get
Set(ByVal value As List(Of DAL.Brand))
_brandlist = value
End Set
End Property
In the database, the brand list is stored as a string separated by comma e.g. the column 'brands' can be a string '3,45,2' where each number represents an id of a brand stored in another table.
my select query is as below:
Dim jobposts As List(Of oJobs) = From j In db.JobPostings
Select New oJobs With { 'hiding all others for code brevity
.PostBrandList = 'problem is here'
}
Since j.BrandList will return a string, I will need to split that string and for each number, run another query to finally return and assign a List(Of DAL.Brand) into .PostBrandList
For those who might ask "what have you tried?",
I have run the query, then did a for each to add the list of brands later - succeeded but not optimal
Coded a function that takes the list as a parameter and returns a separate list of objects - very silly.
Also, I am not allowed to normalize the DB :(
Not tested and might need some tweaking but heres one idea. you will also need to change your property to an IEnumerable rather than List. Because the second linq query is embedded within the first, I believe it should execute it all as one query, but you should check it to make sure.
Dim jobposts As List(Of oJobs) = From j In db.JobPostings
Select New oJobs With { 'hiding all others for code brevity
.PostBrandList = From b In db.Brands Where j.Brands = b.ID Or j.Brands.StartsWith(b.ID & ",") Or j.Brands.EndsWith("," & b.ID) Or j.Brands.Contains("," & b.ID & ",") Select b
}
In c# you can use
.Select(x=>new {x.BrandList})
.ToList() //Materialize first before calling function
.Select(x=> new oJobs{
PostBrandList =
db.Brands.Where(z=>
x.BrandList
.Split(',')
.Select(y=>int.Parse(y.Trim()))
.Contains(z.Id))
.ToList()
});
Note that you must materialize entity first before calling String.Split
I don't know how to translate that to VB.NET.
Of course it will cause SELECT n+1 problem because you can't use join.
If you can't normalize table, my other suggestion is to create indexed view (sql server), so you can use join and improve performance.
Indexed view https://msdn.microsoft.com/en-us/library/ms191432.aspx
You could try it with the Let statement:
Dim jobposts As List(Of oJobs) = From j In db.JobPostings
/* li in the LINQ Statement represents one string value from the BrandList list */
Let postBrandElemsList = j.BrandList.Split(',').Select(Function(li) New DAL.Brand With { ... /* Field initializatione of the Class DAL.Brand here */ }
Select New oJobs With
{
.PostBrandList = postBrandElemsList
}
I'm sorry for the probably bad VB.NET syntax, you should check this when implementing it in your code.
Maybe you would just want to use the Split function on the column brands into an array and iterate through the result, using the Find function to retrieve the brand objects?

Unable to compare 2 objects of same type

Dim obj As New CMS_Page
Dim comparisonObj As New CMS_Page
The assignment
obj = db.CMS_Pages.First(Function(s) s.PageID = pageID)
comparisonObj = db.CMS_Pages.First(Function(s) s.PageID = pageID)
Somwhere in the middle of my code
obj.property = sometextfield.text 'Apparently this also changes the comparisonObj
Basically what I'm doing in the end would be
if (obj.property = comparisonObj.property) then
//...
end if
Why can't i change obj.Property without it changing the same property in comparisonObj.Property?
You are probably setting "comparisonObj = obj". You probably want "comparisonObj = obj.Clone()". You will have to implement the "Clone" method yourself.
If CMS_Page is not under your control, then you can create an extension method to clone it.
obj = db.CMS_Pages.First(Function(s) s.PageID = pageID)
comparisonObj = db.CMS_Pages.First(Function(s) s.PageID = pageID)
These two lines result in two references to the same object. Hence, when you do this: obj.property = sometextfield.text then comparisonObj will also reflect that change.
What is the result of
obj.ReferenceEquals(comparisonObj)
if that is True then obj is comparisonObj. You can do the same check more concisely like this,
obj Is comparisonObj
If you have a VB background, both variables are references to the same object.
If you have a C background, both variables are pointers to the same object.
essentially, the variable holds an integer value that addresses the object in memory.

How do I return the column names of a LINQ entity

I am using LINQ to SQL queries to return data in my application. However I find it is now needful for me to return the column Names. Try as I might I have been completely unable to find out how to do this on the internet.
So if my LINQ entity table has the properties (Last_Name, First_name, Middle_Name) I need to return:
Last_name
First_Name
Middle_name
rather than the usual
Smith
John
Joe
You could certainly do it with some LINQ-To-Xml directly against the ".edmx" file or the embedded model resources in the compiled assembly.
The below query gets the field (not column) names. If you need the columns then just change the query to suit.
var edmxNS = XNamespace.Get(#"http://schemas.microsoft.com/ado/2007/06/edmx");
var schemaNS = XNamespace.Get(#"http://schemas.microsoft.com/ado/2006/04/edm");
var xd = XDocument.Load(#"{path}\Model.edmx");
var fields =
from e in xd
.Elements(edmxNS + "Edmx")
.Elements(edmxNS + "Runtime")
.Elements(edmxNS + "ConceptualModels")
.Elements(schemaNS + "Schema")
.Elements(schemaNS + "EntityType")
from p in e
.Elements(schemaNS + "Property")
select new
{
Entity = e.Attribute("Name").Value,
Member = p.Attribute("Name").Value,
Type = p.Attribute("Type").Value,
Nullable = bool.Parse(p.Attribute("Nullable").Value),
};
Lets assume you're talking about the Contact Table in the assembly named YourAssembly in a Context called MyDataContext
Using Reflection against a Table
You can use reflection to get the properties like you would any type
var properties = from property in
Type.GetType("YourAssembly.Contact").GetProperties()
select property.Name
;
foreach (var property in properties)
Console.WriteLine(property);
As shaunmartin notes this will return all properties not just Column Mapped ones. It should also be noted that this will return Public properties only. You'd need to include a BindingFlags value for the bindingAttr Parameter of GetProperties to get non-public properties
Using the Meta Model
You can use the Meta Model System.Data.Linq.Mapping to get the fields ( I added IsPersistant to only get the Column Mapped properties)
AttributeMappingSource mappping = new System.Data.Linq.Mapping.AttributeMappingSource();
var model = mappping.GetModel(typeof (MyDataContext));
var table = model.GetTable(typeof (Contact));
var qFields= from fields in table.RowType.DataMembers
where fields.IsPersistent == true
select fields;
foreach (var field in qFields)
Console.WriteLine(field.Name);
Using Reflection from a query result
If on the other hand you wanted it from a query result you can still use reflection.
MyDataContextdc = new MyDataContext();
Table<Contact> contacts = dc.GetTable<Contact>();
var q = from c in contacts
select new
{
c.FirstName,
c.LastName
};
var columns = q.First();
var properties = (from property in columns.GetType().GetProperties()
select property.Name).ToList();
I stumbled upon this answer to solve my own problem and used Conrad Frix 's answer. The question specified VB.NET though and that is what I program in. Here are Conrad's answers in VB.NET (they may not be a perfect translation, but they work):
Example 1
Dim PropertyNames1 = From Prprt In Type.GetType("LocalDB.tlbMeter").GetProperties()
Select Prprt.Name
Example 2
Dim LocalDB2 As New LocalDBDataContext
Dim bsmappping As New System.Data.Linq.Mapping.AttributeMappingSource()
Dim bsmodel = bsmappping.GetModel(LocalDB2.GetType())
Dim bstable = bsmodel.GetTable(LocalDB.tblMeters.GetType())
Dim PropertyNames2 As IQueryable(Of String) = From fields In bstable.RowType.DataMembers
Where fields.IsPersistent = True
Select fields.Member.Name 'IsPersistant to only get the Column Mapped properties
Example 3
Dim LocalDB3 As New LocalDBDataContext
Dim qMeters = From mtr In LocalDB3.tblMeters
Select mtr
Dim FirstResult As tblMeter = qMeters.First()
Dim PropertyNames3 As List(Of String) = From FN In FirstResult.GetType().GetProperties()
Select FN.Name.ToList()
To display the results:
For Each FieldName In PropertyNames1
Console.WriteLine(FieldName)
Next
For Each FieldName In PropertyNames2
Console.WriteLine(FieldName)
Next
For Each FieldName In PropertyNames3
Console.WriteLine(FieldName)
Next
Please also read Conrad's answer for notes on each method!