Coder Social home page Coder Social logo

restier's Introduction

Microsoft Restier - OData Made Simple

Releases   |   Documentation   |   OData v4.01 Documentation

Build Status Release Status
Code of Conduct Twitter

What is Restier?

Restier is an API development framework for building standardized, OData V4 based RESTful services on .NET.

Restier is the spiritual successor to WCF Data Services. Instead of generating endless boilerplate code with the current Web API + OData toolchain, RESTier helps you boostrap a standardized, queryable HTTP-based REST interface in literally minutes. And that's just the beginning.

Like WCF Data Services before it, Restier provides simple and straightforward ways to shape queries and intercept submissions before and after they hit the database. And like Web API + OData, you still have the flexibility to add your own custom queries and actions with techniques you're already familiar with.

What is OData?

OData stands for the Open Data Protocol. OData enables the creation and consumption of RESTful APIs, which allow resources, defined in a data model and identified by using URLs, to be published and edited by Web clients using simple HTTP requests.

OData was originally designed by Microsoft to be a framework for exposing Entity Framework objects over REST services. The first concepts shipped as "Project Astoria" in 2007. By 2009, the concept had evolved enough for Microsoft to announce OData, along with a larger effort to push the format as an industry standard.

Work on the current version of the protocol (V4) began in April 2012, and was ratified by OASIS as an industry standard in Feb 2014.

Getting Started

  1. Create an ASP.NET Classic or ASP.NET Core web project.
  2. Add the corresponding Restier package for the flavor of ASP.NET you're targeting: Microsoft.Restier.AspNet or Microsoft.Restier.AspNetCore.
  3. Add thhe corresponding Restier package for the flavor of Entity Framework you are targering: Microsoft.Restier.EntityFramework or Microsoft.Restier.EntityFrameworkCore
  4. Review the ASP.NET Classic or ASP.NET Core samples to help you get started.

Use Cases

Coming Soon!

Supported Platforms

Restier 1.1 currently supports the following platforms:

  • Classic ASP.NET 5.2.7 and later
  • ASP.NET Core 6.0, 7.0, and 8.0 (Binaries targeting deprecated versions of .NET are still available on NuGet.org)
  • Entity Framework 6.4 and later
  • Entity Framework Core 6.0 and later

Restier Components

Restier is made up of the following components:

  • Microsoft.Restier.AspNet & Microsoft.Restier.AspNetCore: Plugs into the OData/WebApi processing pipeline and provides query interception capabilities.
  • Microsoft.Restier.Core: The base library that contains the core convention-based interception framework.
  • Microsoft.Restier.EntityFramework & Microsoft.Restier.EntityFramework: Translates intercepted queries down to the database level to be executed.
  • Microsoft.Restier.Breakdance: Unit test Restier services and components in-memory without spinning up a separate IIS instance, as well as verify the availability of your custom convention-based interceptors.
  • Microsoft.Restier.AspNetCore.Swagger: Automatically generates Swagger documentation for your ASP.NET Core Restier service.

Ecosystem

Restier is used in solutions from:

Community

After a couple years in statis, Restier is in active development once again. The project is lead by Robert McLaws and Mike Pizzo.

Contributing

If you'd like to help out with the project, our Contributor's Handbook is also located in the Wiki.

Reporting Security Issues

Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) [email protected]. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the Security TechCenter. You can also find these instructions in this repo's SECURITY.md.

Contributors

Special thanks to everyone involved in making Restier the best API development platform for .NET. The following people have made various contributions to the codebase:

Microsoft External
Lewis Cheng James Caldwell
Challen H Angel Garay
Eric Erhardt Cengiz Ilerler
Vincent He Kemal M
Dong Liu Mateusz Malicki
Layla Liu Robert McLaws
Fan Ouyang Micah Rairdon
Mike Pizzo Jan-Willem Spuij
Congyong S Chris Woodruff
Mark Stafford
Ray Yao

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 mitchkuijpers 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

$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.

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.

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.

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

Function and action

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

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

Improve key parsing logic

RestierQueryBuilder::GetPathKeyValues

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

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.

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'

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)

Better expression filter support

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

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

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 .

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

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.

Built in property routing

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

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();

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.