I have an interesting issue today!! Basically I have two classes.
public class A : B
{
public virtual new ISet<DifferentItem> Items {get;set;}
}
public class B
{
public virtual int Id {get;set;}
public virtual ISet<Item> Items {get;set;}
}
The subclass A hides the base class B property, Items and replaces it with a new property with the same name and a different type.
The mappings for these classes are
public class AMapping : SubclassMap<A>
{
public AMapping()
{
HasMany(x=>x.Items)
.LazyLoad()
.AsSet();
}
}
public class BMapping : ClassMap<B>
{
public BMapping()
{
Id(x=>x.Id);
HasMany(x=>x.Items)
.LazyLoad()
.AsSet();
}
}
However when I run my unit test to check the mapping I get the following exception:
Tests the A mapping: NHibernate.PropertyAccessException : Invalid Cast (check your mapping for property type mismatches); setter of A
----> System.InvalidCastException : Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericSet1[Item]' to type 'Iesi.Collections.Generic.ISet1[DifferentItem]'.
Anyone have any ideas?
Clearly it is something to do with the type of the collection on the sub-class. But I skimmed through the available options on the mapping class and nothing stood out as being the solution here.
Generics in c# does not support covariance, so essentially you can't have ISet<Item> and ISet<DifferentItem>. Since it's a limitation of the language you need to rethink your design. Or wait til c# 6.
Related
TLDR: Is there anyway to override an abstract property and change it's return type at the same time, just like you can do with concrete properties? I know the direct answer is "no", but I'm wondering if there's any workaround that doesn't involve adding a second property.
Let's say I have this abstract class:
public abstract class Item {
public abstract string SerialNumber { get; }
public string PartNumber => "34";
}
Let's say I want a Toy class that implements Item. Let's also say for some reason I want the SerialNumber and PartNumber properties to both be int values in the child class even though they are strings on the base. See below:
public abstract class Toy {
public override int SerialNumber => 5; //Doesn't compile obviously
public new int PartNumber => int.ParseInt(base.PartNumber); //works fine, refines the type but keeps the value
}
I included PartNumber just to show that I understand how the new keyword is supposed to work. That part works fine for me.
My question is: Is there any way to both override AND refine the type of SerialNumber at the same time? I understand what I have above doesn't work, but I'm looking for a simple workaround. The goal is to NOT use an extra property (like SerialNumberInt or something) so that the Toy class's methods can refer to SerialNumber and get it as an int.
Thanks!
This can be done using Generics. But before you do it you need to be sure you are approaching your problem correctly. That said, I've used the below idea to map domain entities with common properties but they have different Types for their entity id.
public abstract class ItemBase<TId>
{
protected ItemBase(TId id)
{
ItemId = id;
}
public TId ItemId { get; }
}
public class Toy : ItemBase<int>
{
public Toy(int id) : base(id)
{
}
}
public class NotAToy : ItemBase<Guid>
{
public NotAToy(Guid id) : base(id)
{
}
}
You can do that using generics (see below) or you could just make everything an object. However, this really doesn't feel like a great solution but I don't know the purpose behind arbitrarily changing the types in this case.
public abstract class Item <T,V>
{
public abstract T SerialNumber { get; }
public V PartNumber { get; }
}
public abstract class Toy : Item<int, int>
{
public override int SerialNumber => 5;
public new int PartNumber => int.Parse(base.PartNumber.ToString());
}
public abstract class ToyString : Item<string, string>
{
public override string SerialNumber => "3";
public new string PartNumber => "34";
}
You can use generics to achieve this as follows:
public abstract class Item<T> {
public abstract T SerialNumber { get; }
}
public class Toy : Item<int> {
public override int SerialNumber => 5;
}
However, you shouldn't necessarily do this. You may want to confirm if this design is appropriate in your case since if you are redefining types on derived classes then it's most likely a problem with how you are defining your base class.
What you're trying to do is not possible.
The reason for this is that your base class expresses a contract, part of which is the return type of methods it exposes.
Your subclass cannot fulfill this contract: suppose you have a class "ItemSubmitter", which has a method "submitItem", which internally does:
string partNumberToSubmit = item.PartNumber;
Since item.PartNumber in your example returns an int, this would cause an exception. How would you expect the compiler to deal with this situation?
So, it is not possible. This concept is generalized as the Liskov Substitution Principle from Barbara Liskov, see e.g. https://en.wikipedia.org/wiki/Liskov_substitution_principle.
You could use generics, but this would require all base class consumers to also be generic. In this case I'd question if you need a base class at all and simply make the consumers generic.
See also C# Override abstract property with concrete property
It appears you've answered your own question (stated differently) some time ago: https://stackoverflow.com/a/40657803/11389043
I'm using Automapper to copy interfaces to different implementations (for serialization, for view model, for database mapping, etc...).
My code is a lot more complex but I've isolated the problem in the following code snippet sample.
Considering the following code, do I miss something because the second assertion is failing:
[Test]
public void AutoMapperTest()
{
Mapper.CreateMap<IMyBaseInterface, MyClass>();
Mapper.CreateMap<IMyInterface, MyClass>();
IMyBaseInterface baseInstance = new MyBaseClass{ MyBaseProperty = "MyBase" };
var concrete = Mapper.Map<MyClass>(baseInstance);
concrete.MyClassProperty = "MyClass";
MyClass mapped = Mapper.Map<IMyInterface,MyClass>(concrete);
Assert.AreEqual(concrete.MyBaseProperty, mapped.MyBaseProperty);
Assert.AreEqual(concrete.MyClassProperty, mapped.MyClassProperty);
}
Expected: "MyClass"
But was: null
public class MyClass : MyBaseClass, IMyInterface
{
public string MyClassProperty { get; set; }
}
public interface IMyInterface : IMyBaseInterface
{
string MyClassProperty { get; }
}
public class MyBaseClass : IMyBaseInterface
{
public string MyBaseProperty { get; set; }
}
public interface IMyBaseInterface
{
string MyBaseProperty { get; }
}
Environment:
Automapper : 4.1.1.0 / .Net: 4.5 / VS 2013
Work around:
Add Mapper.CreateMap<MyClass, MyClass>();
I don't see the above as a real answer. Since it means that if I have several implementations, I have to create mappings for all combinations. And add another mapping if I write a new implementation, even if the whole time, they all implement the same interface.
If you are using inheritance and you want to combine this with Automapper you have to say Automapper that this class is base for these classes, and these classes are children of this class. I had the same problem and it started work only when I specified ALL my inheritance relationships
Its better to check this doc page about inheritance config
I have a couple independent objects, each of which has a list of a common object. For instance,
public class Project
{
public IEnumerable<CommentEntry<Project>> Comments{get;set;}
}
public class Sample
{
public IEnumerable<CommentEntry<Sample>> Comments{get;set;}
}
public class CommentEntry<T> where T: class
{
public int TId {get;set;}
public int CommentEntryId{get;set;}
public DateTime TimeStamp{get;set;}
public string Comment{get;set;}
}
Using fluent api of Entity Framework 5, I would like a CommentEntry table for Projects and Requests. So, here is my mapping code:
modelBuilder.Entity<CommentEntry<Project>>()
.Map(m =>
{
m.ToTable("EngineeringProjectComments");
});
modelBuilder.Entity<CommentEntry<Request>>()
.Map(m =>
{
m.ToTable("SampleRequestComments");
});
When I attempt my migration I encounter the following message:
The type CommentEntry`1[Project]' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
I can see the obvious flaw of my attempt to use generics in this context. However, can anyone suggest an alternative to my database table structure, classes code or mapping code that will allow me to share the single, generic type among many classes and have independent tables?
Just use the normal inheritance structure. And, instead of using a specific ID name, like EngineeringProjectId, just use Id.
public class Project
{
public ICollection<ProjectCommentEntry> Comments{get;set;}
}
public class Sample
{
public ICollection<SampleCommentEntry> Comments{get;set;}
}
public class ProjectCommentEntry : CommentEntry {}
public class SampleCommentEntry : CommentEntry {}
public class CommentEntry
{
public int Id {get;set;}
public int CommentEntryId{get;set;}
public DateTime TimeStamp{get;set;}
public string Comment{get;set;}
}
By the way, you can't use IEnumerable for navigation properties in EF, you need a full collection which is why you should use ICollection instead.
I have a class like this
public abstract class BaseType<T>
{
public string Name {};
public T TypedValue {
get {
return GetTypedValue(PersistedValue);
}
};
public string PersistedValue {}
public abstract T GetTypedValue(PersistedValue);
}
then many derived classes like
public class IntegerType:BaseType<int>
{
...
}
is it possible to map this hierarchy using EF 4.0 using Table per inheritance scheme ?
Currently the generated code creates has an error because it generates a property like
public <T> ObjectSet<TypedAttribute<T>> TypedAttributes
{
get
{
return _typedAttributes ?? (_typedAttributes = CreateObjectSet<TypedAttribute<T>>("TypedAttributes")); }
}
private ObjectSet<TypedAttribute> _typedAttributes;
I don't think so because:
Inheritance mapping requires the base class to be entity in EDMX.
When inheritance is used the ObjectSet is for base type. What generic argument would you use to create an instance of ObjectSet when it has to be used to retrieve any subtype?
It can be partially achieved without inheritance (at least for POCOs). Simply model your subtypes in EDMX without base type. Then manually create POCO classes and derive them from generic base types. The only rule you have to follow is that POCO class must have the same name as entity in EDMX and it must have all its properties with accessibility set in EDMX. If you want to use change tracking properties must be marked as virtual. If you want to use lazy loading navigation properties must be virtual as well.
Example:
Suppose that I have two entities in EDMX: IntegerValue and DoubleValue. Now I defined these POCOs as follows:
public abstract class BaseType<T>
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual T Value { get; set; }
}
public class IntegerValue : BaseType<int>
{ }
public class DoubleValue : BaseType<double>
{ }
It will result in single table per sub type.
This code compiles but looks very strange.
I have a typical and simple parent/child relationship here which is implemented using generics in a very strange way.
But I can't seem to find any other way of doing it.
class SampleObject<T> //I don't want to make this a generic but am forced to
{
//The SampleContainer this object is in
//This must be located in this base class
public SampleContainer<T> Parent { get; set; }
}
class SpecificObject : SampleObject<SpecificObject>
//SampleObject<SpecificObject> !!? This is the bizzare bit
//It seems really strange but necessary for compilation to work
{
}
//A class to contain a List of objects derived from SampleObjects
class SampleContainer<T>
{
public List<T> List;
}
class Start
{
public void Test()
{
SampleContainer<SpecificObject> container = new SampleContainer<SpecificObject>();
SpecificObject o = new SpecificObject(); //create an object
container.List.Add(o); //add it to the list
o.Parent = container; //set its parent
}
}
Can this code be simplified?
This seems to work without the type.
Is this what you were looking for?
class SampleObject //I don't want to make this a generic but am forced to
{
//The SampleContainer this object is in
//This must be located in this base class
public SampleContainer<SampleObject> Parent;//{ get; set; }
}
class SpecificObject : SampleObject
//SampleObject<SpecificObject> !!? This is the bizzare bit
//It seems really strange but necessary for compilation to work
{
}
//A class to contain a List of objects derived from SampleObjects
class SampleContainer<T>
{
public List<T> List;
}
class Start
{
public void Test()
{
SampleContainer<SampleObject> container = new SampleContainer<SampleObject>();
SpecificObject o = new SpecificObject(); //create an object
container.List.Add(o); //add it to the list
o.Parent = container; //set its parent
}
}
In the MSDN documentation, it states that:
When deriving from a generic base
class, you must provide a type
argument instead of the base-class's
generic type parameter:
public class BaseClass<T>
{...}
public class SubClass : BaseClass<int>
{...}
It's probably a constraint that the C# designers set up in the compiler. They require that a derived type must specify the type of the generic argument at compile time. I'm not quite sure why.
Generics can create some unwieldy class hierarchies. However, the syntax for SpecificObject : SampleObject does make sense, since you're stating that the object has a parent relationship. The only other way I could see you do this, would be to split out the hierarchy with an interface. It doesn't buy much, but it may help clarify the intent.
interface IHasParent<T>
{
T Parent { get; set; }
}
public class SpecificObject : IHasParent<SpecificObject>
{
public SpecificObject Parent { get; set; }
}
If you're concerned about how verbose your collection is, you can tame the angle brackets a bit by using:
public SpecificObjectContainer : Container<SpecificObject>
{
}