I'm trying to create attribute which will generate identity number key for each object in class range. So i need to know which class contain parameter connected with attribute.
I create something like this:
class SampleModel
{
[Identity(typeof(SampleModel))]
public int Id { get; set; }
}
public class IdentityAttribute : Attribute
{
private readonly int _step;
private readonly Type _objectType;
public IdentityAttribute(Type type)
{
_step = 1;
_objectType = type;
}
public object GenerateValue()
{
return IdentityGenerator.GetGenerator(_objectType).GetNextNum(_step);
}
}
But i'm wondering is there any method which will allow me to get Type of base class (in this case SampleMethod) in IdentityAttribute constructor without sending it as parameter?
There is no such method -- an instance of Attribute does not know what it was decorating.
But the code that creates the instance does, so depending on usage you could inject this information externally:
var identityAttribute = (IdentityAttribute)Attribute.GetCustomAttribute(...);
// If you can call GetCustomAttribute successfully then you can also easily
// find which class defines the decorated property
var baseClass = ... ;
// And pass this information to GenerateValue
var value = identityAttribute.GenerateValue(baseClass);
Related
It is the first time i have gotten this requirement and i can't figure out how to make it work.
I have a class that has a generic type. Then the constructor need to get an object of that type. So far so good. Now that same constructor need to receive a secondary generic type as a parameter of that constructor without having to define a second generic type in the class. So here some sample code simplified for understanding purpose
// a base class
public class Item {}
// one of many derived class
public class Switch : Item {}
// another class but unrelated to item
public class Sheet {}
// a derived of sheet
public class CoinSheet : Sheet {}
Now i need a generic class that has an Item as a type so a basic class would be the following and it works perfectly
public class Manager<T> where T : Item
{
public T RelatedItem { get; set; } = null;
public Manager(T relatedItem)
{
RelatedItem = relatedItem;
}
}
The problem is that i now need to pass an object of type Sheet to the constructor without specifing it in the generic of the class. The following does NOT work but it's that i mean by that (based on the previous class definition
public class Manager<T> where T : Item
{
public T RelatedItem { get; set; } = null;
public Manager(T relatedItem, T2 relatedSheet) where T2 : Sheet
{
RelatedItem = relatedItem;
InstanceGenerator.Create<T2>(relatedSheet);
}
}
I proposed to call a method within the Manager class instead that can have the secondary generic type without altering the Manager class definition (which cannot change) but i was given a definitive no as an answer.
I know i can just change the parameter type of that second parameter to object and forget about generic and just hardcode the check for if the object is of type Sheet and find by reflection the InstanceGenerator.Create and invoke it but if it's avoidable i rather do so.
If you don't add it to the class, you can't add it to the constructor.
public class Manager<T> where T : Item
{
public T RelatedItem { get; set; } = null;
// THIS IS INVALID
public Manager<T2>(T relatedItem, T2 relatedSheet) where T2 : Sheet
{
RelatedItem = relatedItem;
InstanceGenerator.Create<T2>(relatedSheet);
}
}
Which makes sense. The constructor will only be called once, so there is no point changing the T2 in the constructor if it is to affect the method.
Your two real options are
Direct use of Sheet.
public class Manager<T> where T : Item
{
public T RelatedItem { get; set; } = null;
public Manager(T relatedItem, Sheet relatedSheet)
{
RelatedItem = relatedItem;
InstanceGenerator.Create<T2>(relatedSheet);
}
}
The T2 to be added on class level.
I'm working on a very simple framework, where I want to automate CRUD functions. For the purpose of this question, I've created the code below, simplified just to illustrate my issue(s).
All items derive from the (abstract) base class "DbItem" (which contains the CRUD functions), whereby the child classes provide additional functionality, and also define the table names where the DbItems are stored. For instance, "Equipment" and "Entity" both derive from DbItem, and define table names ("equipment" and "entities" respectively). However, "Entity" class is abstract and further derived by "Human" and "Animal" class. (All Humans and Animals are stored in a shared "entity" table, but equipment is stored in a separate table.)
This part of code works. The Save() method defined in DbItem properly resolves the DbTable property.
However, then I also have a DbCollection class, which extends the generic C# Collection class. I wish for the DbCollection to be able to automatically determine the correct database table name with reflection.
Hence, if I want to create a list of all equipment, I create a new DbCollection, and the code composes the appropriate SELECT statement (SELECT ... FROM equipment ...). This works.
However, if I want a list of all entities (animals and humans), I have an issue. "Entity" is marked as abstract, and I should not be able to instantiate it. However, I also do not know how to get the value of Entity.DbTable property.
A simple "fix" is to remove the "abstract" qualified from the Entity class definition. This however does not sound right to me.
Can you please let me know how can I get the value of Entity.DbTable property?
class Program
{
abstract class DbItem
{
public int Id { get; set; }
public abstract string DbTable { get; }
public void Save()
{
Console.WriteLine($"INSERT INTO {DbTable} (Id) VALUES ({this.Id})...");
}
}
abstract class Entity : DbItem
{
public sealed override string DbTable { get => "entities"; }
}
class Human : Entity { }
class Equipment : DbItem
{
public override string DbTable => "equipment";
}
class DbCollection<T> : System.Collections.ObjectModel.Collection<T>
{
public virtual string DbTable { get
{
Type t = typeof(T);
//System.Reflection.PropertyInfo p = t.GetProperty("DbName");
if(t.IsAbstract)
{
// What do we do here to get the value of Entity.DbTable?
var prop = t.GetProperty("DbTable");
// Herein lies the problem: One cannot instantiate an abstract class to provide to the GetValue() method
return prop.GetValue(null).ToString(); // System.Reflection.TargetException: 'Non-static method requires a target.'
}
else
{
var obj = Activator.CreateInstance(t);
//return (obj as DbItem).DbTable; // this also works
var prop = t.GetProperty("DbTable");
return prop.GetValue(obj).ToString();
}
}
}
public DbCollection()
{
Console.WriteLine($"SELECT Id FROM {DbTable} WHERE ...");
}
}
static void Main(string[] args)
{
var h = new Human();
h.Save(); // 1. Correctly outputs "entities";
var e = new Equipment();
e.Save(); // 2. Correctly outputs "equipment";
var ec = new DbCollection<Equipment>(); // 3. Correctly outputs "equipment"
var hc = new DbCollection<Human>(); // 4. Correctly outputs "entities"
var entityCollection = new DbCollection<Entity>(); // 5. Error.
Console.ReadLine();
}
}
Don't use a property, use an attribute. That's what you want, right? To associate a class with a table name that is fixed at compile time?
First, create a custom attribute class that stores a table name:
[AttributeUsage(AttributeTargets.Class | Inherited = true)]
public class DbTableAttribute: System.Attribute
{
private readonly string _name;
public string Name { get { return _name; } }
public DbTableAttribute(string name)
{
_name = name;
}
}
Add it to your human/animal/entity/DbItem classes:
[DbTable("entities")]
abstract class Entity : DbItem
{
//Don't need this any more
//public sealed override string DbTable { get => "entities"; }
}
And retrieve it like this:
public string GetDbTable<T>() where T : DbItem
{
var attr = typeof(T).GetCustomAttributes(
typeof(DbTableAttribute), true
).FirstOrDefault() as DbTableAttribute;
return attr?.Name;
}
What do you expect to happen in your fifth case?
There is absolutly no way to create an instance of an abstract class. remove the abstract keyword from your Entity class. If you want to prevent external creation of the Entity class you could use an internal constructor.
Since this is an collection you might also use the first entry of the collection to get the DbTable result - might be dangerous since the second item could be of another type.
I am programming a dll with a factory pattern on C#. The factory receive an enum and return an interface. Depending on the received enum it create different objects and return it encapsulated in the interface. Any class inside the factory implements the interface and its access modifier is internal, except the own interface which is public.
The problem is when i call the dll from the main project. Each object created inside the factory has different properties and do not why i can not access or modify those properties from the main. Some help?
this is the Factory call from the main.
IConfigurator config = ConfigFactory.Instance.CreateConfigurator(Model.First);
This is how the factory works (inside the dll):
public IConfigurator CreateConfigurator(Model model)
{
switch (model)
{
case Model.First:
return (First)new First(model);
case Model.Second:
return (Second)new Second(model);
case Model.Third:
return (Third)new Third(model);
}
}
First, Second and Third has different properties and i am not able to modify it from the interface object received
Thanks.
The short answer is that you're returning an interface, therefore only the properties that are part of the interface are available until you cast the object to its concrete type.
For example:
public class A : INameable
{
public string Name { get; set; }
public int Age { get; set; }
}
public class B : INameable
{
public string Name { get; set; }
public string Description { get; set; }
}
public Interface INameable
{
string Name { get; set; }
}
public Enum Selector
{
A,
B
}
So if I use a method as follows
public INameable GetINameable(Selector selector)
{
if (selector.Equals(Selctor.A))
return new A { Name = "Name A", Age = 10 };
if (selector.Equals(Selector.B))
return new B { Name = "Name B", Description = "New Instance of B"};
}
I will get an instance of INameable returned and will only be able to access the Name property as defined in the interface.
However if I need to access the other properties then I need to cast the returned object to its concrete type as follows:
// This will be an instance of INameable
var obj = GetINameable(Selector.A);
// Now cast as an instance of A
var castObj = obj as A;
// We can now access the Age property
var age = castObj.Age;
The method can have only one return type.
Instead of chossing result by enum, create different factory method / factory class for every item.
Sample:
// instead of this
public enum FactoryEnum {
VariantA,
VariantB,
VariantC
}
object Create(FactoryEnum item);
// do this
IMyInterfaceA CreateA();
IMyInterfaceB CreateB();
IMyInterfaceC CreateC();
I have an object graph containing nodes of various types. All nodes derive from a Node class. Using YamlDotNet I've managed to serialize and deserialize the graph by providing my own implementation of IObjectFactory. The only thing left to do is to get rid of a constructor that only exists to please the YamlDotNet serializer.
Have a look at the following .NET fiddle
https://dotnetfiddle.net/KJMzxD
The FancyNode.ctor() is the constructor I would like to remove but I'm not sure how to tell the serializer that I've handled everything in the deserializer. If I simply remove it I get the following error
Type 'FancyNode' cannot be deserialized because it does not have a default constructor or a type converter.
If you only want to get rid of the parameterless constructor code rather than the constructor itself - given it's required for that type of deserialisation - you could remove both constructors and use a factory method to create the nodes. That would result in the class having a default public constructor.
For example, change:
public class FancyNode : Node
{
private IController controller;
public string ID
{
get;
private set;
}
// I would really like to get rid of this constructor
public FancyNode()
{
throw new NotSupportedException();
}
// NOTICE: no default constructor here
public FancyNode(IController controller, string id)
{
this.controller = controller;
this.ID = id;
}
}
to:
public class FancyNode : Node
{
private IController controller;
public string ID
{
get;
private set;
}
public static FancyNode CreateNode(IController controller, string id)
{
var node = new FancyNode();
node.controller = controller;
node.ID = id;
return node;
}
}
Yes you lose the tight control you have that doesn't allow the object to be created without passing those parameters in, given that anyone can now do var x = new FancyNode(). Then again you aren't validating the parameters so it makes no difference to calling it with new FancyNode(null, null).
I have a class inherited from an abstarct class. On razor, when I create instance of child class, I got this error as is shown in image:
Cannot create an abstract class
But StuffRegisterSearchParameterVM is not an abstract class. Why this happen?
Controller
public ActionResult SearchRegistration(SearchParameterVM model)
Model
abstract public class SearchParameterVM
{
public string FromDate { get; set; }
public string ToDate { get; set; }
public int? MainTestRegisterId { get; set; }
public int TestTypeId { get; set; }
public bool IsForAnsweringPage { get; set; }
}
public class StuffRegisterSearchParameterVM : SearchParameterVM
{
public int? StuffId { get; set; }
public string Code { get; set; }
}
You can not use abstract class as a parameter of action, because asp.net mvc does not know anything abount posted object type, it trys to create an argument type, and this type is abstract.
So, replace it this concrete class or create special binder.
When you define the action:
public ActionResult SearchRegistration(SearchParameterVM model)
That defines a method that MVC will call based on your routes when an http request is made to the server. That http request probably contains only parameters like you would have if you had a web form. MVC model binding simply creates an instance of the class specified in the parameter to the action in C# and tries to set the property values based on the http parameters passed in the http call. This call could be from a view action like you have, from a static html page, from a program, or anywhere else you can make an http call. When it is an abstract class, it cannot create an instance of it.If you had 3 child classes based on your abstract class, MVC would have no way to tell which type to create.
You can check out this question for some more information.
So how would you determine what concrete class should exist in memory when a call to that action is made, given only parameter names with different values? You could create different routes and actions that had different types in their parameters. You could also check those parameters and create different concrete classes based on the passed parameters. For instance if you wanted to use a certain class based on if the 'code' value is passed, , you'll either have to create your own IModelBinder which could determine which concrete class based on the passed query parameters:
public class MyModelBinder : IModelBinder {
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
// create different concrete instance based on parameters
ValueProviderResult code = bindingContext.ValueProvider.GetValue("Code");
if (code.AttemptedValue != null) {
// code is passed as a parameter, might be our class
// create instance of StuffRegisterSearchParameterVM and return it
}
// no Code parameter passed, check for others
}
}
Then you have to tell in your startup that you have a special model binder
for your abstract class:
ModelBinders.Binders.Add(typeof(SearchParameterVM), new MyModelBinder());
Or you could do something in your action to determine what type to create and use TryUpdateModel to set the values:
public ActionResult SearchRegistration() {
SearchParameterVM model = null;
if (Request.Parameters["code"] != null) {
model = new StuffRegisterSearchParameterVM();
TryUpdateModel(model); // check return value
}
}