Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow 1 CLR type to be used for multiple EDM types. #1634

Open
Danielku15 opened this issue Oct 4, 2018 · 0 comments
Open

Allow 1 CLR type to be used for multiple EDM types. #1634

Danielku15 opened this issue Oct 4, 2018 · 0 comments

Comments

@Danielku15
Copy link
Contributor

Danielku15 commented Oct 4, 2018

This is a feature request that resulted from my investigations done in #1629. As I am currently working on a prototype of a new API I will likely attempt to adapt parts of the framework to implement this feature request until some extend. I hope the team working on this project can give some feedback if such a change to the library is something you want to follow up.

Introduction

Currently internally the framework assumes that 1 CLR type will only be used to provide 1 EDM type. All mappings happen on a 1:1 basis but this might not always be the case. If you use a "sparse column" system as alternative to a Entity-Attribute-Value model, 1 table in the database (e.g. Entity) might contain different type of entites. In this case you might end up with 2 instances of the Entity class representing different kinds of semantic models.

As many of you might know, an EAV system is a nightmare to operate (development, performance, and maintenance wise). A data model defining general-purpose columns is also not the golden solution but solves many problems at the cost of lacking normalization. Many people sooner or later come to the point where such a requirement needs to be realized, but not many frameworks have the required features to realize this in a good manner.

Use Case

Lets assume you define a entity which has a set of general purpose string and int properties that are filled depending on your entity definitions. And one property defining what kind of entity you have. In a real world application the entity type would actually be a DB level definition of the entity.

On DB level you might have:

Id EntityType StringValue1 IntValue1
1 Person Daniel 28
2 Building Building 04 4

Which would be represented by objects:

new Entity {
    Id = 1, 
    EntityType = "Person",
    StringValue1 = "Daniel",
    IntValue1 = 28
},
new Entity {
    Id = 2,
    EntityType = "Building",
    StringValue1 = "Building 04",
    IntValue1 = 4
}

On API those would actually be 2 different EDM types

<EntityType Name="Person">
	<Key>
		<PropertyRef Name="Id"/>
	</Key>
	<Property Name="Id" Type="Edm.Int32" Nullable="false"/>
	<Property Name="Name" Type="Edm.String" Nullable="false"/>
	<Property Name="Age" Type="Edm.Int32" Nullable="false"/>
</EntityType>
<EntityType Name="Building">
	<Key>
		<PropertyRef Name="Id"/>
	</Key>
	<Property Name="Id" Type="Edm.Int32" Nullable="false"/>
	<Property Name="Name" Type="Edm.String" Nullable="false"/>
	<Property Name="Levels" Type="Edm.Int32" Nullable="false"/>
</EntityType>

Proposal

The framework needs a possibility where an instance can control what kind of EDM type it actually is. Theoretically the IEdmObject would define a GetEdmType which allows an instance of a class to provide the underlying IEdmType. But this interface is actually used in different ways. It switches the whole OData framework into a untyped mode where a lot of functionality is lost.

Instead the framework should provide some systems where on collections and on instances the developer can tell what EdmType is represented by the contents. Several places in the code are based on the raw CLR types without considering that instances might not directly map to a EDM type by their type. Just to name a few:

  • Microsoft.AspNet.OData.Formatter.ClrTypeCache
  • EdmLibHelpers.GetEdmType
  • EnableQuery.GetElementType
  • ODataSetResourceSerializer
  • ODataResourceSerializer

At the places where a EDM type is resolved for a CLR type (or vice versa) e.g. the IEdmModel should be asked to translate it to a EdmType. Currently a lot of this functionality is implemented in internal extension methods (mostly in EdmLibHelpers) and can not be changed/extended. Also, where possible, rather the actual object should be used for the translation than its type loaded via GetType(). A custom IEdmModel implementation (or a special one provided by the framework) can then do the correct translation between CLR objects/types and EDM types.

A EDM model construction could look like this:

var personType = conventionModelBuilder.AddEntityType<Entity>("Person"); // name is already required on adding to support multiple registrations
personType.Namespace = "My.Models";
personType.IdentifiedBy( x => x.EntityType == "Person" );
personType.ComplexProperty( x => x.String1 ).Name = "Name";
personType.ComplexProperty( x => x.Int1 ).Name = "Age";
var buildingType = conventionModelBuilder.AddEntityType<Entity>("Building"); 
buildingType.Namespace = "My.Models";
buildingType.IdentifiedBy( x => x.EntityType == "Building");
buildingType.ComplexProperty( x => x.String1 ).Name = "Name";
buildingType.ComplexProperty( x => x.Int1 ).Name = "Levels";

For queryables/enumerables there could be a extension method which creates a "wrapper" IQueryable which annotates the original query with the edm types contained. This would allow polymorphic collections but also on strictly type collections the base type is defined.

[EnableQuery]
public IQueryable<Entity> GetPeople()
{
	return _dbContext.Entites.Where(e=>e.EntityType == "Person")
		.WithEdmElementType(Request.GetModel().FindType("My.Models.Person")) 
	;
}

That should give a first basic picture. A final implementation will likely need way more adjustments to ensure the mappings and object constructions work as expected everywhere. With this mapping in place also all other functionality ($filter, $select etc.) will work via the normal property aliasing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant