InvalidCastException while no compile errors with upcasting - c#

What I'm basically trying to do is Convert Objects I get from a web Service to objects that are compatible with my Entity Framework object. I though of doing this by creating an Interface that both is applied to the web service objects & the EF objects. That way I can easily cast from one type to the other.
At this point I have the following object: A, A1, B and interface IAB.
The problem I'm no facing is when I do an upcast from object A to A1 I get a Run-time error but no compile errors. I would like to know why my upcast isn't accepted?
public class A
{
//Has variables & Properties
}
public class A1 : A, IAB
{
//Has some properties
}
Note: I needed to create A1 as extending the partial class A created Serialization problems for the web service. So this seemed to be the best solution.
When contacting the service I ask for a list of A objects and then want to upcast them to A1. Later I will cast them into B.
I try to cast the objects like this:
List<A1> allA1 = new List<A1>();
foreach (A item in retrievedListOfA)
{
allA1.Add((A1)item);
}
As I don't get any compile errors I find it strange that I get this error. if I do a check of the type "A is A1" then it never goes in that if statement.
Can someone point out to me why this is creating problems? Is it because object A is from a web service?
Note: If this method of "Porting" object from one to another is totally absurd please give me some directions how it should be done. It is the first time I'm attempting something like this.

You don't get any compile errors, because (A1)item, is you saying to the compiler, I know what I'm doing so shut up.
So if retrievedListOfA is a collection of As and A1s, every A you cast to an A1, that is in fact an A and then call an A1 method on will blow up.
There are lots of porting methods, e.g. a converter, casting is not one of them.

