linq join query - linq-to-sql

I am trying to do a join in linq , however for some reason I can't access the primary key of a table. It's the 'h.ProjectId' that doesn't seem to be accepted.
The following error is given
CW1.SearchWebService.Bid does not contain a definition for 'ProjectId' and no extention method 'ProjectId' accepting a first argument of type 'CW1SearchWebService.Bid'
Code:
var allProjects = ctxt.Project.ToList() ;
var allBids = ctxt.Bid.ToArray();// return all bids
var projects = (from project in allProjects join h in allBids
on
project.ProjectId equals h.ProjectId)

The problem, according to your error message, is the h.ProjectId. The error message says that the "Bid" class (CW1.SearchWebService.Bid) does not contain a member ProjectId, so the statement fails.
You should revisit your classes, and see what the proper join option would be. If you are sure that field should be in the table, you may need to regenerate your schema.

Perhaps the LINQ schema was generated before the field was added to the database. If it was generated with sqlmetal, try running sqlmetal over your database again. Otherwise just add it using the designer.

Related

Why is 'a1' added at the end of sql query in json config file meant for data extraction from NetSuite? Not adding that can cause error?

I am working on extracting data from Netsuite into Databricks File Storage. I am doing it by scripting a json and executing it through Azure Data Factory . This JSON has a part where you specify the query to use when extracting from Netsuite. It is called "incrementalSql". I see quite often that here, an 'a1' is added at the end.
"incrementalParams": {
"checkpointTablePath": "dbfs:/mnt/data/governed/l1/audit/log/checkpoint_log/",
"extractId": "NETSUITE_CURRENCY_EXCHANGE_RATE",
"incrementalSql": "(select b.NAME as BASE_CURRENCY_CD, c.NAME as CURRENCY_CD, a.EXCHANGE_RATE, a.DATE_EFFECTIVE from Administrator.CURRENCY_EXCHANGE_RATES a left join Administrator.CURRENCIES b on a.BASE_CURRENCY_ID = b.CURRENCY_ID left join Administrator.CURRENCIES c on a.CURRENCY_ID = c.CURRENCY_ID) a1",
"maxCheckPoint1": "(select to_char(max(DATE_EFFECTIVE), 'DD-MM-YYYY HH24:MI:SS') from Administrator.CURRENCY_EXCHANGE_RATES where DATE_EFFECTIVE > to_date('%%{CHECKPOINT_VALUE_1}', 'YYYY-MM-DD HH24:MI:SS'))"
}
Notice the a1 at the end of the incrementalSql field.
Does anyone know why it is added? Is it only added in case of NetSuite or Oracle only? Not adding it can cause error?
As stated in comment by #MT0, Assumption is true. a1 at the end of the incrementalSql field is an alias to refer to sub-query which is dynamically embedded.
Not adding aliase will cause an error. Subquery is kind of derived table. So to refer to that alias is necessary.
If alias is not added you may get error like Error - Alias Required to Avoid Duplicate Columns

Why does Salesforce prevent me from creating a Push Topic with a query that contains relationships?

When I execute this code in the developer console
PushTopic pushTopic = new PushTopic();
pushTopic.ApiVersion = 23.0;
pushTopic.Name = 'Test';
pushTopic.Description = 'test';
pushtopic.Query = 'SELECT Id, Account.Name FROM Case';
insert pushTopic;
System.debug('Created new PushTopic: '+ pushTopic.Id);
I receive this message:
FATAL ERROR System.DmlException: Insert failed. First exception on row
0; first error: INVALID_FIELD, relationships are not supported:
[QUERY]
The same query runs fine on the Query Editor, but when I assign it to a Push Topic I get the INVALID_FIELD exception.
If the bottom line is what the exception message says, that relationships are just not supported by Push Topic objects, how do I create a Push Topic object that will return the data I'm looking for?
Why
Salesforce prevents this because it will require them to join tables, joins in salesforces database are expensive due to the multi-tenancy. Usually when they add a new feature they will not support joins as it requires more optimization of the feature.
Push Topics are still quite new to the system and need to be real time, anything that would slow them down I'd say needs to be trimmed.
I'd suggest you look more closely at your requirement and see if there is something else that will work for you.
Workaround
A potential workaround is to add a Formula field to the Case object with the data you need and include that in the query instead. This may not work as it will still require a join to work.
A final option may be to use a workflow rule or trigger to update the account name to a custom field on the Case object this way the data is local so doesn't require a join...
PushTopics support a very small subset of SOQL queries, see more here:
https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/unsupported_soql_statements.htm
However this should work:
PushTopic casePushTopic = new PushTopic();
pushTopic.ApiVersion = 23.0;
pushTopic.Name = 'CaseTopic';
pushTopic.Description = 'test';
pushtopic.Query = 'SELECT Id, Account.Id FROM Case';
insert pushTopic;
PushTopic accountPushTopic = new PushTopic();
pushTopic.ApiVersion = 23.0;
pushTopic.Name = 'AccountTopic';
pushTopic.Description = 'test';
pushtopic.Query = 'SELECT Id, Name FROM Account';
insert pushTopic;
It really depends on your use case though, if it is for replicating into RDBMS this should be enough, you can use a join to get the full data.

