Not sure if this is possible using Breeze, but we have a situation where we need to return an IQueryable from 2 different sources.
Our business logic called from our controller action checks to see if some data is stored within a database, if so an IQueryable is returned from the repository which uses the EFContextProvider.
Otherwise, a List<T> is created containing default data and turned into an IQueryable, which is then returned.
On the client side, our query contains .expand to ensure the navigation properties are populated with the required data.
This works fine when the IQueryable is coming from the EF, but crashes with the following error when creating the List<T>:
{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"An error has occurred.","ExceptionMessage":"'System.Linq.EnumerableQuery<MyObject>' does not contain a definition for 'Include'","ExceptionType":"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException","StackTrace":" at CallSite.Target(Closure , CallSite , Object , String )\r\n at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)\r\n at Breeze.WebApi.QueryHelper.<>c__DisplayClass14.<ApplyExpand>b__11(String expand)\r\n at System.Collections.Generic.List`1.ForEach(Action`1 action)\r\n at Breeze.WebApi.QueryHelper.ApplyExpand(IQueryable queryable, String expandsQueryString)\r\n at Breeze.WebApi.QueryHelper.ApplySelectAndExpand(IQueryable queryable, NameValueCollection map)\r\n at Breeze.WebApi.BreezeQueryableAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.CallOnActionExecuted(HttpActionContext actionContext, HttpResponseMessage response, Exception exception)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<>c__DisplayClass2.<System.Web.Http.Filters.IActionFilter.ExecuteActionFilterAsync>b__0(HttpResponseMessage response)\r\n at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass41`2.<Then>b__40(Task`1 t)\r\n at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)"}
Is this an issue with Breeze (we're using 1.4.2 at present), or is it something I'm doing wrong?
I think your best bet in this case in this case is remove the 'expand' from the client and move it the server (as an Include). Something like this:
[HttpGet]
public IQueryable<Customer> Customers(someCriteria) {
if (... haveData ...) {
return ContextProvider.Context.Customers.Include("Orders");
} else {
return DefaultCustomerList.AsQueryable()
}
}
Related
I've added a WCF Data Service (v5.6) called test.svc to the root of my MVC app but I can't seem to get my routing figured out so that I can access it. Below are the results I get back in the browser when I go to http:/test.svc along with my route code and test.svc file.
route table entry (first route rule I add):
routes.IgnoreRoute("{resource}.svc/{*pathInfo}");
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class test : DataService<MyEntities>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.UseVerboseErrors = true;
}
Error shown in browser:
The server encountered an error processing the request. The exception
message is 'Expression of type
'System.Data.Entity.Core.Objects.ObjectContext' cannot be used for
return type 'System.Data.Objects.ObjectContext''. See server logs for
more details. The exception stack trace is:
at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type
delegateType, Expression& body, ReadOnlyCollection1 parameters) at
System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body,
String name, Boolean tailCall, IEnumerable1 parameters) at
System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body,
Boolean tailCall, IEnumerable1 parameters) at
System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body,
ParameterExpression[] parameters) at
System.Data.Services.Providers.DbContextHelper.CreateDbContextAccessor(Type
type) at
System.Data.Services.Providers.DbContextHelper.GetDbContextAccessor(Type
type) at
System.Data.Services.Providers.DbContextHelper.IsDbContextType(Type
type) at
System.Data.Services.DataService1.CreateMetadataAndQueryProviders(IDataServiceMetadataProvider&
metadataProviderInstance, IDataServiceQueryProvider&
queryProviderInstance, Object& dataSourceInstance, Boolean&
isInternallyCreatedProvider) at
System.Data.Services.DataService1.CreateProvider() at
System.Data.Services.DataService1.EnsureProviderAndConfigForRequest()
at System.Data.Services.DataService1.HandleRequest() at
System.Data.Services.DataService1.ProcessRequestForMessage(Stream
messageBody) at SyncInvokeProcessRequestForMessage(Object , Object[] ,
Object[] ) at
System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object
instance, Object[] inputs, Object[]& outputs) at
System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc&
rpc) at
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc&
rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean
isOperationContextSet)
Looks like WCF DS and EF 6 are incompatible and there's no timeframe for when, or if, they might fix things.
http://social.msdn.microsoft.com/Forums/en-US/4aa92957-22de-4445-aecd-2871982afe28/eta-on-entity-framework-version-6-provider?prof=required
In our backing database, we have a Data field that is of type varbinary(max). Using Breeze we are able to save data in this field, however, when we want to call it back down we are getting errors. In our generated models, the field gets mapped to a byte[]. But when Breeze tries to serialize that into a string it throws up errors.
$id: "1",
$type: "System.Web.Http.HttpError, System.Web.Http",
Message: "An error has occurred.",
ExceptionMessage: "The specified cast from a materialized 'System.String' type to the 'System.Byte[]' type is not valid.",
ExceptionType: "System.InvalidOperationException",
StackTrace: " at System.Data.Common.Internal.Materialization.Shaper.ErrorHandlingValueReader`1.GetValue(DbDataReader reader, Int32 ordinal) at System.Data.Common.Internal.Materialization.Shaper.GetColumnValueWithErrorHandling[TColumn](Int32 ordinal) at lambda_method(Closure , Shaper ) at System.Data.Common.Internal.Materialization.Coordinator`1.ReadNextElement(Shaper shaper) at System.Data.Common.Internal.Materialization.Shaper`1.SimpleEnumerator.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at Breeze.WebApi.ODataActionFilter.OnActionExecuted(HttpActionExecutedContext actionExecutedContext) at System.Web.Http.Filters.ActionFilterAttribute.CallOnActionExecuted(HttpActionContext actionContext, HttpResponseMessage response, Exception exception) at System.Web.Http.Filters.ActionFilterAttribute.<>c__DisplayClass2.<System.Web.Http.Filters.IActionFilter.ExecuteActionFilterAsync>b__0(HttpResponseMessage response) at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass41`2.<Then>b__40(Task`1 t) at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)"
Any help would be great!
I have been unable to reproduce this.
Breeze is able to take a server side byte[] and convert it to a string on the client. You can see an example of this in the breeze 'DocCode' samples whenever an Employee entity is returned. The Employee type has a Photo property that has a server side datatype of 'byte[]' which is returned to the breeze client as a 'string'.
What I think you are running into is a server side materialization issue where you are trying to materialize a binary blob on the database into a string property. This would occur if your model property was typed as a 'String' instead of as a 'byte[]'.
Hope this helps.
I've been using the MVC4 beta and am currently working to upgrade to the recently released RC version.
It appears that model-binding complex request types has changed, but I can't figure out how / what I'm doing wrong.
For example, say I have the following API controller:
public class HomeApiController : ApiController
{
public TestModel Get()
{
return new TestModel
{
Id = int.MaxValue,
Description = "TestDescription",
Time = DateTime.Now
};
}
}
This yields the expected result:
<TestModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/xxxx">
<Description>TestDescription</Description>
<Id>2147483647</Id>
<Time>2012-06-07T10:30:01.459147-04:00</Time>
</TestModel>
Now say I just change the signature, taking in a request type, like this:
public TestModel Get(TestRequestModel request)
{
...
public class TestRequestModel
{
public int? SomeParameter { get; set; }
}
I now get the following error:
<Exception xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/System.Web.Http.Dispatcher">
<ExceptionType>System.InvalidOperationException</ExceptionType>
<Message>
No MediaTypeFormatter is available to read an object of type 'TestRequestModel' from content with media type ''undefined''.
</Message>
<StackTrace>
at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger) at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(HttpParameterBinding parameterBinder) at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext() at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken)
</StackTrace>
</Exception>
I've looked at the source code of where this exception is thrown in the HttpContentExtensions, but it looks like it checks for content headers (which I should have), and if it doesn't have that it tries to get a formatter from the MediaTypeFormatter collection it has for the specific type (which it can't) and then throws.
Anyone else experienced this? Some global registration I'm missing?
I see your original question was answered, but to answer the other one, Model binding has changed somewhat in the RC.
http://weblogs.thinktecture.com/cweyer/2012/06/aspnet-web-api-changes-from-beta-to-rc.html
This link has some details about it. But to sum up the change that appears to be affecting you, Model binding pulls its values from either the body, or the uri of the request. This is true for previous releases as well, but with the release candidate, MVC4 will, by default, look to the body for complex types, and the uri for value types.
So, if you submit a body with your request containing the "SomeParameter" key, you should see it bind. Or you could bind with the url if you change the declaration to:
public TestModel Get(int? someParameter)
{
}
Thankfully, the team foresaw the potential problems with this and left us with attributes we could use to override this behavior.
public TestModel Get([FromUri]TestRequestModel request)
{
}
The key here is the [FromUri] which tells the model binder to look in the uri for the values. There is also [FromBody] if you want to put a value type in the body of a request.
We were seeing the same thing. In our case the problem was a complex object being passed into a get method. We needed to add a [FromUri] attribute in the parameter to that method.
http://forums.asp.net/t/1809925.aspx/1?GET+requests+with+complex+object+as+input+parameter
public class SearchController : ApiController
{
// added [FromUri] in beta to RC transition otherwise media type formatter error
public IQueryable<SearchResultEventModel> Get( [FromUri]SearchSpecModel search )
{
// ...
}
}
I'm hosting an ASP.NET MVC site on Winhost and recently added a WCF data service for users providing feedback. The service works fine from my local machine, but when I deploy it I get the following System.NullReferenceException returned in Fiddler whenever I try to save an entry to the service:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code></code>
<message xml:lang="en-US">An error occurred while processing this request.</message>
<innererror>
<message>Object reference not set to an instance of an object.</message>
<type>System.NullReferenceException</type>
<stacktrace> at System.Data.Services.Providers.ObjectContextServiceProvider.SetValue(Object targetResource, String propertyName, Object propertyValue)
at System.Data.Services.Serializers.Deserializer.SetPropertyValue(ResourceProperty resourceProperty, Object declaringResource, Object propertyValue, ContentFormat contentFormat, IDataService service)
at System.Data.Services.Serializers.PlainXmlDeserializer.ApplyProperty(XmlReader reader, String propertyName, ResourceType resourceType, Object resource)
at System.Data.Services.Serializers.PlainXmlDeserializer.ApplyContent(XmlReader reader, ResourceType resourceType, Object resource)
at System.Data.Services.Serializers.PlainXmlDeserializer.ApplyContent(Deserializer deserializer, XmlReader reader, ResourceType resourceType, Object resource, EpmAppliedPropertyInfo propertiesApplied, Int32 currentObjectCount)
at System.Data.Services.Serializers.SyndicationDeserializer.ApplyProperties(SyndicationItem item, ResourceType resourceType, EpmAppliedPropertyInfo propertiesApplied, Object resource)
at System.Data.Services.Serializers.SyndicationDeserializer.CreateObject(SegmentInfo segmentInfo, Boolean topLevel, SyndicationItem item)
at System.Data.Services.Serializers.SyndicationDeserializer.CreateSingleObject(SegmentInfo segmentInfo)
at System.Data.Services.Serializers.Deserializer.ReadEntity(RequestDescription requestDescription)
at System.Data.Services.Serializers.Deserializer.HandlePostRequest(RequestDescription requestDescription)
at System.Data.Services.DataService`1.HandlePostOperation(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService`1.ProcessIncomingRequest(RequestDescription description, IDataService dataService)
at System.Data.Services.DataService`1.HandleNonBatchRequest(RequestDescription description)
at System.Data.Services.DataService`1.HandleRequest()</stacktrace>
</innererror>
</error>
Turn on tracing both on the server and on the client (see http://msdn.microsoft.com/en-us/library/ms733025.aspx), then use SvcTraceViewer (http://msdn.microsoft.com/en-us/library/ms732023.aspx). It will usually give you a more detailed exception.
My problem turned out to be 2 issues:
My site reroutes Url's for all GET requests to lower case. ODATA queries are case sensitive. This was causing me trouble with diagnosing the problem but wasn't causing the POST issue. Removing this for my service uri helped me identify the real problem.
The real issue was that my DataContext for my POCO entities had proxy generation enabled.
Here's a lengthier description of how I found the problem in case it helps anyone debug a similar issue:
Per Dan's suggestion, I enabled tracing to see if I could get any additional details. This showed me that I was getting a System.ServiceModel.CommunicationObjectAbortedException. I searched for the cause of this for a while with no success.
Next, I set my EntitySetRights for my DataService to EntitySetRights.All to see if I could determine the error by attempting to read my entity set (note that in my case this is a temporary change for debugging).
static public void InitializeService(DataServiceConfiguration config) {
...
config.SetEntitySetAccessRule("myentityset", EntitySetRights.All);
...
}
I then queried the service http://mydomain.com/myservice.svc/myentityset and received the following error:
Resource not found for the segment 'myentityset'
This turned out to be due to my site forcing all urls to lower case (apparently ODATA queries are case sensitive). My entity set is named something like 'MyEntitySet', but it was being queried as 'myentityset' Once I disabled this for my service url, I received an error similar to the following:
Internal Server Error. The type 'System.Data.Entity.DynamicProxies.myentity_XXXX' is not a complex type or an entity type
Looking up this error led me to this link identifying my core problem. The problem is with POCO Entity Generation for DataServices. In order to fix the problem ProxyCreationEnabled needs to be set to false. I added the following method to my T4 template that generates my ObjectContext.
<#+
private void WriteProxyCreation()
{
#>
this.ContextOptions.ProxyCreationEnabled = false;
<#+
}
#>
I then added a call to my method in each of the constructors similar to the following:
public <#=code.Escape(container)#>()
: base(ConnectionString, ContainerName)
{
<#
WriteLazyLoadingEnabled(container);
WriteProxyCreation();
#>
}
I am developing a ASP.NET MVC web app under .NET 3.5, NHibernate and hosted on Windows Azure. When, the webapp is run from the local development fabric it works fine. Yet, when I move it to Windows Azure, every insert performed from the MVC web role ends up with the exception listed below.
Any idea what's wrong with my NHibernate logic? (might be the session management, not sure)
[AssertionFailure: null id in Lokad.Translate.Entities.User entry (don't flush the Session after an exception occurs)]
NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(Object obj, IEntityPersister persister, Object id, EntityMode entityMode) +292
NHibernate.Event.Default.DefaultFlushEntityEventListener.GetValues(Object entity, EntityEntry entry, EntityMode entityMode, Boolean mightBeDirty, ISessionImplementor session) +93
NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event) +158
NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event) +469
NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) +339
NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event) +85
NHibernate.Impl.SessionImpl.Flush() +275
NHibernate.Transaction.AdoTransaction.Commit() +236
Lokad.Translate.Repositories.PageRepository.Create(Page page)
Lokad.Translate.Controllers.PagesController.Create(Page page)
lambda_method(ExecutionScope , ControllerBase , Object[] ) +69
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +251
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) +31
System.Web.Mvc.<>c__DisplayClassa.b__7() +88
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func1 continuation) +534
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +312
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +856
System.Web.Mvc.Controller.ExecuteCore() +185
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +221
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +586
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +177
Note that I am using _session.FlushMode = FlushMode.Commit; and that the User is used in a custom RoleProvider
public class SimpleRoleProvider : RoleProvider
{
readonly UserRepository Users = new UserRepository();
public override string[] GetRolesForUser(string username)
{
try
{
var user = Users.Get(username);
// no role if user is not registered
if (null == user) return new string[0];
// default role for registered user
return user.IsManager ? new[] { "Manager", "User" } : new[] { "User" };
}
catch (Exception)
{
// role should not fail in case of DB issue.
return new string[0];
}
}
}
You should never catch exceptions and ignore them during a NHibernate transaction.
I try to explain why.
There could be exceptions for instance caused by constraints in the database. (it could also be caused by mapping problems, exceptions thrown by properties or anything else.) NHibernate tries to synchronize the state in memory with the database. This is done on commit - and sometimes before queries to make sure that queries are done on actual data. When this synchronization fails, the state in the database is something random, some changes are persisted, others are not. The only thing you can do in such a case is closing the session.
Consider that decisions and calculations in your code are based on values in memory. But - in case of an ignored exception, this values are not the values in the database, they will never be there. So your logic will decide and calculate on 'fantasy-data'.
By the way, it is never a good idea to catch any exception (untyped) and ignore them. You should always know the exceptions you handle, and be sure that you can continue.
What you're doing here is swallowing programming errors. Believe me, the system will not be more stable. The question is only: do you notice the error when it occurs, or do you ignore it there and even persist the result of the error to the database? When you do the latter, you don't have to be surprised when your database is inconsistent and other error arise when you try to get the data from the database. And you will never ever find the code that is the actual cause of the error.
I have finally found a solution to my own problem. In case people would be interested, I am posting the solution here.
public class SimpleRoleProvider : RoleProvider
{
// isolated session management for the RoleProvider to avoid
// issues with automated management of session lifecycle.
public override string[] GetRolesForUser(string username)
{
using (var session = GlobalSetup.SessionFactory.OpenSession())
{
var users = new UserRepository(session);
var user = users.Get(username);
// no role if user is not registered
if (null == user) return new string[0];
// default role for registered user
return user.IsManager ? new[] {"Manager", "User"} : new[] {"User"};
}
}
}
Basically what was happening is that the RoleProvider repository does not seem to have the same lifecycle than regular in-view / in-controller repositories. As a result, at the time the RoleProvider is called, the NHibernate session has already been disposed causing the exception observed here above.
I have replaced the code by the following one here above. This one has its own NHibernate session management, and ends up working fine.
This exception can occur if your column names include reserved words (e.g. use status as a column name and it will become impossible to Save)