how to properly handle OData complex type relationships - asp.net-web-api

Trying to build a WebAPI 2 / OData v4 service around a typical default Northwind database, using Entity Framework 6.1
My WebApiConfig is unhappy about "complex type relationships":
An exception of type 'System.InvalidOperationException'
occurred in System.Web.OData.dll
but was not handled in user code
Additional information: The complex type 'ODataProductService.Models.Order_Detail'
refers to the entity type 'ODataProductService.Models.Product'
through the property 'Product'.
Obviously, in any given database, these relationships are very likely to occur.
What is the proper way of hanlding this?

Here is how I gat this resolved:
1) added the following statements to my WebApiConfig.cs:
I have made a GitHub repo with a working solution in it:
builder.EntitySet<Customer>("Customers");
builder.EntitySet<Product>("Products");
builder.EntitySet<Order>("Orders").EntityType.HasKey(o => o.OrderID);
builder.EntitySet<Order_Detail>("Order Details").EntityType.HasKey(od => od.OrderID);
builder.EntitySet<CustomerDemographic>("CustomerDemographics").EntityType.HasKey(cd => cd.CustomerTypeID);
I have also made a repo with a working solution:
https://github.com/eugene-goldberg/ODataProductService/
The Readme file pretty much describes what to pay attention to.

You could also use the containment feature of OData V4. Using containment, you can avoid defining an entity set for Order_Detail.

Related

AsyncCrudAppService Breaks Swagger When Providing TCreateInput and TUpdateInput

I recently downloaded a Single Page Web Application (Angular) from https://aspnetboilerplate.com/Templates using 3.x target version.
I just simply added a few entities and then started to follow the steps on this page https://aspnetboilerplate.com/Pages/Documents/Application-Services
Things do work well for me to Get, List, Update, and Delete entities when my app service class is just inheriting AsyncCrudAppService<Entities.PhoneBook, PhoneBookDto, long, GetAllPhoneBooksInput>, however when it is inheriting AsyncCrudAppService<Entities.PhoneBook, PhoneBookDto, long, GetAllPhoneBooksInput, CreatePhoneBookInput, and UpdatePhoneBookInput> the swagger definition will no longer load.
GitHub Repo: https://github.com/woodman231/MyPhoneBooks
(which currently does not work and will not load Swagger page).
I can get the swagger page to load by removing CreatePhoneBookInput and UpdatePhoneBookInput from
https://github.com/woodman231/MyPhoneBooks/blob/main/aspnet-core/src/MyPhoneBooks.Application/SimpleCrudAppServices/ISimplePhoneBookCrudAppService.cs#L9
and
https://github.com/woodman231/MyPhoneBooks/blob/main/aspnet-core/src/MyPhoneBooks.Application/SimpleCrudAppServices/SimplePhoneBookCrudAppService.cs#L14
However, again I am still unable to create entities using this default implementation. Any ideas?
I have cloned your repo and run it and I figured out the error, first as I tell you in comments I verified the text log, and it said the next:
System.InvalidOperationException: Can't use schemaId "$CreatePhoneBookInput" for type "$MyPhoneBooks.SimpleCrudAppServices.Dtos.CreatePhoneBookInput". The same schemaId is already used for type "$MyPhoneBooks.PhoneBooks.Dtos.CreatePhoneBookInput"
What happenig is that you have these two classes UpdatePhoneBookInput, CreatePhoneBookInput repeated in SanokeCrudAppServices\Dtos and PhoneBooks\Dtos
You have the classes in both folders with same exact name, and thats the problem, if you change the name in whatever place the swagger definition will load without errors, I have do it like this and everything works fine!
Change the name in one of the places, and all will be working fine
Personally I don't like to use a different Dto for Create and Update for me is easier to user just one Dto for all.
Ok I figured it out. I had also made a DIY AppService and some of the DTO Class Names associated with the DIY App Service clashed with the DTO Class Names associated with the Automated Service. It was acceptable in .NET since they were in different name spaces but once the swagger definition was configured I assume that there was multiple instances of the same DTO Defition. I checked the AbpLogs table but they didn't give me much details as to the specifics of the internal server error while loading the definition. It sure would have been useful to know that.

BindingResolutionException while creating services to access database

We are trying to implement architecture suggested in following article in order to make our application extensible
http://dfg.gd/blog/decoupling-your-code-in-laravel-using-repositiories-and-services
The article divides the models into following
Entities - normal Eloquent classes
Repositories - these use Entities to get data
Services - Contain Business logic
To try out the architecture we are trying to access a small table
We have created following classes
app/models/entities/Reminder.php //normal Eloquent model
app/models/repositories/reminder/ReminderInterface.php
app/models/repositories/reminder/ReminderRepository.php
app/models/repositories/reminder/ReminderRepositoryServiceProvider.php
app/models/services/reminder/ReminderFacade.php
app/models/services/reminder/ReminderService.php
app/models/services/reminder/ReminderServiceServiceProvider.php
We are struck at following error
Illuminate \ Container \ BindingResolutionException
Target [Repositories\Reminder\ReminderInterface] is not instantiable.
Can someone please guide what may be going wrong?
Our code is exactly the same as in the article. I tried to be brief in this description as posting code of all 7 classes is not sensible. Please let me know if you need any details.
Option 1
You might be missing a binding:
App::bind('Repositories\Reminder\ReminderInterface', 'Repositories\Reminder\ReminderRepository');
If you don't tell Laravel which implementation of your Interface you need it to Instantiate it will try to instantiate ReminderInterface, which is not instantiable, as the error says.
Option 2
If you are binding it in your service provider, you have to make sure your service provider is being executed, by adding it to app/config/app.php?