Two LINQ data contexts issue

I'm getting this error when using LINQ2SQL:
The query contains references to items defined on a different data context.
Here's the code:
var instances = (from i in context.List
join j in context.CatsList on i.ListID equals j.ListID
join c in context.Cats on j.CatID equals c.CatID
where c.SID == Current.SID
orderby i.Title
select i).Distinct();
The problem, as far as I can ascertain, is that the Current object is actually a LINQ2SQL object returned from a property executing a different LINQ statement.
So, therefore, LINQ2SQL doesn't like executing a query on the database where the query has to be built from one LINQ statement including another statement's result.
My problem with that is that (I'll try to summarise the issue here) the Current object is retrieved using the same context as the query above and ultimately the Current.SID should simply resolve to an int, so what is the compiler's problem with executing it?
In short, why is it not possible to execute a LINQ query using a previous query's returned object as an argument?
This is a solution to the issue, rather than a direct answer of your final question, but you can probably get by with:
var sid = Current.SID;
var instances = (from i in context.List
join j in context.CatsList on i.ListID equals j.ListID
join c in context.Cats on j.CatID equals c.CatID
where c.SID == sid
orderby i.Title
select i).Distinct();

Update mapping table in Linq

I have a table Customers with a CustomerId field, and a table of Publications with a PublicationId field. Finally, I have a mapping table CustomersPublications that records which publications a customer can access - it has two fields: CustomerId field PublicationId.
For a given customer, I want to update the CustomersPublications table based on a list of publication ids. I want to remove records in CustomersPublications where the PublicationId is not in the list, and add new records where the PublicationId is in the list but not already in the table.
This would be easy in SQL, but I can't figure out how to do it in Linq.
For the delete part, I tried:
var recordsToDelete = dataContext.CustomersPublications.Where
(
cp => (cp.CustomerId == customerId)
&& ! publicationIds.Contains(cp.PublicationId)
);
dataContext.CustomersPublications.DeleteAllOnSubmit(recordsToDelete);
... but that didn't work. I got an error:
System.NotSupportedException: Method 'Boolean Contains(Int32)' has no supported translation to SQL
So, I tried using Any(), as follows:
var recordsToDelete = dataContext.CustomersPublications.Where
(
cp => (cp.CustomerId == customerId)
&& ! publicationIds.Any(p => p == cp.PublicationId)
);
... and this just gives me another error:
System.NotSupportedException: Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator
Any pointers?
[I have to say, I find Linq baffling (and frustrating) for all but the simplest queries. Better error messages would help!]
Wow. Almost by chance, I discovered that the reason I couldn't use Contains in my first example was that my publicationIds was an IList<int> rather than a an int[]. I changed it, and it worked.
Thanks, compiler message author! :-|

How does linq-to-sql generate sql for collection pseudoqueries?

My understanding is that the LinqToSql pseudolanguage describes a set using a syntax very similar to SQL and this will allow you to efficiently update a property on a collection of objects:
from b in BugsCollection where b.status = 'closed' set b.status = 'open'
This would update the underlying database using just one SQL statement.
Normally an ORM needs to retieve all of the rows as separate objects, update attributes on each of them and save them individually to the database (at least that's my understanding).
So, how does linq-to-sql avoid having to do this when other orms are not able to avoid it?
The syntax shown in your question is incorrect. LINQ is not intended to have side-effects; it is a query language. The proper way to accomplish what you're looking for is
var x = from b in dataContext.BugsCollection where b.status == "closed";
foreach (var y in x)
y.status = "open";
dataContext.SubmitChanges();
This would generate the single SQL statement that you're talking about. The reason it is able to accomplish this is because of deferred execution - the L2S engine doesn't actually talk to the database until it has to - in this case, because SubmitChanges() was called. L2S then sends the generated SQL statement to the database for execution.
Because LINQ to SQL uses Expression Trees to convert your Query Syntax to actual SQL...it then executes the SQL against the database (rather than pulling all of the data, executing against the in-memory data, and then writing the changes back to the database).
For example, the following Query Syntax:
var records = from r in Records
where r.Property == value
select r
Gets translated first to Lamda Syntax:
Records.Where(r => r.Property == value).Select();
And finally to SQL (via Expression Trees):
SELECT Property, Property2, Property3 FROM Record WHERE Property = #value
...granted, the example doesn't update anything...but the process would be the same for an update query as opposed to a simple select.