Adding related records in LINQ - vb.net

Processing an XML file with LINQ to add records into a table in a SQL Server database via a data context. As we are adding records we need to assign the parents in various other tables. Most of the time we can find the existing parent and use it but sometimes we will need to create a new parent.
Thought we could do this like this:
Dim defaultPub As publication
defaultPub = New publication With {.name = e..Value}
Dim pub = _Data.publications.Where(Function(s) s.name = e..Value).DefaultIfEmpty(defaultPub).SingleOrDefault
.publication = pub
So we are trying to find a publication that matches e..Value from our XML, but if we can't find one then we use 'defaultPub'. If it does exist though we don't want to add it again. So maybe this approach is flawed anyway even if it did work...
Anyway it's not currently working, we get this error:
Unsupported overload used for query operator 'DefaultIfEmpty'.
The overload requires a publication and is getting one (I've checked the TypeNames in quickwatch), don't know what is going on here.
What we are really looking for is something like the find_or_create from activerecord for Ruby that LINQ seems to be a copied from.
Thanks in advance,
Dave.

OK, I can just do it like this:
Dim pub = _Data.publications.Where(Function(s) s.name = e.<source>.Value).SingleOrDefault
Dim newPub As publication
If Not IsNothing(pub) AndAlso pub.name <> "" Then
.publication = pub
Else
newPub = New publication With {.name = e.<source>.Value}
.publication = newPub
End If
Thanks Val!

Related

The ""sort"" value has to be formed as this example: ""field_ASC""

I'm developing an integration between a ERP and a Prestashop store, I'm now trying to use a product filter to check if a product already exists, so i can either add a new product or update an existing one.
When I try to get the filter result i got a XML response with the following error message
<![CDATA[The ""sort"" value has to be formed as this example: ""field_ASC"" or '[field_1_DESC,field_2_ASC,field_3_ASC,...]' (""field"" has to be an available field)]]>
I didn't understand what that really means, I have searched around a while and coudn't find any specific problem like this and all the examples I've found are the same.
Here is my code, it is in VB and I'm using Bukimedia Prestasharp
Dim oProdFac = New ProductFactory(oParameters.UrlAPI, oParameters.TokenAPI, oParameters.KeyAPI)
Dim objFiltro = New Dictionary(Of String, String)
Dim listFilterProducts As New List(Of Bukimedia.PrestaSharp.Entities.product)
objFiltro.Add("reference", "My_Product_Id")
listFilterProducts = oProdFac.GetByFilter(objFiltro, "null", "null")
What am I doing wrong?
Allright, the answer is SO SIMPLE that i felt ashamed.
I was sending incorrect parameters in the GetByFilter function.
I've changed this
listFilterProducts = oProdFac.GetByFilter(objFiltro, "null", "null")
to this
listFilterProducts = oProdFac.GetByFilter(objFiltro, "reference_ASC", "null")
Just like the xml message said. It solved the problem.

How to use LINQ to Entities to make a left join using a static value

I've got a few tables, Deployment, Deployment_Report and Workflow. In the event that the deployment is being reviewed they join together so you can see all details in the report. If a revision is going out, the new workflow doesn't exist yet new workflow is going into place so I'd like the values to return null as the revision doesn't exist yet.
Complications aside, this is a sample of the SQL that I'd like to have run:
DECLARE #WorkflowID int
SET #WorkflowID = 399 -- Set to -1 if new
SELECT *
FROM Deployment d
LEFT JOIN Deployment_Report r
ON d.FSJ_Deployment_ID = r.FSJ_Deployment_ID
AND r.Workflow_ID = #WorkflowID
WHERE d.FSJ_Deployment_ID = 339
The above in SQL works great and returns the full record if viewing an active workflow, or the left side of the record with empty fields for revision details which haven't been supplied in the event that a new report is being generated.
Using various samples around S.O. I've produced some Entity to SQL based on a few multiple on statements but I feel like I'm missing something fundamental to make this work:
int Workflow_ID = 399 // or -1 if new, just like the above example
from d in context.Deployments
join r in context.Deployment_Reports.DefaultIfEmpty()
on
new { d.Deployment_ID, Workflow_ID }
equals
new { r.Deployment_ID, r.Workflow_ID }
where d.FSJ_Deployment_ID == fsj_deployment_id
select new
{
...
}
Is the SQL query above possible to create using LINQ to Entities without employing Entity SQL? This is the first time I've needed to create such a join since it's very confusing to look at but in the report it's the only way to do it right since it should only return one record at all times.
The workflow ID is a value passed in to the call to retrieve the data source so in the outgoing query it would be considered a static value (for lack of better terminology on my part)
First of all don't kill yourself on learning the intricacies of EF as there are a LOT of things to learn about it. Unfortunately our deadlines don't like the learning curve!
Here's examples to learn over time:
http://msdn.microsoft.com/en-us/library/bb397895.aspx
In the mean time I've found this very nice workaround using EF for this kind of thing:
var query = "SELECT * Deployment d JOIN Deployment_Report r d.FSJ_Deployment_ID = r.Workflow_ID = #WorkflowID d.FSJ_Deployment_ID = 339"
var parm = new SqlParameter(parameterName="WorkFlowID" value = myvalue);
using (var db = new MyEntities()){
db.Database.SqlQuery<MyReturnType>(query, parm.ToArray());
}
All you have to do is create a model for what you want SQL to return and it will fill in all the values you want. The values you are after are all the fields that are returned by the "Select *"...
There's even a really cool way to get EF to help you. First find the table with the most fields, and get EF to generated the model for you. Then you can write another class that inherits from that class adding in the other fields you want. SQL is able to find all fields added regardless of class hierarchy. It makes your job simple.
Warning, make sure your filed names in the class are exactly the same (case sensitive) as those in the database. The goal is to make a super class model that contains all the fields of all the join activity. SQL just knows how to put them into that resultant class giving you strong typing ability and even more important use-ability with LINQ
You can even use dataannotations in the Super Class Model for displaying other names you prefer to the User, this is a super nice way to keep the table field names but show the user something more user friendly.

Linq query results to List

I have the following....
Dim ProjectToDelete As Project2Host = dc.Project2Hosts.Single(Function(p) p.Host = tb_Host.Text)
dc.Projects2Hosts.DeleteOnSubmit(ProjectToDelete)
dc.SubmitChanges
This worked fine as there was only ever one Project associated to a Host.
The rules have now changed and there can be more than one Project per Host. I now need to create a List of ProjectsToDelete and populate that with a Linq query and then do a DeleteAllOnSubmit(ProjectsToDelete).
How do I create the list? I've beem trying .SelectMany but with no joy.
Any help appreciated.
How about:
For Each item in dc.Project2Hosts.Where(Function(p) p.Host = tb_Host.Text)
dc.Projects2Hosts.DeleteOnSubmit(item)
Next
dc.SubmitChanges()
OR:
dc.Project2Hosts.DeleteAllOnSubmit(dc.Project2Hosts.Where(Function(p) p.Host = tb_Host.Text))
dc.SubmitChanges()

Entity Framework 0 to 1 relationship - Object Reference Not Set

Having a bit of trouble having not long started with Entity Framework.
I have two tables which have a 0 to 1 relationship. When I select a row from the main table (staff) it's fine unless I select a row that has no joining record in the second table (status). If I do this, then it throws up 'Object reference not set to instance of an object' when it's trying to access the proprty of the second table:
If Not cls.STATUS_DESC.STAFF_INFO Is DBNull.Value Then
lblStatusDescription.Text = cls.STATUS_DESC.STAFF_INFO
End If
The LINQ I use to get the record is:
Dim account As STAFF =
(From a In sa.STAFFs
Where a.STAFF_NO = staffno
Select a).FirstOrDefault
There is no direct reference to the sub table in the statement, however the join is defined in the database diagram which allows me to reference the properties.
I'm certain it's a very basic issue, but like I said I've only just started using it!
I'm not sure if I can interpret your code correctly but I think it has to do with the Lazy Loading feature of Entity Framework. You must explicitly include the reference to load it into memory. You can to this by using the Include() method as below. I assume that STATUS_DESC is the name of the navigation property. Replace it with the actual one if I'm wrong:
Dim account As STAFF =
(From a In sa.STAFFs Where a.STAFF_NO = staffno Select a) _
.Include("STATUS_DESC") _
.FirstOrDefault
Got around it by adding:
If Not cls.STATUS_DESC Is Nothing Then
end if
to the property call, which seems fairly obvious now I think about it. Is this the most efficient way though, I would have thought that EF would have been able to handle a simple left join.

How do I use Linq-to-sql to iterate db records?

I asked on SO a few days ago what was the simplest quickest way to build a wrapper around a recently completed database. I took the advice and used sqlmetal to build linq classes around my database design.
Now I am having two problems. One, I don't know LINQ. And, two, I have been shocked to realize how hard it is to learn. I have a book on LINQ (Linq In Action by Manning) and it has helped some but at the end of the day it is going to take me a couple of weeks to get traction and I need to make some progress on my project today.
So, I am looking for some help getting started.
Click HERE To see my simple database schema.
Click HERE to see the vb class that was generated for the schema.
My needs are simple. I have a console app. The main table is the SupplyModel table. Most of the other tables are child tables of the SupplyModel table.
I want to iterate through each of Supply Model records. I want to grab the data for a supply model and then DoStuff with the data. And I also need to iterate through the child records, for each supply model, for example the NumberedInventories and DoStuff with that as well.
I need help doing this in VB rather than C# if possible. I am not looking for the whole solution...if you can supply a couple of code-snippets to get me on my way that would be great.
Thanks for your help.
EDIT
For the record I have already written the following code...
Dim _dataContext As DataContext = New DataContext(ConnectionStrings("SupplyModelDB").ConnectionString)
Dim SMs As Table(Of Data.SupplyModels) = _dataContext.GetTable(Of Data.SupplyModels)()
Dim query = From sm In SMs Where sm.SupplyModelID = 1 Select sm
This code is working...I have a query object and I can use ObjectDumper to enumerate and dump the data...but I still can't figure it out...because ObjectDumper uses reflection and other language constructs I don't get. It DOES enumerate both the parent and child data just like I want (when level=2).
PLEASE HELP...I'M stuck. Help!
Seth
in C# it would be:
var result = from s in _dataContent.SupplyModels where s.SupplyModelID==1 select s;
foreach(SupplyModel item in result)
{
// do stuff
foreach(SupplyModelChild child in item.SupplyModelChilds)
{
//do more stuff on the child
}
}
and a VB.NET version (from the Telerik code converter)
Dim result As var = From s In _dataContent.SupplyModels _
Where s.SupplyModelID = 1 _
Select s
For Each item As SupplyModel In result
' do stuff
'do more stuff on the child
For Each child As SupplyModelChild In item.SupplyModelChilds
Next
Next