How to get cross apply to operate row by row on a view?
It is not possible to fully guarantee evaluating the view per row of the outer query, without using something that introduces a new T-SQL execution scope, for example a non-inline (multi-statement, BEGIN...END) table-valued function. This is pretty much the advice given in response to your previous question How to use merge hints to isolate complex queries in SQL Server.


  At one time I thought there was a claim that cross apply was guaranteed for row by row execution when it calls a UDF


This does not apply to inline table-valued functions, since the definition is expanded into the calling query before optimization begins.



That said, there are some things you can do to strongly encourage the desired outcome.


  The execution plan shows that the execution branch for the view part is ignoring the predicate on ObjectId whereas in the original case it uses them to perform index seeks.


You are expecting the ObjectId values to be evaluated "inside the view" using index seeks for each driving row. This is the correlated nested loops join (apply) style of execution. Note that using the APPLY T-SQL language element does not guarantee that physical execution will use the apply style.

It very much sounds as if SQL Server is choosing to execute with the ObjectId values tested at the Nested Loops Join operator instead. This is a non-correlated, or naive, nested loops join execution pattern.

Most likely, this is caused by the join hints you are using inside the view. Join hints are generally to be avoided, because they greatly restrict optimizer freedom, and not just for the physical type of join. In particular, join hints also force the order of joins for the whole query (just as if you had use a FORCE ORDER hint) and prevent several optimizations related to aggregation placement and strategy, and much else besides.

If you really must have join hints inside a view (something I would strongly suggest you avoid in general), you might find the most reliable way to get the plan shape you desire is:


Create an inline (RETURNS TABLE) function using the view definition (not referencing the view).
Provide @ObjectId as a parameter to the function.
Placing a predicate using the parameter within the function body in a way that makes an index seek more likely.
A FORCESEEK table hint inside the function may be used if truly every use should result in a seek.
Call the new inline function using APPLY.


I generally dislike using specific syntaxes and hints in an attempt to force a certain physical plan shape. You may have more general success by parameterizing queries and guaranteeing the plan shape using a plan guide.