jaydata/jaysvcutil 1.3.5 inverseProperty support for WebAPI

I've seen Missing inverse property in asp.net webapi odata $metadata and the WebAPI $metadata I'm dealing with behaves as described in this article: it doesn't reuse associations for bi-directional navigation properties.
When using jaysvcutil 1.3.5 all navigational properties come up as $$unbound.
$data.Entity.extend('API.Models.Document', {
...
'Document_Versions': {
'type':'Array',
'elementType':'API.Models.Document_Versions',
'inverseProperty':'$$unbound' }
});
Other than manually updating the inverseProperty information is there anything to get the desired result automatically?
Update based on #Robesz answer
Manually adding inverseProperty information to the static .js converted by JaySvcUtil is doable, but I'm asking if there's an option to accomplish that with the dynamic conversion as well.
There seems to be to options
make modifications to the .NET WebAPI. Might be challenging, because their seem to be good reason for their implementation, but maybe somebody already successfully did that.
modifying the conversion XSLT that JayData using to take that behavior into account.
We've just arrived to the same results with WebAPI OData, but after editing the generated context file manually and adding the inverseProperty everything is working fine
This should be most probably handled by extending JayData's XSLT conversion. I've created an issues for that on https://github.com/jaydata/jaydata/issues/155.

How do you build OData IEdmModel from Entity Framework model

The title says it all really. I've found several blogs with different ways (serializing the EF model to XML and then de-serializing again to the IEdmModel was one) but they're all based on old version of the OData package.
Serializing is the only way.
Example.
Relevant work.
I've ranted about this a few months ago. AFAIK nothing changed since then, and I personally don't expect them to change too much. The short story is that as of September 2012, there is no plan to use EdmLib in EF, nor is there to use EF's code in other projects.
How much should we align with OData’s EdmLib?
Not worth adopting code
Cost of implementing SSDL & MSL
Freedom to evolve our API independently
Look at aligning names of types and properties where appropriate
If your DbContext is being built from a database-first approach the given answer will fail giving this error:
Creating a DbModelBuilder or writing the EDMX from a DbContext created using Database First or Model First is not supported. EDMX can only be obtained from a Code First DbContext created without using an existing DbCompiledModel.
After some time messing with this I have found an appropriate solution. Basically you grab the CSDL resource from the assembly containing the DbContext in question and parse it using the Microsoft.Data.Edm.Csdl.CsdlReader.TryParse method. The resulting IEdmModel is valid containing the exact information given by EntityFramework when the model was built.
See here for an example with usage

Update Custom Entities in MS CRM 4.0 via Custom Workflow

I have created a custom entity in MS CRM 4.0 and am trying to update a couple of the attributes via a custom worflow in .Net. I have read through several of the forums and blog posts and am still confused on how to access the custom entity and update some of their attributes.
I created a custom entity to replace how CRM was doing allotments as our company has some specific business rules that CRM wasn't doing. When a task is completed on an incident I want to update an attribute in the custom entity with the task duration. Any help would be greatly appreciated.
Thanks
When using the CRM web service in a custom workflow, you'll need to use DynamicEntity objects. The workflow context webservice is just an ICrmService so it doesn't know about your specific customizations. There's a pretty sample here: http://www.stunnware.com/crm2/topic.aspx?id=CustomWorkflowActivity
I imagine you could also add the CRM web services as a web reference to your workflow project. Then you'd have strongly types objects for your custom entities. I've never done this for my custom workflows, but it works for other custom apps accessing CRM.
Choosing Dynamic Entities over WSDL in favour is always the better choice.
When you develop a piece of code, you are more flexible with your classes. You could use your piece of software in different contexts for different systems. That's the reason Dynamic Entities were invented.
It's very easy and you dont'have to use DynamicEntity. You have to go to Settings -> Customization -> Download WSDL. Take the wsdl and use it in your project. Now you have all your custom entities strongly typed. All you have to do is to write something like this:
Guid entityId = getEntityId();
new_yourCustomEntity entity = new new_yourCustomEntity();
entity.new_yourCustomEntityid = entityId;
entity.new_customProperty = "value";
CrmService crmService = new CrmService();
crmService.Update(entity);
Maybe what you really mean is Custom Workflow Activity? This involves writing your own .NET class to add functionality to the standard CRM WF in form of new step types. If what you want to do is just to update an attribute you don't really need this, even if it is on a custom entity. The Update record step does just this and allows dynamic values (coming from other entities) to be specified.
Hope it helps
Daniel

Resources