Code Monkey home page Code Monkey logo

restier's People

Contributors

0xced avatar alanwong-ms avatar ansonliam avatar biaol-odata avatar caldwell0414 avatar challenh avatar chinadragon0515 avatar cilerler avatar congysu avatar cwoodruff avatar darryl-davidson avatar eerhardt avatar gathogojr avatar jspuij avatar karataliu avatar laylaliu avatar lewischeng-ms avatar mikepizzo avatar mirsking avatar mkemal avatar qingshuchen avatar rayao avatar robertmclaws avatar robward-ms avatar vikingsfan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

restier's Issues

Support tracking entities

  1. QueryExpressionSourcer::Source
    When there is a real domain entity manager, the underlying DbContext shouldn't track the entities.
  2. ChangeSet::AnEntityHasChanged
    make the ChangeSet 'dynamic' so it gets added to as things change during the flow

Function and action

Function and action support: being able to incorporate those defined DbContext into EdmModel.

Better expression filter support

Provide more meaningful context information via QueryExpressionContext.ModelReference. Now only a limited set of node types have correspondent ModelReferences.

ETag in response for single entity support in RESTier

  • ETagMessageHandler (in System.Web.OData) will verify the returned HttpContent is ObjectContent and its ObjectContent.Value matches the EntityType, if will add Etag header, and if not it won’t add the ETag header.
  • Unfortunately, RestierController will wrap the entity object in an instance of EntityResult, ETagMessageHandler has no clue of that type, so it won’t emit ETag header even it is an entity type result.

Now the question is:

  • What’s the purpose of EntityResult and EntityCollectionResult? Why not return the entity or entity collection directly?

If we must keep EntityResult and EntityCollectionResult, we'd have to implement another etag handler to add the ETag header into response.

Office App Request

• Thread safety in core library, e.g. DomainConfiguration.
• Multiple DB schema support for EF provider. (P0)
• Built in property routing. Now we have to add specific property routing with code, which is very hard for us to achieve. (P1)
• OData data source as a provider. (P1)
• A framework to support data mash between multiple data sources. (P2)
• Better expression filter support, provide more meaningful context information via QueryExpressionContext.ModelReference. Now only a limited set of node types have correspondent ModelReferences. (P2)

Built in property routing

Now we have to add specific property routing with code, which is very hard for us to achieve. (P1)

SQL issue when duration lenght greater than 1 day

See case FilterBuiltInDateFunctions, when duration is > 1day, the exception is:

Microsoft.OData.Client.DataServiceRequestException : An error occurred while processing this request.
---- Microsoft.OData.Client.DataServiceClientException : {
"error":{
"code":"","message":"An error has occurred.","innererror":{
"message":"An error occurred while updating the entries. See the inner exception for details.","type":"System.Data.Entity.Infrastructure.DbUpdateException","stacktrace":" at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at Microsoft.Restier.EntityFramework.Submit.SubmitExecutor.<ExecuteSubmitAsync>d__0.MoveNext() in d:\\git\\DomainFramework\\src\\Microsoft.Restier.EntityFramework\\Submit\\SubmitExecutor.cs:line 24\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at Microsoft.Restier.Core.Submit.DefaultSubmitHandler.d__2d.MoveNext() in d:\git\DomainFramework\src\Microsoft.Restier.Core\Submit\DefaultSubmitHandler.cs:line 281\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at Microsoft.Restier.Core.Submit.DefaultSubmitHandler.d__0.MoveNext() in d:\git\DomainFramework\src\Microsoft.Restier.Core\Submit\DefaultSubmitHandler.cs:line 100\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at Microsoft.Restier.Core.Domain.<SubmitAsync>d__19.MoveNext() in d:\\git\\DomainFramework\\src\\Microsoft.Restier.Core\\Domain.cs:line 707\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at Microsoft.Restier.WebApi.ODataDomainController1.<Post>d__e.MoveNext() in d:\\git\\DomainFramework\\src\\Microsoft.Restier.WebApi\\ODataDomainController.cs:line 328\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Threading.Tasks.TaskHelpersExtensions.<CastToObject>d__31.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()","internalexception":{
"message":"An error occurred while updating the entries. See the inner exception for details.","type":"System.Data.Entity.Core.UpdateException","stacktrace":" at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Data.Entity.Core.Objects.ObjectContext.d__3b1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStoreAsync>d__37.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<ExecuteAsyncImplementation>d__91.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Data.Entity.Core.Objects.ObjectContext.d__2f.MoveNext()","internalexception":{
"message":"SqlDbType.Time overflow. Value '1.01:00:00' is out of range. Must be between 00:00:00.0000000 and 23:59:59.9999999.","type":"System.OverflowException","stacktrace":" at System.Data.SqlClient.SqlCommand.b__24(Task1 result)\r\n at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()\r\n at System.Threading.Tasks.Task.Execute()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.d__0.MoveNext()"
}
}
}
}
}
Result StackTrace:
at Microsoft.OData.Client.SaveResult.HandleResponse()
at Microsoft.OData.Client.BaseSaveResult.EndRequest()
at Microsoft.OData.Client.DataServiceContext.SaveChanges(SaveChangesOptions options)
at Microsoft.OData.Client.DataServiceContext.SaveChanges()
at Microsoft.Restier.WebApi.Test.Scenario.TrippinE2ETestCases.FilterBuiltInDateFunctions() in d:\git\DomainFramework\test\ODataEndToEndTests\Microsoft.Restier.WebApi.Test.Scenario\TrippinE2ETestCases.cs:line 277
----- Inner Stack Trace -----

