Casting between Derived Models and their Base

Posts   
 
    
Posts: 37
Joined: 09-Nov-2016
# Posted on: 12-Jul-2018 12:32:44   

Hi,

I am using LLBLGen 5.4 and LLBLGen Runtime Pro Framework in a .NET Standard 2.0 Class Library.

I have created a Model which looks something like this:

public class Car
{
    public int Id { get; set; }

    public Door Door { get; set; }
}

public class Door
{
    public int Id { get; set; }

    public string Name { get; set; }
}

I have created two Derived Models (DTO Class Models), CarContract (which contains the Door) and DoorContract. However the Door in CarContract is not of type DoorContract, but of type Contract.DtoClasses.CarContractTypes.Door.

This means that when I need to create a new CarContract it would look something like this:

var car = new CarContract
{
    Id = 1,
    Door = Contract.DtoClasses.CarContractTypes.Door { Id = 1, Name = "Door" }
}

and not like I would expect:

var car = new CarContract
{
    Id = 1,
    Door = new DoorContract { Id = 1, Name = "Door" }
}

In my case I get a list of cars and I need to check if the doors are already created. This means I need to convert each Door in Car to DoorContract. I have tried something like this:

var doors = cars.Select(x => x.Door).Cast<DoorContract>().ToList();

However I get this exception:

System.InvalidCastException: 'Unable to cast object of type 'Contract.DtoClasses.CarContractTypes.Door' to type 'Contract.DtoClasses.Door'.'

Is there an automatic way to convert between the two or somehow link the two in the Derived Model?

Best regards, Andreas

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 12-Jul-2018 22:05:24   

Why do you create 2 classes, Car and CarContract? What's the difference?

Same question for Door and DoorContract

Posts: 37
Joined: 09-Nov-2016
# Posted on: 13-Jul-2018 09:37:12   

The Car is the standard LLBLGen Entity. In my program, I have to determine if the Car already exists or not. The program is running as a Service, so it is constantly being asked to insert a Car if it is not already there. I have to do many of these checks so it is not feasible to check the database, which is in the cloud, so there is alot of latency per lookup.