Tony Hopkinson's answer is right about the reason you are getting the current error. But, it does not currently address the larger issue you hint at.
Quote: Oxillery
Note: If this method of "Porting" object from one to another is totally absurd
pleasegive me some directions how it should be done. It is the first time I'm attempting
something like this.
It appears that you are receiving a collection of serialized class instances (from a web service), and want to make them into entity framework compatible objects. I believe your issues arise because there is inconsistency between the serialization and deserialization of your instances.
Take the following simple class (our desired Entity Framework entity):
public class Dog
{
/// <summary>
/// The unique ID of this dog, aka the number printed on the dog's implanted microchip.
/// </summary>
[Key]
public int Id { get; set; }
public DateTime Birthday { get; set; }
public string Name { get; set; }
/// <summary>
/// The embarassing number of homeworks shredded.
/// </summary>
public long ShreddedHomeworks { get; set; }
}
Based on your statement, you are receiving from a web service some kind of other object that you want to convert to a Dog. Suppose the class that you are receiving looks like the following:
public class PseudoDog
{
public DateTime Birthday { get; set; }
public string Name { get; set; }
public int Legs { get; set; }
}
Notice that that the two classes are not directly compatible in terms of serializing a PseudoDog and deserializing it as a Dog. I purposely made the PseudoDog have 'Legs' as a property and Dog have 'ShreddedHomeworks' so that without manipulation, they cannot be converted.
The basis for how they can be converted is from the Web API tutorial about data transfer objects (DTO's) and their use. Continuing the example above, the class used to convert a PseudoDog to a Dog would look like the following:
using System.Runtime.Serialization;
[DataContract]
public class DogDTO
{
#region Static Methods
public static Dog ToDog(DogDTO dto)
{
// if the data transfer object is null
// we cannot make a Dog from it.
if (dto == null)
{
return null;
}
Dog dog = new Dog();
// We can be sure of what these values are intended to be.
dog.Name = dto.Name;
dog.Birthday = dto.Birthday;
return dog;
}
public static DogDTO CreateFrom(Dog dog)
{
if (dog == null)
{
return null;
}
DogDTO dto = new DogDTO();
dto.Name = dog.Name;
dto.Birthday = dog.Birthday;
}
#endregion
[DataMember]
public DateTime Birthday { get; set; }
[DataMember]
public string Name { get; set; }
}
The juicy bits of DogDTO are two properties at the bottom (Birthday and Name) which have the DataMember attribute. This means that the DogDTO class can be serialized and deserialized with the DataContractSerializer.
Putting all the pieces together, the entire process looks like this:
Some client makes a PseudoDog instance named 'Fido'
The client serializes 'Fido' (to XML, JSON, binary, etc...) to file named 'serialdog.xml'.
The client sends the webservice the 'serialdog.xml' file.
The webservice that receives 'serialdog.xml' deserializes it into a DogDTO instance (named 'FidoDTO')
Your method calls DogDTO.ToDog(FidoDTO) which returns a new Dog instance named 'myFido'.
You can now use the Dog instance 'myFido' as it is an Entity Framework entity.
Succintly, conversion goes like this:
Web Service object --> object DTO --> Entity Framework compliant object
Under the following assumptions that
Type A - the Type of the instance from web service
Type A1 - the Type of your current conversion instance
Type B - the Type of the instance used by Entity Framework
and
Type A_DTO - the Type of the data transfer object
You should make the following changes wherever it is you have this code:
List<A1> allA1 = new List<A1>();
foreach (A item in retrievedListOfA)
{
allA1.Add((A1)item);
}
I would replace it with this:
List<B> allB = new List<B>();
foreach (A_DTO dto in retrievedListofA_DTO)
{
allB.Add(A_DTO.ToB(dto));
}

Related

Create and use covariant and mutable list (or potential workaround)

I'm currently modifying a Blazor library and the souce code of the current state is available on gitlab.
My situation is as follows:
I have a LineChartData object which is supposed to store multiple Datasets for LineCharts.
These Datasets intern have a List of Data. Instead of just working with List<object> I wanted to be able to have List<TData>.
Because there is a Mixed Chart which can accept both LineChartDatasets and BarChartDatasets, there is an interface called IMixableDataset.
I started by making this interface generic so it now looks like this (simplified):
public interface IMixableDataset<TData>
{
List<TData> Data { get; }
}
I then made my implementing class (LineChartDataset) generic as well and it now looks like this (simplified):
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public List<TData> Data { get; }
}
Next up was LineChartData. I first made this generic as well and continued with that until I reached the top level (see current state of my master branch). However I later wanted to change this because I wanted to support multiple Datasets with different kind of values. For this reason I reverted the generic stuff in all classes "above" the Datasets and the LineChartData now looks like this (simplified):
public class LineChartData
{
// HashSet to avoid duplicates
public HashSet<LineChartDataset<object>> Datasets { get; }
}
I decided to go with LineChartDataset<object> because: Since everything is castable to object, (in my mind) XYZ<Whatever> should also be castable to XYZ<object> but as I learned, this is not the case.
The where keyword didn't help either since I don't want to enforce TData to have relations apart from object - it could be int, string or something completely different. The only relation these LineDatasets are supposed to have is that they are LineDatasets, not what type they contain.
I then learned about Covariance and Contravariance (out and in-keyword). I tried out to make TData in IMixableDataset covariant but since List and IList/ICollection are all invariant I was unable to persue.
I also read about IReadOnlyCollection<> which is covariant but I cannot use this because I have to be able to modify the list after creation.
I have also tried using implicit/explicit operators to convert LineChartDataset<whatever> to LineChartDataset<object> but this has a few issues:
Since I created a new instance, I would need to store and use the new instance instead of the original one to add items, completely destroying the typesafety I had with the original one.
Since there are many more properties in LineChartDataset I would have to clone all of them as well.
If there is a way to convert a more specific one to the other while preserving the instance and not having to write code for every property this might be a solution.
Complete sample which reproduces the error I get and shows the issue:
// Provides access to some Data of a certain Type for multiple Charts
public interface IMixableDataset<TData>
{
List<TData> Data { get; }
}
// Contains Data of a certain Type (and more) for a Line-Chart
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public List<TData> Data { get; } = new List<TData>();
}
// Contains Datasets (and more) for a Line-Chart
// This class should not be generic since I don't want to restrict what values the Datasets have.
// I only want to ensure that each Dataset intern only has one type of data.
public class LineChartData
{
// HashSet to avoid duplicates and Public because it has to be serialized by JSON.Net
public HashSet<LineChartDataset<object>> Datasets { get; } = new HashSet<LineChartDataset<object>>();
}
// Contains the ChartData (with all the Datasets) and more
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
LineChartDataset<int> intDataset = new LineChartDataset<int>();
intDataset.Data.AddRange(new[] { 1, 2, 3, 4, 5 });
config.ChartData.Datasets.Add(intDataset);
// the above line yields following compiler error:
// cannot convert from 'Demo.LineChartDataset<int>' to 'Demo.LineChartDataset<object>'
// the config will then get serialized to json and used to invoke some javascript
}
public void WorkingButBadUseCase()
{
LineChartConfig config = new LineChartConfig();
LineChartDataset<object> intDataset = new LineChartDataset<object>();
// this allows mixed data which is exactly what I'm trying to prevent
intDataset.Data.AddRange(new object[] { 1, 2.9, 3, 4, 5, "oops there's a string" });
config.ChartData.Datasets.Add(intDataset); // <-- No compiler error
// the config will then get serialized to json and used to invoke some javascript
}
}
The reason everything only has getters is because of my initial attempt with using out. Even thought this didn't work out, I learned that you usually don't expose Setters for Collection-properties. This is not fix and also not very important for the question but I think worth mentioning.
Second complete example. Here I'm using out and an IReadOnlyCollection. I have removed the descriptions of the class (already visible in the previous example) to make it shorter.
public interface IMixableDataset<out TData>
{
IReadOnlyCollection<TData> Data { get; }
}
public class LineChartDataset<TData> : IMixableDataset<TData>
{
public IReadOnlyCollection<TData> Data { get; } = new List<TData>();
}
public class LineChartData
{
public HashSet<IMixableDataset<object>> Datasets { get; } = new HashSet<IMixableDataset<object>>();
}
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
IMixableDataset<int> intDataset = new LineChartDataset<int>();
// since it's ReadOnly, I of course can't add anything so this yields a compiler error.
// For my use case, I do need to be able to add items to the list.
intDataset.Data.AddRange(new[] { 1, 2, 3, 4, 5 });
config.ChartData.Datasets.Add(intDataset);
// the above line yields following compiler error (which fairly surprised me because I thought I correctly used out):
// cannot convert from 'Demo.IMixableDataset<int>' to 'Demo.IMixableDataset<object>'
}
}
So the question:
Is there anyway to have a mutable and covariant collection?
If not, is there a workaround or something I can do to achieve this functionality?
Additional stuff:
I'm using the newest version of everything (.net core, VS, blazor, C#). Since the library is .NET Standard I'm still on C# 7.3 there.
In the repo under WebCore/Pages/FetchData you can perfectly see what I want to achieve (see comments at the end of the file).
Looking more closely at your example, I see one major problem: you are attempting to involve value types (e.g. int) in type variance. For better or worse, C# type variance applies only to reference types.
So, no…sorry, but it is quite impossible to do exactly what you're asking. You would have to represent all value-type based collections as object, not as their specific value types.
Now, as far as reference-type collections go, your example will work fine, with one minor change. Here's a modified version of your second example showing it working, with that one minor change:
public interface IMixableDataset<out TData>
{
IReadOnlyCollection<TData> Data { get; }
}
public class LineChartDataset<TData> : IMixableDataset<TData>
{
private readonly List<TData> _list = new List<TData>();
public IReadOnlyCollection<TData> Data => _list;
public void AddRange(IEnumerable<TData> collection) => _list.AddRange(collection);
}
public class LineChartData
{
public HashSet<IMixableDataset<object>> Datasets { get; } = new HashSet<IMixableDataset<object>>();
}
public class LineChartConfig
{
public LineChartData ChartData { get; } = new LineChartData();
}
public class Demo
{
public void DesiredUseCase()
{
LineChartConfig config = new LineChartConfig();
// Must use reference types to take advantage of type variance in C#
LineChartDataset<string> intDataset = new LineChartDataset<string>();
// Using the non-interface method to add the range, you can still mutate the object
intDataset.AddRange(new[] { "1", "2", "3", "4", "5" });
// Your original code works fine when reference types are used
config.ChartData.Datasets.Add(intDataset);
}
}
In particular, note that I've added an AddRange() method to your LineChartDataset<TData> class. This provides a type-safe way to mutate the collection. Note that the code that wants to mutate the collection must know the correct type, bypassing the variance restrictions.
The variant interface IMixableDataset<TData> itself cannot, of course, include a way to add things, because this would not be type-safe. You would be able to treat your LineChartDataset<string> as a IMixableDataset<object>, and then if you could add things via that interface, you'd be able to add some other type of object, even a non-reference type like a boxed int value, to your collection that's supposed to only contain string objects.
But, just as the invariant List<T> can implement the covariant IReadOnlyCollection<T>, your concrete LineChartDataset<TData> class can implement IMixableDataset<TData> while still providing a mechanism for adding items. This works because while the concrete type determines what the object can actually do, the interfaces simply define a contract that users of the reference must abide by, allowing the compiler to ensure type safety where the interface is used, even when used in a variant way. (The invariant concrete type ensures type safety as well, but only because the type has to match exactly, which is of course more restrictive/less flexible.)
If you don't mind using object in place of any specific value type for the value-type-based collections, then the above would work. It's a bit clumsy, since any time you actually want to get the value type values out, you'd need to retrieve them as object and then cast as necessary to actually use them. But at least the broader variant approach would then succeed, and no special handling would be required for any reference types.
Aside: that type variance in C# is restricted to reference types is based on the pragmatic requirement that type variance doesn't affect the runtime code. It's just a compile-time type-conversion. This means that you have to be able to just copy references around. To support value types would require adding new boxing and unboxing logic where it otherwise wouldn't exist. It's also not quite as useful, because value types don't have the same rich degree of type inheritance that reference types can have (value types can only ever inherit object, so variant scenarios are much less useful and interesting, in general).