Improve baseline test

Figure out a better update baseline experience. Currently the Test Explorer doesn't let you select individual items from the test output.

Allow connectionstring name to be picked from the URL so a single instance of the service may be reused for different tenants

I've posted the following message on OData.org blog:
"We need to allow a single OData Service instance to address multiple connection strings configured in the web config file depending on a key to be included on the URI, like
http://mywebsite.com/myODataService/ConnectionStringA/Products
where ConnectionStringA is the connection name. Does Restier address this need?"

I appreciate Quian Li's reply, and I've tried miself to find how to achieve it.
I'm not familiar with ASP.NET MVC, but I've tried to change the WebApiConfig.cs as follows:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        RegisterM2uSalesV4(config, GlobalConfiguration.DefaultServer);
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

    public static async void RegisterM2uSalesV4(HttpConfiguration config, HttpServer server)
    {
        await config.MapODataDomainRoute<M2uSalesV4Controller>(
           "M2uSalesV4Api", "api/m2uSalesV4/{company}/",
            new ODataDomainBatchHandler(server));
    }
}

Then on my controller:

    public class M2uSalesV4Controller : ODataDomainController<M2uSalesV4Domain>
    {
        //private m2uSalesV4OData.Models.m2uSalesv4Entities DbContext
        //{
        //    get { return Domain.Context; }
        //}
        public  Task<System.Net.Http.HttpResponseMessage> Get([FromUri]string company, CancellationToken cancellationToken)
        {
            return  this.Get(cancellationToken);
        }
    }

and I've checked that the value I've included in the URL I'm using on fiddler for the company gets to this method, but can't find a way to use it to create a DbContext using the company name as the connection string key.

The only way I found, which I'm not proud of was to set a static string property:

public class M2uSalesV4Controller : ODataDomainController<M2uSalesV4Domain>
{
    //private m2uSalesV4OData.Models.m2uSalesv4Entities DbContext
    //{
    //    get { return Domain.Context; }
    //}
    public static string CompanyName = "m2uSalesv4Entities";
    public Task<System.Net.Http.HttpResponseMessage> Get([FromUri]string company, CancellationToken cancellationToken)
    {
        CompanyName = company;
        return this.Get(cancellationToken);
    }
}

and change the DbContext initializer:

    public partial class m2uSalesv4Entities : DbContext
    {
        }
            public m2uSalesv4Entities()
            : base("name=" + M2uSalesV4Controller.CompanyName) //  "m2uSalesv4Entities"
        {
        }

and it worked! I can now use:
http://localhost:64068//api/m2uSalesV4/companyA/myTable
http://localhost:64068//api/m2uSalesV4/companyB/myTable
to get values from two databases with the same structure, each with its own connectionstring:

  <connectionStrings>
    <add name="m2uSalesv4Entities" connectionString="metadata=re ..."/>
    <add name="companyA" connectionString="metadata=res://*/Models.m ..."/>
    <add name="companyB" connectionString="metadata=res://*/Mode ..."/>
  </connectionStrings>

So, I'm looking forward to better alternatives to pick the appropriate connection string .

Support full edm interfaces

Implement domain versions of all the other EDM interfaces to ensure it is impossible to get elements that are not supposed to be visible.

