Service Stack Redis in Web Api OData add EdmEntityObject / EdmEntityObjectCollection - dynamic

We are able to add a normal POCO class to a Service Stack Redis client
IRedisTypedClient<Product> objRedisTypedClientNB = redisClient.As<Product>();
IRedisList<Product> objRedisListNB = objRedisTypedClientNB.Lists["Product"];
Product objNews = new Product(DateTime.Now.Ticks.ToString());
objRedisTypedClientNB.Lists["Product"].Push(objNews);
Adding products object to redis cache is fine
When using dynamic web api odata v4 service,
Metadata is created at runtime for each request based on url
How to add EdmEntityObject / EdmEntityObjectCollection into redis
EdmEntityObject objEntityObject = new EdmEntityObject((EdmEntityType)objEntityType);
objEntityObject.TrySetPropertyValue("Id", "test id");
IRedisTypedClient<EdmEntityObject> objRedisTypedClientEEOC = redisClient.As<EdmEntityObject>();
IRedisList<EdmEntityObject> objRedisListEEOC = objRedisTypedClientEEOC.Lists["EntityName"];
objRedisTypedClientEEOC.Lists["EntityName"].Add(objEntityObject); // throws error
Error
An unhandled exception of type 'System.StackOverflowException' occurred in Unknown Module.
How to resolve the error
How to add EdmEntityObject / EdmEntityObjectCollection 's instances into redis cache
Note:
1. Should not use POCO classes
2. ODataModel itself is created at runtime only

A System.StackOverflowException is indicative that you're trying to serialize non-serializable objects with cyclical dependencies which are not supported in any of ServiceStack's Serializers (and will also fail in most other Serializers).
Essentially EF DataModels have cyclical dependencies making them poor candidates for serialization. You should instead have them map to clean DTO's which can be serialized, or alternatively use any Micro ORM like OrmLite or Dapper which map resultsets into clean POCO models.
As a goal OrmLite ensures that all its features (inc. its Reference Support) maps to clean disconnected POCO's so they're always serializable and can be re-used in Services DTO's and other NoSQL datastores.

Related

Spring Data REST return entity after create/update using projection

Spring Data REST has 2 properties (spring.data.rest.return-body-on-create, spring.data.rest.return-body-on-update) that are apparently true by default.
These properties make Spring return the submitted entity after create/update using POST/PUT/PATCH.
What I want is that the returned entity uses a projection to return its data so I tried submitting to this URL: http://localhost:8080/MYAPP/api/persons?projection=personProjection for new entities using POST and to this URL: http://localhost:8080/MYAPP/api/persons/1?projection=personProjection for old entities using PUT and PATCH.
All trials failed and I got the following exception: "Target bean of type com.sun.proxy.$Proxy231 is not of type of the persistent entity (com.test.entities.Person)!: com.sun.proxy.$Proxy231"
According to this question: After upgrade from Spring Boot 1.5 to 2.1 we get Target bean of type com.sun.proxy.$Proxy is not of type of the persistent entity
It seems that what I was trying to do was possible in older version of Spring Data REST.
So is this a bug ?
Or is there a workaround for this behavior without having to get the entity again in another back-end call or creating a custom controller for submissions ?
I am using Spring Boot v2.2.6.RELEASE
Thanks

Why this EntityManagerSaveException?

I am using Silverlight 4 and DevForce 6.1.11.0
I have some POCO classes that implement EntityAspect.
When changes are saved via EntityManager.SaveChanges; DevForce does not save these POCO entities to the server, because these POCO entities are not part of EF.
Instead I send them to a webservice via WebClient.UploadStringAsync.
This works, expect when I am saving more than one entity of the same type. Then I get this exception:
EntityManagerSaveException: An entity with this key: PocoMyClass: 0,0
already exists in this entityManager
I have checked the cache, and there is no entity with that key.
The WebClient.UploadStringAsync still sends the data and everything gets saved, but the exception does not look good to customers.
How do I work around this exception?
The poco entities that I am having problems with are only supposed to live on the client, not the DevForce server. The reason is that only the client can access these on the local network.
So I am using WebClient.OpenReadAsync to read the data in and create the poco entities on the client. And then I use WebClient.UploadStringAsync when saving the poco entities.
When creating the poco entity and adding it to the entitymanager, I do like this:
var pocoEntity = new PocoMyClass();
pocoEntity.keyId = some integer;
…
entityManager.AddEntity(pocoEntity);
pocoEntity.EntityAspect.AcceptChanges();
After doing this I see that the properties for EntityVersion.Original of the poco entity only contains empty stuff (NULL´s and zero’s).
Is this the reason for the exception when saving?
How can I manipulate EntityVersion.Original when the entity does not come from the DevForce server?

Share POCO types between WCF Data Service and Client Generated by Add Service Reference

