Is it possible to have a generic server-side query like the following?
public IQueryable<TContact> GetContactsOfType<TContact>()
where TContact : Contact
{
return ObjectContext.Contacts.OfType<TContact>();
}
I want RIA to recognize and regenerate for me this query at the client project.
Note: Contact is an abstract class that has some subclasses. I'm using Entity-Framework generated EntityObjects.
The error I get when I'm trying compile: Type 'TContact' is not a valid entity type. Entity types must have a default constructor.
By default WCF RIA Services does not expose generic domain service methods for the client to call. RIA is strongly-typed to make it easier to reason about the behavior.
But there seems to be a workaround with defining your on DomainOperationEntry and a custom DomainServiceDescriptionProvider. Colin Blair posted an answer here. That seems to match what you are expecting.
Update: I tried what you want im my silverlight project and defined a generic query method on my domain service. The project compiles successfully but the generic parameter is ommited on the client side.
Instead, I would suggest to use Text Template of EF generator to create RIA Services operations for every entity. And use a pattern of name like how RIA Services uses "Get" <Type> Query, and other methods.
Related
I am using https://www.nuget.org/packages/Microsoft.AspNetCore.OData/7.3.0 to expose OData 4 based Querable API using ASP.NET Core 3.1 API
I have EF Data context with keyless entity(Database view), When I try to expose it with OData Convention based model
modelBuilder
.Entity<Student>(eb =>
{
eb.HasNoKey();
eb.ToView("vw_students", "public");
});
And here is my OData EDM Model mapping with EF Core entity type
var edmBuilder = new ODataConventionModelBuilder();
edmBuilder.EntitySet<Student>("Students");
return edmBuilder.GetEdmModel();
I get an error on this line edmBuilder.GetEdmModel() The entity set 'Students' is based on type 'ODataCore31.Student' that has no keys defined.
My questions-
1) Are Keyless entity types are natively supported by OData model?
2) Is there any workaround?
To expose a Keyless View or custom query via OData you must use an Unbound Function instead of a full OData Controller, which is configured via the EntitySet<T>() expression in the conventional builder.
This makes sense because an EntitySet Controller allows item based operations but without a specific Key there is no conventional way to address a specific Item as its own Resource.
A keyless type is therefore a ComplexType and not an Entity, by definition an Entity MUST have a key so that OData can support by item or key based navigation.
The OData way to expose a Keyless collection as an Unbound Function involves 3 important steps:
1. Declare your query in a controller so that it returns an IQueryable<T>
A specific quirk about an Unbound Function or Action is that they are not specifically bound to any controller, so you can declare your unbound endpoints in any controller of your choosing.
I prefer to create a specific controller to host all unbound components, the name is not important as long as it does not clash with other controllers or entity types in your domain.
As with the default collection endpoints, we need to make sure our endpoint returns an IQueryable<Student>, you can still use the IActionResult response type if you need that level of abstraction, but the implementation should be queryable.
2. Set the specific route for the method using ODataAttributeRouting.
This is a crucial element, in usual convention-based implementations we do not declare specific routes on each method, each controller's route prefix is resolved from the controller name and the endpoints are resolved from the method names.
It helps to follow conventions as much as possible to keep your implementation transparent and configurable, if you always declare specific routes then it is important that the routes you configure match the registration with the ODataConventionModelBuilder.
In the specific case for an unbound endpoint, there is no controller element in the route at all, which has two implications: you cannot use a route prefix specified at the controller level because leaving the prefix empty in an ODataRouteComponent will trigger the default logic which will use the name of the controller class; and you must declare the route on the endpoint itself as a full route because all the convention based routing depends on the controller element in the route to map to the controller to resolve the possible endpoints, so the runtime won't know which controller to inspect.
You can write your own route resolution logic but that is out of scope for this issue.
This is a minimal example controller implementation:
NOTE: "odata" is the routePrefix registered to the OData service in this example
[ODataAttributeRouting]
public partial class ViewsController : ODataController
{
protected AppDbContext _db;
public ViewsController(AppDbContext context)
{
_db = context;
}
[HttpGet("odata/" + nameof(Students))]
[EnableQuery]
public IQueryable<Student> Students(ODataQueryOptions<Student> _queryOptions)
{
return this._db.Students;
}
}
3. Register/Map the route in the ODataConventionModelBuilder
This step is often overlooked, if you skip this step the endpoint can be queried via HTTP but this is a standard Web API implementation and not a conventional OData implementation, so the endpoint will not be documented in the CSDL ($metadata) or some swagger implementations and as a result will not be available to OData client generators.
var edmBuilder = new ODataConventionModelBuilder();
edmBuilder.Function("Students").ReturnsCollection<Student>();
return edmBuilder.GetEdmModel();
An inline services registration would look like this, notice the "odata" routePrefix declared in the AddRouteComponents method, this MUST match the custom route declaration prefix.
services.AddControllers().AddOData(opt =>
{
var edmBuilder = new ODataConventionModelBuilder();
edmBuilder.Function("Students").ReturnsCollection<Student>();
opt.AddRouteComponents("odata", edmBuilder.GetEdmModel()).EnableQueryFeatures();
}
This solution was tested and written using OData 8.0 (for .Net 5.0), the concepts are fully compatible with OP's requested ASP.NET Core 3.1 and OData Lib v7.3 however some of the namespaces are different. 7.3 can easily be upgraded to 8 or 9 and I encourage you to do so as there are significant performance and functional improvements in the latest releases.
It looks like keyless entities are explicitly unsupported in OData's Edm Model Builder:
https://github.com/OData/ModelBuilder/blob/86d26d7346e9359e64e5ec08c31ac7e829cbe788/src/Microsoft.OData.ModelBuilder/ODataModelBuilder.cs#L629
So the answer to your first question is no, for the time being.
For the second question, this may not be possible in your use case, but if your keyless entity corresponds to a view on your database, you could actually just build a normal (not keyless) entity if there's a column or combination of columns on this view that's unique. In one of my models, I created an entity class corresponding to a database view, and then added the [Key] attribute to the property on a column on that view that's guaranteed to be unique per record. Then OData treats this just like any other entity. The only caveat here is that it's read-only.
If, on the other hand, you want to surface a keyless entity to OData because it pulls columns from multiple database tables/views (like you could do with a defining query in classic EF), and you don't want to create a new view in your database, I think you're out of luck.
When using the SQLEntityConnection F# type provider, the generated types provided by this type provider may not be used by other F# assemblies and, by implication, any other assemblies, such as a separate ASP.Net application, console application, C# program, etc. So the type provider must be marked internal or private.
Moreover, functions that use the type provider may not be more accessible than the types provided by the type provider. So such functions must also be marked internal or private.
What is the proper way to map these types, which by necessity must be marked internal or private, to something public which can be used outside the same assembly?
A simple pattern using Entity Framework is to retrieve an entity from a DbContext, update some properties, call some methods, update some child entities, etc., then call SaveChanges() on the DbContext. Since the retrieved entity is still attached to the DbContext and the DbContext is still in scope, the changes are tracked automatically and the update is simple.
Is the same workflow possible using the SQLEntityConnection F# type provider? My guess is no if the entity needs to be updated outside the assembly where the TypeProvider is used, e.g. in a C# MVC project, given the limitations I mentioned in the original post. What is a comparable approach to doing basic CRUD operations using the SQLEntityConnection type provider? Unfortunately, the official documentation is very lacking in basic operations. Maybe an alternate type provider is a better, more functional choice.
I am trying to create an OData service for an entity with a System.Data.Entity.Spatial.DbGeography type property. The entity is part of EntityFramework context (database). Getting the data in/out of the Db works fine. I am able to request the data from OData service. It is returned as a WKT string. However, when I POST a similar entity back to insert a new record into the Db, I get the error "no parameterless constructor" from the framework. It is the DbGeography constructor referred I guess, because when the property is set to null in the POST request, all is fine.
I found that OData should be working better with System.Spatial.Geography (I used System.Spatial.GeographyPoint). Even then it complaints about the IConvertible interface not implemented when I post data.
Is there any "direct" way of getting the data out and into the OData service using the Entity.DbGeography (and similar) types? I found some ways of binding in case of using MVC models and controllers - would similar action be needed here? Or should I "just" use the System.Spatial.Geography for OData and translate to DbGeography for EF? Something like:
Geography --> OData service --> .net translate --> DbGeography --> EF --> DB
I found another question similar to this one: OData + EF. Writing geography types
But there is no answers nor comments there at all. I assume this problem may have some trivial solution I cannot see, or no one has the same issue (hard to believe...)?
I am having trouble getting related entities to be loaded on the client using RIA Services and EF 4.1 with Silverlight.
I'm currently using the Include() method on my DbDomainService with an Expression parameter and am finding that when stepping through my service the related entities are loaded just fine. However, when the Queryable results are returned to the client NO related entities are loaded - they are null. All of my entities are marked with the [DataMember] attribute so I have assumed that it isn't a serialization issue. Moreover, my DbDomainService query method is marked with the [Query] attribute.
I was wondering if there is anything specific that has to be set up on the client when using RIA Services with EF 4.1 code first? I must be missing something, but I'm not sure what.
Any help would be appreciated.
Thanks,
sfx
While you may have used the .Include() in your service call, you also have to add the [Include] attribute in the metadata class that is also created.
The .Include() statement tells EF to generate the SQL necessary to retrieve the data, while the Include attribute tells WCF RIA Services to make sure the Entity Class is also created on the client.
Once the data arrives at the client, it needs to know what type of structure to put it in as well.
HTH
How i can use Inheritance in Entity Framework with .NET RIA Services?
Problem:
if there is inheritance in EF, silverlight application don't compiled.
Can you help me?
This:
Type 'Common.Individual' is a direct or indirect subclass of Type 'Common.Customer'. DomainServices cannot return a Type that is a subclass of another Type returned from the same DomainService.
...does not mean "no inheritance support." It does mean that (in the current version of RIA services) you can't return both a parent and a child type to the client.
That is a limitation, and it's a limitation you have to live with in RIA Services for now. That said, I don't think it's as big as a limitation as the thread you reference implies. Is not the same as saying you cannot use inheritance at all. Also, I think that inheritance tends to be overused in entity mapping for reasons I explain in great detail in this presentation.
So while I can't fix the limitation, my suggestions are:
Use composition instead of inheritance when appropriate (cf. the presentation referenced above).
When you must use inheritance, RIA Services will require that you don't return the parent type.