Jil.DeserializationException : Error occurred building a deserializer in redis cache

I'm using Redis Cache using Stack Exchange library.
I used cloudStructure library to use Redis Dictionary and Redis List.
Problem is when I try to retrieve values and if that model has a null
value for one list property it is throwing me below exception -
Jil.DeserializationException : Error occurred building a deserializer
for TestMainClass: Expected a
parameterless constructor for
System.Collections.Generic.ICollection1[TestChildClass]
---- Jil.Common.ConstructionException : Expected a parameterless constructor for
System.Collections.Generic.ICollection1[TestChildClass]
public class TestMainClass
{
public TestMainClass();
public int Id { get; set; }
public virtual ICollection<TestChildClass> Mydata { get; set; }
public string Title { get; set; }
}
public class TestChildClass
{
public TestChildClass();
public int Id { get; set; }
public string Value { get; set; }
}
Redis code for retrieve value:
RedisDictionary<int, TestMainClass> dictionary =
new RedisDictionary<int, TestMainClass>("localhost", "mylocaldictionary");
var result = await dictionary.Get(121);
What If I could not able to convert ICollection < T > into List < T >?
It might be a nice feature if the serialization library detected interfaces like ICollection<T> and IList<T> and implemented them with the concrete List<T> during deserialization, but ultimately: every feature needs to be thought of, considered (impact), designed, implemented, tested, documented and supported. It may be that the library author feels this is a great idea and should be implemented; it might not be high on the author's list, but they'd be more than happy to take a pull request; or there might be good reasons not to implement it.
In the interim, as a general rule that will solve virtually every serialization problem you will ever encounter with any library:
the moment the library doesn't work perfectly with your domain model: stop serializing your domain model - use a DTO instead
By which, I mean: create a separate class or classes that are designed with the specific choice of serializer in mind. If it wants List<T>: then use List<T>. If it wants public fields: use public fields. If it wants the types to be marked [Serializable]: mark the types [Serializable]. If it wants all type names to start with SuperMagic: then start the type name with SuperMagic. As soon as you divorce the domain model from the serialization model, all the problems go away. In addition: you can support multiple serializers in parallel, without getting into the scenario that A needs X and doesn't work with Y; B needs Y and doesn't work with X.
All you then need to do is write a few lines of code to map between the two similar models (or use libraries that do exactly that, like AutoMapper).