I have a WCF Data Service layer that is exposing POCO entities generated by the POCO T4 template. These POCO entities are created in their own project (i.e. Company.ProjectName.Entities) because I'd like to share them wherever possible.
I have a set of interfaces in another project (Company.ProjectName.Clients) that reference these POCO types by adding an assembly reference to the Company.ProjectName.Entities.dll. One of the implementation of these interfaces is a .NET client that I want to consumes the service using the WCF Data Service Client Library.
I've used the Add Service Reference to add service reference. This generated the DataServiceContext client class and the POCO entities that are used by the service. However, these POCO types gemerated by the Add Service Reference utility now have a different namespace (i.e. Company.ProjectName.Clients.Implementation.WcfDsReference).
What that means is that the POCO types defined in the interfaces cannot be used by the types generated by the utility without have to cast or map.
i.e. Suppose I have:
1. POCO Entity: Company.ProjectName.Entities.Account
2. Interface: interface IRepository<Company.ProjectName.Entities.Account>{....}
3. Implementation: ServiceClientRepository : IRepository<Company.ProjectName.Entities.Account>
4. WcfDsReference: Company.ProjectName.Clients.Implementation.WcfDsReference
& Company.ProjectName.Clients.Implementation.WcfDsReference.Account
Let's say I want to create a DataServiceQuery query on the Account, I won't be able to do this:
var client = new WcfDsReference(baseUrl);
var accounts = client.CreateQuery<Company.ProjectName.Entities.Account>(...)
OR: client.AddToAccounts(Company.ProjectName.Entities.Account)
, because the CreateQuery<T>() expects T to be of type & Company.ProjectName.Clients.Implementation.WcfDsReference.Account
What I currently have to do is to pass the correct entity to the CreateQuery method and have to map the results back to the type the interface understands. (Possible with a mapper but doesn't seems like a good solution.)
So the question is, is there a way to get the Add Service Reference utility to generate methods that use the POCO types that are in the Company.ProjectName.Entities namespace?
One solution I am thinking of is to not use the utility to generate the DataServiceContext and other types, but to create my own.
The other solution is to update the IRepository<T> interface to use the POCO types generated by the utility. But this sounds a little bit hacky.
Is there any better solution that anyone has come up with or if there's any suggestion?
Ok, a few hours after starting the bounty I found out why it wasn't working as expected on my end.
It turns out that the sharing process is quite easy. All that needs to be done is mark the model classes with the [DataServiceKey] attribute. This article explains the process quite well, in the 'Exposing another Data Model' section
With that in mind, what I was trying to do is the following:
Placing the model on a separate class library project C, sharing it with both webapplication projects A and B
Create the data service on project A
Add the service reference on project B
Delete the generated model proxies out of the service reference, and update it to use my model classes in project C
Add the DataServiceKey attribute to the models, specifying the correct keys
When I tried this it did not work, giving me the following error:
There is a type mismatch between the client and the service. Type
{MyType} is not an entity type, but the type in the
response payload represents an entity type. Please ensure that types
defined on the client match the data model of the service, or update
the service reference on the client.
This problem was caused by a version mismatch between project C (which was using the stock implementations on the System.Data.OData assemblies) and the client project B that was calling the service (using the Microsoft.Data.OData assemblies in the packages). By matching the version on both ends, it worked the first time.
After all this, one problem remained though: The service reference procedure is still not detecting the models to be shared, meaning proxies are being created as usual. This led me to opt out of the automatic service integration mechanic, instead forcing me to go forward with a simple class of my own to serve as the client to the Wcf Data service. Basically, it's a heavily trimmed version of the normally autogenerated class:
using System;
using System.Data.Services.Client;
using System.Data.Services.Common;
using Model;
public class DataServiceClient : DataServiceContext
{
private readonly Lazy<DataServiceQuery<Unit>> m_units;
public DataServiceClient(Uri _uri)
: base(_uri, DataServiceProtocolVersion.V3)
{
m_units = new Lazy<DataServiceQuery<Unit>>(() => CreateQuery<Unit>("Units"));
}
public DataServiceQuery<Unit> Units
{
get { return m_units.Value; }
}
}
This is simple enough because I'm only using the service in readonly mode. I would still like to use the service reference feature though, potentially avoiding future maintenance problems, as evidenced by the hardcoded EntitySet name in this simple case. At the moment, I'm using this implementation and have deleted the service reference altogether.
I would really like to see this fully integrated with the service reference approach if anyone can share a workaround to it, but this custom method is acceptable for our current needs.

Entity Framework T4 POCO objects raising exception in WCF

These objects have collections of type ICollection<>
If I pass an object graph from client to server it throws the following exception:
System.NotSupportedException was unhandled by user code
Message=Collection was of a fixed size.
Source=mscorlib
Which is occurs in the fixup code the T4 template has generated. It seems the collections are being deserialized on the server as arrays and so can't be modified. Is there a way to specify the type the serializer should use?
I would strongly recommend that you don't use the POCO classes on your service boundary. Create a separate set of classes to model the data you want to send and receive across the wire (Data Transfer Objects - DTOs) and use a tool like automapper to move data between the DTOs and your POCO classes
Essentially you end up tying the consumers of your service to your service's internal conceptual model which means you become constrained in changing your implementation because you need to avoid breaking your clients
Try using the following attribute
[ServiceKnownType(typeof(List<string>))]
If that doesn't work, perhaps try using IList<T> if that is possible in your situation

How do lazily loaded POCO entities, entity framework and WCF work together?

If a project has used POCO for entities and uses entity framework and uses lazy loading then you have an "incomplete" object graph going back over the wire. So when a client uses the Entity is there some sort of proxy that will automagically load the remaining values? Do we have to create this proxy ourselves and wrap the original entity in it? Or is there an accepted pattern for identifying lazy loaded types which would then signal the client to make another call to the WCF?
Lazy loading with WCF usually doesn't work because your method looks like:
public List<MyPoco> GetData()
{
using (var context = new MyObjectContext())
{
return context.MyPocos.ToList();
}
}
As you see context is closed in method (you have to close context somewhere). But when the list is serialized it will try to lazy load dependent objects => exception because context is already closed. In WCF you should use eager loading.
Use flat DTO's, you probably don't want to expose your full domain to the client anyway. WCF is message-based, not Domain Driven.