Good day,
I have a class which represents a collection. The collection has a property of type Type which allows you to specify the data type of each element's meta data object. Each time an element is added to the collection a new instance of the assigned meta data object is created with Activator.CreateInstance(Type type) from within the collection class.
What I need is to restrict the meta data object's type to a type that implements a specific intreface. Example:
publlic class Collection
{
public Type MetaDataType;
// other code
}
public class CollectionImplementation
{
// some properties
public CollectionImplementation()
{
Collection c = new Collection();
// valid assignment
c.MetaDataType = typeof(ValidMetaClass);
// invalid assignement
c.MetaDataType = typeof(InvalidMetaClass);
}
// some functions
}
public class ValidMetaClass : IMetaInterface
{
// valid meta class code
}
public class InvalidMetaClass
{
// invalid meta class code
}
public interface IMetaInterface
{
// interface code
}
Is something like this possible?
Thank you in advance to any and all contributors; I appreciate any and all input.
Kind regards,
me
Try using generics and type constraints on your constructor instead of setting the public field MetaDataType in your Collection class.
public CollectionImplementation<T>(T MetaDataType) where T : NameOfInterface
{
}
You can use restrictions on generic types.
public class Collection<T> where T : IMetaInterface
{
T MetaDataType;
}
Or you could just throw an exception when adding elements like:
if( !( addedElement is IMetaInterface )) throw new Exception("Please don't do this");
Related
The following scenario:
public enum ChildType
{
Type1,
Type2,
Type3
}
public abstract class MyParentClass
{
public abstract ChildType Id { get; }
}
public class Child1 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type1; } }
}
public class Child2 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type2; } }
}
public class Child3 : MyParentClass
{
public override ChildType Id { get { return ChildType.Type3; } }
}
and i would like to use autofac to register all the subtypes using their id as a key, so something like:
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(type => type.IsSubclassOf(typeof(MyParentClass)))
.Keyed<MyParentClass>(c => c.Id)
.SingleInstance();
now obviously the above doesn't work, but is there some way to achieve that without registering each subclass separately? I want to then be able to access them by the enum, i.e. at runtime when i don't know what the value of the enum will be:
public static MyParentClass GetSubClassByEnum(ChildType id)
{
AutofacHostFactory.Container.ResolveKeyed<MyParentClass>(id);
}
Unfortunately you probably won't be able to get exactly this setup working because it's sort of a chicken/egg problem - you want to resolve an object based on information that won't be available... unless you resolve the object.
One way to get this working is to use attributes rather than properties. Attributes are available before the type is instantiated so you could store the info there and achieve the desired result.
Autofac has attribute metadata support that allows you to create custom attributes to provide this sort of information. You could create an attribute that gets inherited and only allows one instance per class. Apply it with the default value to your base class, then when you need to override apply a new attribute on the derived class.
There is plenty of documentation with examples on the Autofac doc site showing how to work with this.
While trying to save Object into mongo DB using Spring-Data MongoDB, I am getting this exception.
Ambiguous field mapping detected! Both <child class field> and <parent class field> map to the same field name <field name>! Disambiguate using #DocumentField annotation!
I am hiding a field in child class and this is causing the issue. But this is essential for me to hide the field declared in super class. I cannot find #DocumentField annotation anywhere. How can I proceed here? Is there any other solution?
For those of you (like me) who arrived here due to another form of Ambiguous field mapping detected! error that is not quite the same as OP, consider that your child class may not even need a field at all and the answer provided by #sparm could be changed to:
public class Parent {
private String myField;
public getMyField() {
return myField;
}
}
#Document(collection = "children")
public class Child extends Parent {
public getMyField() {
super.getMyField();
}
}
I believe its an error in the exception. You need to use #Field instead and specify a different name for the parameter so:
public class Parent {
private String myField;
}
#Document(collection = "children")
public class Child extends Parent {
#Field("childField")
private String myField;
}
In my design I have a class that has a property whose type can be inherited from:
public class Feed
{
...
[JsonProperty(TypeNameHandling = TypeNameHandling.Auto)]
public FeedSource Source { get; set; }
...
}
public abstract class FeedSource { ... }
public class CsvSource : FeedSource { ... }
public class DbSource : FeedSource { ... }
I'm using the Entity Framework to load and store this object to a database and I'm using Json.NET to serialize this object into JSON for further processing.
The problem I stumbled on is that the $type property is containing the typename of the EF proxy instead of the "real" typename. So instead of:
$type: "System.Data.Entity.DynamicProxies.CsvSource_0B3579D9BE67D7EE83EEBDDBFA269439AFC6E1122A59B4BB81EB1F0147C7EE12"
which is meaningless to other clients, I would like to get:
$type: "MyNamespace.CsvSource"
in my JSON.
What's the best way to achieve this?
Another way which doesn't require you to make changes to your EF configuration is to use a custom SerializationBinder, e.g.:
class EntityFrameworkSerializationBinder : SerializationBinder
{
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
if (serializedType.Namespace == "System.Data.Entity.DynamicProxies")
typeName = serializedType.BaseType.FullName;
else
typeName = serializedType.FullName;
}
public override Type BindToType(string assemblyName, string typeName)
{
throw new NotImplementedException();
}
}
Usage:
string json = JsonConvert.SerializeObject(entityFrameworkObject, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, Binder = new EntityFrameworkSerializationBinder() });
You can do two things:
disabling tracking proxies, by setting ProxyCreationEnabled to false. You can find this property in your context's Configuration property. If you use a context for a single GetXxx method, you can do it without interfering other context instanced.
using the AsNoTracking() extension method when you recover your entity, like this:
MyContext.MyTable.AsNoTracking(). // rest of the query here
This indicates that you don't want a tracking proxy for your entity, so you'll get the entity class. This has no interference with the afore mentioned configuration.
I have the following class hierarchy
[BsonKnownTypes(typeof(MoveCommand))]
public abstract class Command : ICommand
{
public abstract string Name
{
get;
}
public abstract ICommandResult Execute();
}
public class MoveCommand : Command
{
public MoveCommand()
{
this.Id = ObjectId.GenerateNewId().ToString();
}
[BsonId]
public string Id { get; set; }
public override string Name
{
get { return "Move Command"; }
}
public override ICommandResult Execute()
{
return new CommandResult { Status = ExecutionStatus.InProgress };
}
}
if I save the command like so:
Command c = new MoveCommand();
MongoDataBaseInstance.GetCollection<Command>("Commands").Save(c);
and then query the DB, I don't see the derived properties persisted.
{ "_id" : "4df43312c4c2ac12a8f987e4", "_t" : "MoveCommand" }
I would expect a Name property as a key in the document.
What am I doing wrong?
Also, is there a way to avoid having a BsonKnowTypes attribute on the base class for persisting derived instances? I don't see the why a base class needs to know about derived classes. This is bad OO design and is being forced on my class hierarchy by the BSON library. Am I missing something here?
1.Name property was not saved into database because it haven't setter. Serializers not serialize properties that's haven't setters (because if serializer serialize such property it will not able deserialize it back). So if you want serialize Name property then just add fake setter(into ICommand need to add it also):
public override string Name
{
get { return "Move Command"; }
set{}
}
2.If you don't want use BsonKnownTypes attribute there is another way to notify serializer about know types it might encounter during deserialization. Just Register maps once, on app start event:
BsonClassMap.RegisterClassMap<MoveCommand>();
//all other inherited from ICommand classes need register here also
So you should use or KnownTypes attribute or register BsonClassMap for each polymorphic class, otherwise you will get 'unknown descriminator' error during deserializtion:
var commands = col.FindAllAs<ICommand>().ToList();
3 You said:
This is bad OO design and is being
forced on my class hierarchy by the
BSON library.
In any way even without KnownTypes atribute your code using Bson lib through BsonId attribute.
If you want avoid it you can:
BsonClassMap.RegisterClassMap<MoveCommand>(cm => {
cm.AutoMap();
cm.SetIdMember(cm.GetMemberMap(c => c.Id));
});
So now you can remove reference to Mongodb.Bson lib from your domain code lib.
I'm creating MVVM application and in Model section I have simple base abstract class Animal and class Dog which derives from it:
public abstract class Animal
{
public int Age { get; set; }
}
public class Dog : Animal
{
public string Name { get; set; }
}
ViewModel section containts UI-friendly VM classes of them:
public abstract class AnimalVM<T> : ViewModelBase where T : Animal
{
protected readonly T animal;
public int Age
{
get { return animal.Age; }
set
{
animal.Age = value;
OnPropertyChanged("Age");
}
}
protected AnimalVM(T animal)
{
this.animal = animal;
}
}
public class DogVM : AnimalVM<Dog>
{
public string Name
{
get { return animal.Name; }
set
{
animal.Name = value;
OnPropertyChanged("Name");
}
}
public DogVM(Dog dog) : base(dog) { }
}
Suppose I have another VM class which contains ObservableCollection<AnimalVM>. The problem is how to create that kind of property which allow me to store there different types of Animal? I want to achieve something like this:
public class AnimalListVM : ViewModelBase
{
// here is a problem, because AnimalVM<Animal> isn't compatible with DogVM
readonly ObservableCollection<AnimalVM<Animal>> animals;
public ObservableCollection<AnimalVM<Animal>> Animals
{
get { return animals; }
}
public AnimalListVM(IList<Animal> animals)
{
//this.animals = ...
}
}
I can change ObservableCollection<AnimalVM<Animal>> property to ICollection property and then create list of AnimalVM using some dictionary Animal -> AnimalVM wrapper and Activator.CreateInstance() - it works but when I try to extend AnimalListVM adding another property SelectedAnimal which will be binded in sample View to e.g. DataGrid control I have another problem with type of that kind of property SelectedItem. It can't be of type AnimalVM<Animal> because when I have DogVM object in my Collection it won't fit with this and throw an exception.
Everything will be clear if only I had non-generic AnimalVM but I don't want to copy and paste similar properties in every DogVM, CatVM, BirdVM class derived from AnimalVM. How can I achieve this?
Ok, I've found a solution and of course it's very simple: just create another, non-generic abstract base class for your generic abstract base class and then derive your generic class from that newly created non-generic class. In that case you also must rewrite properties from non-generic class to generic class (to be more specific override them), but you do this only once, so you don't have to copy and paste the same code in every generic derived ViewModel (in our example in every DogVM, CatVM, BirdVM, etc.).