Property of generic type in non-generic class

Intro
I'm working with the legacy code which contains two classes:
I have a class which stores its value of System.Object type.
(I named this class as DomainItem)
Its Identifier property refers to
enum which holds information what a type of DomainItem is (in the
context of business domain).
There is also a class which stores these
items as an Enumerable List. (DomainItems)
What's more:
I don't want to change these classes into generic. This code is very sensitive and not covered by tests.
In order to get DomainItem, I must get it from DomainItems.Items collection.
Code
The code for classes is equivalent as below:
public class DomainItem
{
public Identifier Identifier { get; set; } // Readonly in the "real" code
public object Value { get; set; }
}
public class DomainItems
{
public IEnumerable<DomainItem> Items { get; set; }
}
The question is
How can I extend these classes using generics, to resolve type of Value property in the compile time. Is it even possible?
Example case might be as following:
DomainItem price = new DomainItem { Value = 25.20d, Identifier = Identifier.Price };
// ....
double priceValue = price.ProperValue; // generic property of type T
Obviously, above code is conceptual and it shows what I want to achieve. Any suggestions how to resolve that? Is it even possible?
Edit
My idea is to create a new IEnumerable<DomainItem<T>> where the collection is populated from non-generic DomainItem objects. Since the type of DomainItem.Value is known, it should be possible to make such collection somehow.
There's no such thing as a generic property, but you could easily create a generic method:
public T GetValue<T>() { ... }
public void SetValue<T>(T value) { ... }
You could then check typeof(T) within the method to make sure that it was appropriate for your identifier, ideally having made the identifier read-only. (It would be better as a constructor argument - I wouldn't expect it to make any sense to have a domain item whose identifier changed over time.)
Alternatively, you could just make the type of the Value property dynamic instead of object, assuming you're using C# 4+ with .NET 4+. Then your example code would compile - but it would perform an implicit (dynamic) conversion to double at execution time. You wouldn't get much safety there, but it would compile...

Dynamic return type for .NET Method

I have a web method which calls a method in the DAL to execute a procedure by id and returns an object of type MyData.
[WebMethod]
public MyData GetDataById(int id)
{
DAL myDAL = new DAL();
return myDAL.GetDataById(id);
}
The class MyData looks like the following
public class MyData
{
public string Name;
public Data[] DataItems;
}
and the class Data,
public class Data
{
public string key;
public string value;
}
Now this worked fine for until we wanted to return a bit of complex types.
For an example, a DataTable, or perhaps something like a structure (or class) containing latitude, longitude and a value. So obviously, class Data cannot hold multiple values or a DataTable. (It contains two strings, key and value).
So what I actually want is the type to be Generic. So I changed it to...
public class MyData<T>
{
public string Name;
public T[] DataItems;
}
So I'll be doing something like this inside the myDAL.GetDataById because what type of data is returned differs according to the type.
if (GetTypeOfId(id) == "NormalData")
{
MyData<Data> result = new MyData<Data>();
}
else if (GetTypeOfId(id) == "Map")
{
MyData<MapData> result = new MyData<MapData>();
}
But I need to specify a type for the method signature as well which unfortunately is found out ONLY at run time.
How do I handle such a situation ?
I feel like I am using Generics for something I shouldn't be using it for.
Or how is usually a situation where the type is found out only at run time resolved?
UPDATE: The worst case scenario is having different web service calls for getting normal data and map data which I would like to avoid.
You can only use generics in circumstances where you can specify the type at compile time, so I don't think they're suitable for your situation.
I would recommend having two web service calls, that's the easiest way to handle this.
If you really want only one web service call you could have your web method simply return a byte[] (created using BinaryFormatter) or a string (created using, say, an XmlSerializer) and then deserialise that on your client side, but it means you have to have the same classes on both sides (or you have to have custom deserialisation code) and your web service can't easily be consumed by multiple clients.
I'm not sure what you are asking for but i have a feeling that using an interface with a generic method could help you pass return values around and call the GetValue generic method on that.
If you use IData instead of a generic type as return value you will be able to extract the embedded data with ret.GetValue<MyData>(). And if you set the Type when creating the Data<T> you will be able to query on that too.
Maybe you can adapt the following snippet to your needs.
public interface IData {
type Type { get; set; }
string Name { get; set; }
T GetValue<T>();
}
public class Data<T> : IData
{
public Type Type { get; set; }
public string Name { get; set; }
public T Value { get; set; }
public Tret GetValue<Tret>() {
return (Tret)(Object)Value;
}
}
The answer was pretty easy and stupid of me not to think of it before.
I just created a parent class and made the Data and all the other required classes inherit from it.

2 objects, exactly the same (except namespace) c#

I'm using a 3rd party's set of webservices, and I've hit a small snag. Before I manually make a method copying each property from the source to the destination, I thought I'd ask here for a better solution.
I've got 2 objects, one of type Customer.CustomerParty and one of type Appointment.CustomerParty. The CustomerParty objects are actually property and sub-oject exactly the same. But I can't cast from 1 to the other.
So, I need to find a certain person from the webservice. I can do that by calling Customer.FindCustomer(customerID) and it returns a Customer.CustomerParty object.
I need to take that person that I found and then use them a few lines down in a "CreateAppointment" request. Appointment.CreateAppointment takes an appointment object, and the appointment object contains a CustomerParty object.
However, the CustomerParty object it wants is really Appointment.CustomerParty. I've got a Customer.CustomerParty.
See what I mean? Any suggestions?
Why don't you use AutoMapper? Then you can do:
TheirCustomerPartyClass source = WebService.ItsPartyTime();
YourCustomerPartyClass converted =
Mapper.Map<TheirCustomerPartyClass, YourCustomerPartyClass>(source);
TheirCustomerPartyClass original =
Mapper.Map<YourCustomerPartyClass, TheirCustomerPartyClass>(converted);
As long as the properties are identical, you can create a really simple map like this:
Mapper.CreateMap<TheirCustomerPartyClass, YourCustomerPartyClass>();
Mapper.CreateMap<YourCustomerPartyClass, TheirCustomerPartyClass>();
This scenario is common when writing domain patterns. You essentially need to write a domain translator between the two objects. You can do this several ways, but I recommend having an overridden constructor (or a static method) in the target type that takes the service type and performs the mapping. Since they are two CLR types, you cannot directly cast from one to the other. You need to copy member-by-member.
public class ClientType
{
public string FieldOne { get; set; }
public string FieldTwo { get; set; }
public ClientType()
{
}
public ClientType( ServiceType serviceType )
{
this.FieldOne = serviceType.FieldOne;
this.FieldTwo = serviceType.FieldTwo;
}
}
Or
public static class DomainTranslator
{
public static ServiceType Translate( ClientType type )
{
return new ServiceType { FieldOne = type.FieldOne, FieldTwo = type.FieldTwo };
}
}
I'm using a 3rd party's set of
webservices...
Assuming you can't modify the classes, I'm not aware of any way you can change the casting behavior. At least, no way that isn't far, far more complicated than just writing a CustomerToAppointmentPartyTranslator() mapping function... :)
Assuming you're on a recent version of C# (3.5, I believe?), this might be a good candidate for an extension method.
Have you looked at adding a conversion operator to one of the domain classes to define an explicit cast. See the msdn documentation here.
Enjoy!
A simple and very fast way of mapping the types is using the PropertyCopy<TTarget>.CopyFrom<TSource>(TSource source)
method from the MiscUtil library as described here:
using MiscUtil.Reflection;
class A
{
public int Foo { get; set; }
}
class B
{
public int Foo { get; set; }
}
class Program
{
static void Main()
{
A a = new A();
a.Foo = 17;
B b = PropertyCopy<B>.CopyFrom(a);
bool success = b.Foo == 17; // success is true;
}
}
Two classes with exactly the same signature, in two different namespaces, are two different classes. You will not be able to implicitly convert between them if they do not explicitly state how they can be converted from one to the other using implicit or explicit operators.
There are some things you may be able to do with serialization. WCF DataContract classes on one side do not have to be the exact same type as the DataContract on the other side; they just have to have the same signature and be decorated identically. If this is true for your two objects, you can use a DataContractSerializer to "convert" the types through their DataContract decoration.
If you have control over the implementation of one class or the other, you can also define an implicit or explicit operator that will define how the other class can be converted to yours. This will probably simply return a new reference of a deep copy of the other object in your type. Because this is the case, I would define it as explicit, to make sure the conversion is only performed when you NEED it (it will be used in cases when you explicitly cast, such as myAppCustomer = (Appointment.CustomerParty)myCustCustomer;).
Even if you don't control either class, you can write an extension method, or a third class, that will perform this conversion.

Categories

Resources