I keep an in-memory dictionary of all Cars and for the key, I use a Hash calculated from the relevant properties of the Car (so I remove the Id and FK's). I cannot calculate a Hash from the regular Car Entity (as this contains a Guid) and generally do not serialize as well as a Derived Model (as it contains more than just the properties).

I did a simple speedtest and it took under a second to calculate the hash for 100000 objects and check the dictionary for 10000 of them. Using the same data it took an in-memory representation (a List) around 15 seconds and using a database (localhost, so without the extra network latency) it took around 18 seconds.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 13-Jul-2018 11:34:33   

An entity class has its GetHashCode() overridden to create the hashcode from the PK, so you don't need to do anything to get a proper hashcode from an entity instance I think. (so you can simply use a HashSet<CarEntity>() to store the ones which have been inserted, or better, keep a list of IDs perhaps?)

In a derived model, a nested type is always unique to the containing type, so if you have two derived elements D1 and D2 and both contain derived type from the entity E then D1.DE is different from D2.DE even though they look the same. So you can't cast D1.DE to D2.DE as they're different types.

So if I understand you correctly the derived model instances is what you get and you want to convert them into entity instances to process them further? the only connection between them at runtime is the ID values. But perhaps I misunderstood you...

Frans Bouma | Lead developer LLBLGen Pro
Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 13-Jul-2018 11:38:14   

If you have a Derived Model for Customer, so Customer is the Root Derived Element, then you want to embed Orders, you get what is called an Embedded Derived Element. Which in fact is a definition of a type (class) in the same file and under a namespace named after the Root Derived Element.

So you get

DtoClasses.CustomerDto DtoClasses.CustomerDtoTypes.Order

The later "Order" is only used within the Customer Dto, and has nothing to do with any other Derived Element, in case you have an OrderDto Root Element.

A DTO is a self contained, and customized as per your need, for example, a developer might need to have a DTO for Order will all the fields available. But at the Same time, within the DTO of the Custom, you want to embed an Order with a subset of the fields. That's why a Root Element (OrderDto) has nothing to do with an Embedded Element (Order defined within the Customer DTO).

Posts: 37
Joined: 09-Nov-2016
# Posted on: 13-Jul-2018 12:10:49   

Thank you for your replies simple_smile

An entity class has its GetHashCode() overridden to create the hashcode from the PK, so you don't need to do anything to get a proper hashcode from an entity instance I think. (so you can simply use a HashSet<CarEntity>() to store the ones which have been inserted, or better, keep a list of IDs perhaps?)

Yes but if you already have the PK, then you know it is in the database (whether or not it is changed is a different matter) and you only know this by doing a lookup (if I understand you correctly).

If you create the hash by only using the properties of an object, you can check for its existence in a Dictionary by its properties alone. The Dictionary is defined like this:

public Dictionary<string, CarEntity> CarDictionary { get; set; }

The key is the result of a Hash computation where the input is the CarContract. The consumer of the program containing the Dictionary (i.e. DataStore) wants to insert a Car. The only thing the consumer knows are the properties of the Car, so it creates a CarContract with those properties and sends this information to the DataStore. The DataStore computes the Hash of the CarContract, if the Car is already there, it does nothing, if not it creates a CarEntity from the CarContract, inserts it in both the database and the DataStore. There is only a database operation if it is not there (which is fair enough simple_smile ).

Since a 'Contract.DtoClasses.CarContractTypes.Car' and 'Contract.DtoClasses.Car' contain the same properties I was hoping there would be a way to Cast them, as I manually have to map them now and with so many great options in LLBLGen, I thought I might ask, as I still consider my LLBLGen skills to be Intermediate, so I might have missed it simple_smile

Whether or not the above approach is mad, remains to be seen.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 16-Jul-2018 09:16:55   

Jordbaerhjelmen wrote:

Thank you for your replies simple_smile

An entity class has its GetHashCode() overridden to create the hashcode from the PK, so you don't need to do anything to get a proper hashcode from an entity instance I think. (so you can simply use a HashSet<CarEntity>() to store the ones which have been inserted, or better, keep a list of IDs perhaps?)

Yes but if you already have the PK, then you know it is in the database (whether or not it is changed is a different matter) and you only know this by doing a lookup (if I understand you correctly).

If you create the hash by only using the properties of an object, you can check for its existence in a Dictionary by its properties alone.

But a hash from the properties doesn't make it unique, that's why identifying fields are identifying fields, i.e. the PK. All other fields / attributes can be duplicates of one another in some form.

So what you're doing is basically having a second database where the hash of the field values forms another PK and which is used to determine whether a potentially new entity is indeed 'new' or not. I don't think that's very reliable nor correct. (reliable as in: it's not consistent at all with your DB due to latency etc., correct as in: is a new entity with the same property values really a duplicate? I know this sounds philosophical but it's a point you have to address)

The only thing the consumer knows are the properties of the Car, so it creates a CarContract with those properties and sends this information to the DataStore. The DataStore computes the Hash of the CarContract, if the Car is already there, it does nothing, if not it creates a CarEntity from the CarContract, inserts it in both the database and the DataStore. There is only a database operation if it is not there (which is fair enough simple_smile ).

Since a 'Contract.DtoClasses.CarContractTypes.Car' and 'Contract.DtoClasses.Car' contain the same properties I was hoping there would be a way to Cast them, as I manually have to map them now and with so many great options in LLBLGen, I thought I might ask, as I still consider my LLBLGen skills to be Intermediate, so I might have missed it simple_smile

Whether or not the above approach is mad, remains to be seen.

You can't cast them as they're different types, despite the fact they have the same shape (at the moment!). As they're different types/elements they can take different shapes over time, which is different from entities. Your problem isn't new, others have expressed the same desire (i.e. a basic 1:1 DTO reflection of the Entity model so e.g. your Car entity will be reflected as a Car DTO and everywhere where you use the 'Car' entity the CarDTO will be used so in that light you could cast (as it's the same type), but alas that's not how derived models work at the moment.

Not sure if you have many types like this, but if not I'd write an extension method which created a new copy of CarContractTypes.Car with the values of DtoClasses.Car so it's easy to 'cast' (which in this case would be a copy). If you have many types, it might be 'best' (I know it's not ideal hence 'best' between quotes wink ) to generate these methods with a template.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 37
Joined: 09-Nov-2016
# Posted on: 16-Jul-2018 11:35:22   

But a hash from the properties doesn't make it unique, that's why identifying fields are identifying fields, i.e. the PK. All other fields / attributes can be duplicates of one another in some form.

No exactly, I don't want it to be unique, I want to know if the same entity is in the database based on it's values. I am merging 8 different data sources into a single data source each containing the same types of entities with variations. Before I transfer the entity I need to know if it is already there in the database. The problem is that each entity is not a simple entity, but consists of multiple relationships, so to check if the entity is already in the database, requires a lot of joins, which takes too long disappointed

So what you're doing is basically having a second database where the hash of the field values forms another PK and which is used to determine whether a potentially new entity is indeed 'new' or not. I don't think that's very reliable nor correct. (reliable as in: it's not consistent at all with your DB due to latency etc., correct as in: is a new entity with the same property values really a duplicate? I know this sounds philosophical but it's a point you have to address)

I agree with you. It’s only because of the domain it will work. 7 of the data sources are one-time transfers, where the database is offline to users and the last is a real-time transfer, but only that program alter the data, so reliability in the sense you defined should not be a problem. With regards to correctness, the 8 data sources are within the same domain, and one of the goals is to merge records. There is, as you correctly state, a very philosophical question: If you have two John Does in the database, are they the same? In this case we don’t know and will probably never find out, since we have so little information. If we don’t merge the two, then the user will have to pick between two identical John Does.

Not sure if you have many types like this, but if not I'd write an extension method which created a new copy of CarContractTypes.Car with the values of DtoClasses.Car so it's easy to 'cast' (which in this case would be a copy). If you have many types, it might be 'best' (I know it's not ideal hence 'best' between quotes ) to generate these methods with a template.

Ahhh yes. Haven’t thought about that. Excellent idea, thank you simple_smile