I'm currently having a strange issue with the latest mongodb c# driver (2.7), inheritance and serialization.
Here is a super simplified class hierarchy that I'm using:
public abstract class BaseClass
{
public Guid Id { get; private set; }
protected BaseClass(Guid id)
{
Id = id;
}
}
public class DerivedClass : BaseClass
{
public DerivedClass(Guid id) : base(id) {}
}
When I try to register the class for mongo serialization, I am getting an error:
var type = typeof(DerivedClass);
if (BsonClassMap.IsClassMapRegistered(type))
return;
var cm = new BsonClassMap(type);
cm.AutoMap();
BsonClassMap.RegisterClassMap(cm);
The error is:
The memberInfo argument must be for class DerivedClass, but was for class BaseClass.
Parameter name: memberInfo
Now, and this is where things start to get weird, when I tried to understand what was happening I made some changes to the BaseClass or the DerivedClass.
ANY of the two changes listed below will make the RegisterClassMap method work without errors...
change 1: adding a second, unused property on BaseClass
public abstract class BaseClass
{
public Guid Id { get; private set; }
public string Test { get; }
protected BaseClass(Guid id)
{
Id = id;
}
}
change 2: changing the Id property to have a non-private setter (any other works)
public abstract class BaseClass
{
public Guid Id { get; protected set; }
protected BaseClass(Guid id)
{
Id = id;
}
}
My question is... what is happening? While I may agree that for the serializer you should have an accessible setter for every property on the class that you want to map (in this case, DerivedClass), why the case #1 is actually not giving any error?
Related
I have base class thats inherited by multiple classes
public class Animal
{
public int Id { get; set; }
public static Animal Get(int id)
{
return ...
}
}
public class Cat : Animal
{
public CatPayload Payload { get; set; }
}
public class Dog : Animal
{
public DogPayload Payload { get; set; }
}
public class CatPayload
{
public int Lives { get; set; }
}
public class DogPayload
{
public bool IsDangerous { get; set; }
}
I store only Animal identifier in the database, so when I'm getting it using static method I get instance of Animal and I can't get strongly-typed instance of Payload. What would be the best way to implement this? Only thing I currently have on mind is checking type of animal instance if it matches any of the subclasses, but I'm not sure if there some simpler way?
I find your answer lacking a bit in details, so i don't know if this helps, but otherwise please extend your answer with more details, especially the parts of code that use these classes.
You can easily check with pattern matching:
// Creates a Cat, casts to an animal type
Animal myAnimal = new Cat(3);
if(myAnimal is Cat castedAnimalToCat)
{
// myCat is Animal returned true, which means we have a cat object.
// castedAnimalToCat is now a new variable of type Cat
Console.WriteLine($"{castedAnimalToCat.CatPayload}");
}
if(myAnimal is Dog castedAnimalToDog)
{
// ...
}
So somwhere in your database logic you can cast this to the right operand.
You can do some more fancier things with reflection, but your concern is always time safety and complexity with that.
1) Inheritance is supported in EF. This guide shows how it can be done:
Tutorial: Implement Inheritance with EF in an ASP.NET MVC 5 app
2) You might also consider using a flat structure for your data model in which the entities are referencing each other with foreign keys:
public class Cat
{
public int AnimalId { get; set; }
[ForeignKey(nameof(AccountId))]
public Animal Animal { get; set; }
public CatPayload Payload { get; set; }
}
3) For the payload you might use a string property for persistance containing json of the serialized class:
[NotMapped]
[JsonIgnore]
CatPayload Payload { get; set; }
public string PayloadJson
{
get => Payload == null ? null : JsonConvert.SerializeObject(Payload);
set => Payload = value == null ? null : JsonConvert.DeserializeObject<CatPayload>(value);
}
Is there a way i can have derived classes override the default value of the base class? In the example below i would need the Hammer.Name to return "Hammer".
public class ItemBase
{
public string Name = "Base";
}
public class Hammer: ItemBase
{
new public string Name = "Hammer";
}
public class Test
{
ItemBase MyThing = new Hammer();
// Prints "Base"
Console.WriteLine(ItemBase.Name);
}
You don't need different fields, you need different initializations of the same field.
class Base {
protected string name = "";
public Base() { name = "X"};
}
class Derived : Base {
public Derived() { name = "Y"}; //same {name } field of a Base class
}
You might consider using virtual properties instead of exposing public fields (which is considered bad practice).
As such, you can (with C# 6.0):
void Main()
{
ItemBase myThing = new Hammer();
// Doesn't print "Base"
Console.WriteLine(myThing.Name);
}
public class ItemBase
{
public virtual string Name { get; } = "Base";
}
public class Hammer : ItemBase
{
public override string Name { get; } = "Hammer";
}
or (if you're using older version of C#)...
public class ItemBase
{
public virtual string Name { get { return "Base"; } }
}
public class Hammer : ItemBase
{
public override string Name { get { return "Hammer"; } }
}
You are not defining a new default value in the derived type, you are declaring a completely new field that hides the field with the same name in the base class.
Because fields can't be virtual, the returned field is the one declared in the type through which you are invoking it.
Solution? Don't redeclare a new field, simply assign a new value to the existing field in the constructor of the derived type:
public class Hammer
{
public Hammer() {
Name = "Hammer"; }
}
Trying to figure out what exactly is needed while skating around the .NET version restrictions has been a headache but I have a solution. According to your comments you can use a constructor.
In that case this is really easy to do with properties (which are the preferred way to handle your situation) instead of public fields:
public class ItemBase
{
public ItemBase()
{
//When instantiating ItemBase the value of Name is "Base"
Name = "Base";
}
public string Name { get; set; }
}
public class Hammer : ItemBase
{
public Hammer()
{
//When instantiating Hammer the value of Name is "Hammer"
Name = "Hammer";
}
}
And to test just run this:
public class Program
{
public static void Main()
{
ItemBase itemBase = new Hammer();
Console.WriteLine(itemBase.Name);
itemBase.Name = "Foo";
Console.WriteLine(itemBase.Name);
}
}
Outputs:
Hammer
Foo
This should check off all the boxes. You now use properties (making your code better), each class has a default value, and the properties can be changed after instantiation.
I have a class called ModelBase:
public abstract class ModelBase : ViewModelBase
{
public ModelBase
{
ProcessObjects.Instance.AddProcessObject(name, this);
}
public abstract void Dispose();
public String Name { get; set; }
....
....
}
public class SomeModel1: ModelBase
{
public String customprop1 { get; set; }
}
public class SomeModel2: ModelBase
{
public String customprop2 { get; set; }
}
I keep a list of all object instances in an application singleton. Now somewhere else in the application I want to retrieve customprop2 from SomeModel2 using this singleton list. I can retrieve the object as modelbase object and cast it:
SomeMethod()
{
if(_obj.Name == "SomeModel2"){
var _obj = obj as SomeModel2;
var _customProp2 = obj.customprop2 ;
}
}
But ideally i want to just try to retrieve the value straight from the object instance by knowing it is there in the parent of the basemodel.
try{
//Some code to automatically cast the object as parent.
var _customProp2 = _obj.customProp2;
}catch{
//Notify user that his request failed
}
The reason for this is that the user can write into a textbox and start a logging function for that particular property.
Can you create a virtual property/method in the base class and override it in your derived class. This way you can retrieve the values from the object with base class reference, whenever you want.
The general idea starts from creating a control, which would work with any database object.
The database is accessed via LINQ to SQL, and database objects are auto-generated from the existing database.
To make the control work with any of these objects, I use a base interface, which all the auto-generated objects inherit from. So the control actually works with that interface.
This interface at first should provide:
an ability to get the identifier (primary key) of an object
an expression which would return this identifier, if compiled and called via an instance of an object
So this looks like:
public interface IEntity<TEntity, TEntityId>
where TEntity : IEntity<TEntity, TEntityId>
where TEntityId : IEquatable<TEntityId>
{
Expression<Func<TEntity, TEntityId>> GetIdExpression();
TEntityId EntityId { get; }
}
Then a database object's class definition would look like:
//let it be the auto-generated part:
public partial class Entity1
{
public Guid UId { get; set; }
}
//and the manual part:
public partial class Entity1 : IEntity<Entity1, Guid>
{
public Expression<Func<Entity1, Guid>> GetIdExpression()
{
return (Expression<Func<Entity1, Guid>>)(se => se.UId);
}
public Guid EntityId
{
get { return this.UId; }
}
}
And we can test it:
var e1 = new Entity1();
e1.UId = Guid.NewGuid();
Console.WriteLine(e1.UId);
Console.WriteLine(e1.GetIdExpression().Compile()(e1));
The output of these two lines is equal. It's OK.
The next idea was, that the lion share of all database objects have int identifier with the name Id. And it would be much better to avoid editing every of them, i.e. not to write one and the same code implementing IEntity<TEntity, TEntityId>.
As far as I need to specify the implementation of this interface, I need a class.
For now I ended up with
public class IntEntity<TEntity> : IEntity<TEntity, int>
where TEntity : IntEntity<TEntity>
{
public int Id { get; set; }
public Expression<Func<TEntity, int>> GetIdExpression()
{
return (Expression<Func<TEntity, int>>)(e => e.Id);
}
public int EntityId
{
get { return this.Id; }
}
}
Then the database object class would derive from it:
//let it be the auto-generated part:
public partial class Entity2
{
public int Id { get; set; }
}
//and the manual part:
public partial class Entity2 : IntEntity<Entity2>
{
}
Already now it's obvious, that Id in Entity2 does hide the Id in IntEntity<TEntity>. This is also proved by the same test:
var re = new Entity2();
re.Id = 5;
Console.WriteLine(re.Id); //5
Console.WriteLine(re.GetIdExpression().Compile()(re)); //0
So this solution is wrong...
I also tryed to make IntEntity<TEntity> abstract, but obviously failed, as in Entity2 Id is defined in the auto-generated part, and I could not make it override the abstract Id of the base class.
Is there anything I can do to make all database objects with int Id use the Id from the base IntEntity<TEntity> class. Or, probably, vice versa...
The main point here is that the expression returned by GetIdExpression should have the form e => e.Id, as this expresion would later be used in LINQ to SQL queries, so LINQ should be able to translate it to a correct SQL query.
I'm not sure that's exactly what you want, but you can try to do so
public abstract class IntEntity<TEntity> : IEntity<TEntity, int>
where TEntity : IntEntity<TEntity>
{
public abstract Expression<Func<TEntity, int>> GetIdExpression();
public abstract int EntityId
{
get;
}
}
public partial class Entity2
{
public int Id { get; set; }
}
//and the manual part:
public partial class Entity2 : IntEntity<Entity2>
{
public override int EntityId
{
get { return this.Id; }
}
public override Expression<Func<Entity2, int>> GetIdExpression()
{
return (Expression<Func<TEntity, int>>)(e => e.Id);
}
}
in fact without override virtual you can't get access to field in derive class from method in base class
in sample above in fact equals your first sample
UPDATE
as workaround you can add method SetId that set id in derive and base class like this
public class IntEntity<TEntity> : IEntity<TEntity, int>
where TEntity : IntEntity<TEntity>
{
public int Id { get; set; }
public Expression<Func<TEntity, int>> GetIdExpression()
{
return (Expression<Func<TEntity, int>>)(e => e.Id);
}
public int EntityId
{
get { return this.Id; }
}
}
//let it be the auto-generated part:
public partial class Entity2
{
public int Id { get; set; }
}
//and the manual part:
public partial class Entity2 : IntEntity<Entity2>
{
public void SetId(int id)
{
Id = id;
base.Id = id;
}
}
var re = new Entity2();
re.SetId(5);
Console.WriteLine(re.Id); //5
Console.WriteLine(re.GetIdExpression().Compile()(re)); //5
I have a customer hierarchy like so:
abstract class Customer {
public virtual string Name { get; set; }
}
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
}
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
}
When I retrieve a Customer, I would like to show on the web form the properties to edit/modify. Currently, I use if statements to find the child customer type and show the specialized properties. Is there a design pattern (visitor?) or better way so I can avoid the "if" statements in presentation layer? How do you do it?
Further information: This is an asp.net website with nHibernate backend. Each customer type has its own user control on the page that I would like to load automatically given the customer type.
Can you use reflection to get the list of properties specific to an subclass (instance)? (Less error-prone.)
If not, create a (virtual) method which returns the special properties. (More error prone!)
For an example of the latter:
abstract class Customer {
public virtual string Name { get; set; }
public virtual IDictionary<string, object> GetProperties()
{
var ret = new Dictionary<string, object>();
ret["Name"] = Name;
return ret;
}
}
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
public override IDictionary<string, object> GetProperties()
{
var ret = base.GetProperties();
ret["Max spending"] = MaxSpending;
return ret;
}
}
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
public override IDictionary<string, object> GetProperties()
{
var ret = base.GetProperties();
ret["Award"] = Award;
return ret;
}
}
You probably want to create sections (fieldsets?) on your Web page, anyway, so if would come into play there, making this extra coding kinda annoying and useless.
I think a cleaner organization would be to have a parallel hierarchy of display controls or formats. Maybe use something like the Abstract Factory Pattern to create both the instance of Customer and of CustomerForm at the same time. Display the returned CustomerForm instance, which would know about the extra properties and how to display and edit them.
new:
interface CustomerEdit
{
void Display();
}
edit:
abstract class Customer {
protected CustomerEdit customerEdit; // customers have an object which allows for edit
public virtual string Name { get; set; }
public void Display() { customerEdit.Display(); } // allow the CustomerEdit implementor to display the UI elements
}
// Set customerEdit in constructor, tie with "this"
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
}
// Set customerEdit in constructor, tie with "this"
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
}
usage:
Customer whichCouldItBe = GetSomeCustomer();
whichCouldItBe.Display(); // shows UI depeneding on the concrete type
Have you tried something like this:
public class Customer<T>
where T : Customer<T>
{
private T subClass;
public IDictionary<string, object> GetProperties()
{
return subClass.GetProperties();
}
}
With a subclass of:
public class FinancialCustomer : Customer<FinancialCustomer>
{
}
This is off the top of my head so might not work. I've seen this type of code in CSLA.NET.
Here's the link to the CSLA.NET class called BusinessBase.cs which has a similar definition to what I've given above.