Model should be loaded later on demand

For now, in MapODataDomainRoute, controller.Domain.GetModelAsync is called. This is due to webapi.odata's ODataPathRouteConstraint requires an immutable Edm model, instead of Func<> or a model provider. Therefore, need to create an Edm model when create an OData route.

Handle the case when entity is resolved but concurrency checks have failed

ChangeSetPreparer::FindEntity

There are 2 cases where the entity is not found:

  1. it doesn't exist
  2. concurrency checks have failed
    we should account for both - I can see 3 options:
    a. always return "PreConditionFailed" result - this is the canonical behavior of WebAPI OData (see http://blogs.msdn.com/b/webdev/archive/2014/03/13/getting-started-with-asp-net-web-api-2-2-for-odata-v4-0.aspx)
  • this makes sense because if someone deleted the record, then you still have a concurrency error
    b. possibly doing a 2nd query with just the keys to see if the record still exists
    c. only query with the keys, and then set the DbEntityEntry's OriginalValues to the ETag values, letting the save fail if there are concurrency errors

Exception thrown when accessing navigation property member after Include()

It throws saying that sequence contains more than 1 element. Stack is in EntityQueryModelVisitor.BindMemberExpression, in the closure which calls "ps.Single() as IProperty", apparently ps contains more than 1 element.
Repro code is as simple as:

            var l5 = ctx.Contacts.Include(o => o.User2).Select(o => new
            {
                M1 = o.ID,
                M2 = o.User2.Name,    // This causes InvalidOperationException, while o.User2 ok.
            }).ToArray();

Multiple controller should work more wisely

Problems

For example, we have a the default controller

public class AdventureWorksController : ODataDomainController<AdventureWorksDomain>
    {
        private AdventureWorks DbContext
        {
            get { return Domain.Context; }
        }
    }

And one special controller

public class CustomersController : ODataController
{
        private readonly AdventureWorks _context = new AdventureWorks();

        [ODataRoute("Customers({key})/EmailAddress")]
        public string GetEmailAddress([FromODataUri] int key)
        {
            return _context.Customers.Single(c => c.CustomerID == key).EmailAddress;
        }
}

When try to request ~/Customers(1)/EmailAddress, it can work correctly but when try to request ~/Customers, it gets 404 error.User has to add the basic Get method in the CustomersController to make it work.

Suggested behavior

The default controller can automatically handle the basic CRUD operations, these should be "inherited" to the specific controllers. In other word, user don't have to write the logic already implemented in the default controller. In the code above, for example
~/Customers -- should work directly by AdventureWorksController
~/Customers(1)/EmailAddress -- should work directly by CustomersController

Use more up-to-date database for RESTier samples

Customer complains

Why are you using Northwind and not a database compatible with VS 2013? It’s a PITA to have to get Northwind converted to VS 2013 and will stop many people at step one if they try the tutorial.

One better option is to use AdventureWorks or AdventureWorksLT (light version of AdventureWorks). They have better support for higher version of SQL server and can easily download the data files (.md) easily at codeplex and codeplex

Improve key parsing logic

RestierQueryBuilder::GetPathKeyValues

Current parsing implementation does not allow key values to contain commas or equal.

Wiki: Invalid File Names

Some of the wiki file names have colons (:) in them, such as Samples 1: Getting started basic.md. Colons are illegal characters on NTFS file systems. As a result, the Wiki is unable to be cloned to a Windows machine.

error: Invalid path 'Samples 1: Getting started   basic.md'
error: Invalid path 'Samples 2: Getting started - advanced.md'
error: Invalid path 'Samples 3: How to enable multiple controllers.md'
error: Invalid path 'Samples 4: How to add OData operations.md'

$count on OnFilterEntities is not consistent

Suppose there is code below in NorthwindDomain.cs

        private IQueryable<Customer> OnFilterCustomers(IQueryable<Customer> customers)
        {
            return customers.Where(c => c.CountryRegion == "France");
        }

Then in NorthwindController.cs

        [ODataRoute("Customers/$count")]
        public IHttpActionResult GetCustomersCount()
        {
            return Ok(DbContext.Customers.Count());
        }

still returns the count of customers from all CountryRegion and this cause the inconsistency problem. And user cannot directly using return Ok(OnFilterCustomers(DbContext.Customers).Count()); since the OnFilterCustomers is a private method.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.