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.
Related
we have an interface with a Generic Customer (gold/silver), now lets say i stored the last created cutomer somewhere (Cache/DB/etc).
how do i create a GetCustomer method that returns the type of customer.
Should i add GetCustomer to the base class or interface or elsewhere ? and how do we use GetCustomer ?
Hope that makes sense.
interface ICstomerInterface<T>
{
T MakeCustomer();
}
public abstract class BaseCustomer<T>: ICstomerInterface<T>
{
public string Type { get; set; }
public string Name { get; set; }
// methods
public abstract T MakeCustomer();
}
public class Gold : BaseCustomer<Gold>
{
public override Gold MakeCustomer()
{
var customer = new Gold
{
Type= "Gold",
Name = "Jack"
};
return customer;
}
}
public class Silver : BaseCustomer<Silver>
{
public override Silver MakeCustomer()
{
var customer = new Silver();
customer.Name = "Jones";
customer.Type = "Silver";
return customer;
}
}
The problem with you design is that MakeCustomer is an instance method of the customers. This means, that you must create a customer to be able to call MakeCustomer. In other words, this design will not work!
You have two options: Either simply initialize the customers in their respective constructors, or create a customer factory. This must be a separate class (static or not).
The generic type parameter is superfluous.
public abstract class CustomerBase
{
public CustomerBase(string name)
{
Name = name;
}
public abstract string Type { get; }
public string Name { get; }
}
public class GoldCustomer : CustomerBase
{
public GoldCustomer(string name)
: base(name)
{
}
public override string Type => "Gold";
}
public class SilverCustomer : CustomerBase
{
public SilverCustomer(string name)
: base(name)
{
}
public override string Type => "Silver";
}
The Type property can be a getter only property. It can be abstract in the base class to force concrete customer classes to implement it.
The name must be passed to the constructor to be able to assign different names to different customers.
See also Abstract factory pattern on Wikipedia.
Example:
var customers = new List<CustomerBase> {
new GoldCustomer("Frank"),
new SilverCustomer("Jack")
};
foreach (CustomerBase c in customers) {
Console.WriteLine($"{c.Name} is {c.Type}");
}
Prints:
Frank is Gold
Jack is Silver
See working example: https://dotnetfiddle.net/BiAskT
You could have an extension method to get the customer type (on a static class):
public static Type GetCustomerType<T>(this ICstomerInterface<T> _customer)
{
return typeof(T);
}
All with the intention that you don't have to make redundant code each time you create a new Customer class that implements that interface.
Your sample code doesn't contain a base type that can contain all your customers, i.e. there is no strongly-typed variable that could hold either a BaseCustomer<Gold> or a BaseCustomer<Silver>. There is no type compatibility between those two at all, no more than a Dictionary<int> can be stored as a Dictionary<string> (in fact, even worse, because there isn't covariance in your object model). Given that fact, your question makes little sense, because you'll always have to declare the type parameter to store a customer somewhere, therefore there is no need to learn the type at run time.
But let's say you want a cache that works for both. You'd introduce a non-generic base interface:
public interface ICustomer
{
Guid CustomerGuid { get; }
}
public abstract class BaseCustomer<T>: ICstomerInterface<T>, ICustomer
{
///etc....
Now you can define a cache that could hold all customers:
var cache = new Dictionary<Guid,ICustomer>();
var gold = new Gold();
cache.Add( gold.CustomerGuid, gold );
var silver = new Silver();
cache.Add( silver.CustomerGuid, silver );
Now you can retrieve any customer by its Guid:
var customer = cache[guid];
And to determine its type, just use
bool isGold = customer is Gold;
bool isSilver = customer is Silver;
Or to get a string that tells you the type:
string type = customer.GetType().Name;
It appears that you are trying to use the classic Factory Method.
Personally for simple classes, like the one you are asking about, I would add a static method on the base class, like so:
public abstract class BaseCustomer<T>: ICstomerInterface<T>
{
public static BaseCustomer<T> GetLastCustomer()
{
// Get from storage, the last customer.
}
}
That way your Create customer method is separate from your get customer method but still accessible from anywhere.
I admit that a large part of my trouble with this must stem from simple lack of understanding around a few of the components involved in the solution. I'm not exactly "new to" Entity Framework, code-first, nor generic types, but the inner workings of all three are still mysterious enough to me that this is giving me fits.
I have a code-first project in which I have separated-out "model" classes from "service" classes, with generalizations in both. I am NOT using the full Repository Pattern, for a variety of reasons. For most of what I am doing, the structure I have in place is working beautifully -- I understand it and it seems quite clean.
But there is one area where I am running into problems, and that is being able to pass one of my model class types as a generic parameter to an instance of a generic service object, given a string path/name of the model class.
(Background: I need to do this because I "seed" several tables in the database with initialization values from a JSON file. This JSON file contains the names of the model entities. So, at runtime, I need to get that string value, and then feed that as the type to the generic service object that does the database operations.)
Here are the pertinent code snippets:
In BaseEntity.cs I have the top-level interface and a number of abstract classes from which the specific model entities then inherit:
namespace POST.API.Models
{
public interface IEntity
{
int Id { get; set; }
}
public abstract class BaseEntity { }
public abstract class Entity : BaseEntity, IEntity
{
public virtual int Id { get; set; }
}
public abstract class TypeEntity : Entity
{
public TypeDefinition Definition { get; set; }
}
}
In BaseService.cs I have another interface and more abstract classes from which specific model service classes inherit. There is also one concrete class, here, that is generalized for performing an insert operation:
namespace POST.API.Services
{
public interface IEntityService { }
public abstract class BaseEntityService<T> : IEntityService
where T : Models.BaseEntity
{
public T Fetch(int Id)
{
using (var Db = new PostDbContext())
{
return Db.Set<T>().Find(Id);
}
}
public void Create(T Item)
{
if (Item != null)
{
using (var Db = new PostDbContext())
{
DbSet Entity = Db.Set<T>();
Entity.Add(Item);
Db.SaveChanges();
}
}
}
public IEnumerable<T> All()
{
using (var Db = new PostDbContext())
{
return (IEnumerable<T>)Db.Set<T>().ToList();
}
}
}
public abstract class BaseTypeEntityService<T> : BaseEntityService<T>
where T : Models.TypeEntity
{ }
public abstract class BasePropertyTypeEntityService<T> : BaseTypeEntityService<T>
where T : Models.PropertyTypeEntity { }
public abstract class BasePropertyEntityService<T> : BaseEntityService<T>
where T : Models.BaseEntity { }
public class TypeEntityService<T> : BaseTypeEntityService<T>
where T : Models.TypeEntity { }
#endregion
}
I've removed some methods not pertinent to the presentation.
I have some code that then attempts to use these base classes to rummage through the JSON file and insert some rows, thus:
using (PostDbContext Db = new PostDbContext())
{
string JsonString = System.IO.File.ReadAllText(JsonDataFile);
DataSet JsonDataSet = JsonConvert.DeserializeObject<DataSet>(JsonString);
foreach (DataTable Table in JsonDataSet.Tables)
{
Type EType = Type.GetType("POST.API.Models." + Table.TableName);
POST.API.Models.BaseEntity E = (POST.API.Models.BaseEntity)Activator.CreateInstance(EType);
Services.TypeEntityService<EType> S = new Services.TypeEntityService<EType>();
foreach (DataRow Row in Table.Rows)
{
// Set properties of E and call Create method of S
}
}
}
I've clearly misunderstood something fundamental, because that code won't compile. On this line of code:
Services.TypeEntityService<EType> S = new Services.TypeEntityService<EType>();
...I get an error on my references to EType, with the compiler complaining "The type or namespace `EType' could not be found."
So, obviously, that reference, there, cannot be evaluated at runtime. Which, then, makes me wonder how on earth do I do this. All the related topics seem to yield no satisfactory answer -- at least not in a way that makes sense in the context of my own implementation.
You need to create the Services.TypeEntityService<EType> with the Activator, something like below...
Type EType = Type.GetType("POST.API.Models." + Table.TableName);
Type[] typeArgs = { EType };
var generic = typeof(Services.TypeEntityService<>).MakeGenericType(typeArgs);
var S = Activator.CreateInstance(generic);
Well you have a variabl of type System.Type in EType. You cannot use Type instances for generic types. You shouldchange your implementation of Services.TypeEntityService to take runtime Type instances (e.g TypeEntityService(System.Type type)).
Edit: Or actually like the other answer states, use Reflection to build Generic parameters from your Type instance. That makes more sense.
I have the following code (ready to paste into linqpad). Basically there is a base 'model' class with a 'treemembermodel' class deriving from it.
class MemberModel
{
public List<Member> Team = new List<Member>();
public void IncreaseAge()
{
// Would like this to modify the Treemember
Team[0].Age++;
}
}
class TreeMemberModel : MemberModel
{
public new List<TreeMember> Team = new List<TreeMember>();
public void UpdateName(string newName)
{
}
}
Same for a Member class with a 'TreeMember' deriving from it.
public class Member
{
public string Name;
public int Age;
}
public class TreeMember: Member
{
public string ParentName;
}
The idea is that the base member model stores a list of normal members, while the 'tree' model (and member) classes ensure a 'tree' structure by maintaining integrity between parent and subordinate fields.
This all seemed a good idea (the below example is stylised) - I figured any 'common' methods applying to both object types (eg 'increaseage' below) would modify the treeMember in the TreeModel when called from that model. However, calling 'increaseage' in my main method instead tries to access the 'Team' collection from the base 'MemberModel' instead, which of course doesn't exist.
void Main()
{
TreeMemberModel t = new TreeMemberModel();
t.Team.Add(new TreeMember() { Name = "original", Age = 10 });
// Call method within derived class
t.UpdateName("changed");
Console.WriteLine (t.Team[0].Name);
// Call method which drops down to base class method, and would like it to modify the TreeMemberModel's Team collection, not MemberModel's.
t.IncreaseAge();
Console.WriteLine (t.Team[0].Age);
}
I figure this is a covariance issue, and have tried a few things around making classes generic and so forth. Can I configure this so it drops back to the base class's methods while manipulating the elements of the TreeModel collection?
You should try following:
class MemberModel<TMember> where TMember : Member
{
public List<TMember> Team = new List<TMember>();
public void IncreaseAge()
{
// Would like this to modify the Treemember
Team[0].Age++;
}
}
class TreeMemberModel : MemberModel<TreeMember>
{
public void UpdateName(string newName)
{
}
}
I am working on an application which currently creates data entity objects from the results of a sql query. In the database 3 of the tables are very similar but have several different properties.
My initial plan was to create 3 different classes, even though each class is very similar. However when I came to create the method which returns a list of objects, I have hit a stumbling block as the return type will be different depending on which mode the application is in.
e.g.
public class A
{
public int Id {get;}
public string Name {get;}
}
public class B
{
public int Id {get;}
public string Name {get;}
public string ExtraInfo {get;}
}
public class MainScreen
{
...
this.resultsGrid.DataSource = LoadData();
}
I would prefer not to write one method to load a list of each data type.
What should the return type of LoadData() be, to allow it to be generic as possible.
What is the most elegant way of dealing with this scenario?
Thanks,
Sean
You should have inheritance to allow polymorphism, so you would have a base class that all entities included in the data binding derive from it.
Then, you can have a mid-base class to have some shared properties like Name and ID.
Base class:
public abstract class Entity
{
}
Entity with Name and ID:
public class NameAndIDEntity : Entity
{
public int Id { get; set; }
public string Name { get; set; }
}
Entity with Name, ID and ExtraInfo:
public class NameIDAndExtraEntity : NameAndIDEntity
{
public string ExtraInfo { get; set; }
}
Entity with other information (can't be derived from NameAndIDEntity), derives from Entity so it can be included in the data binding:
public class OtherInformationEntity : Entity
{
public int Age { get; set; }
}
Finally, you can make the LoadData return type Entity.
Simples!
Create a class ListItem (with properties Id and Name, I presume). In your factory class/method, make instances of that class from the records into a List and bind the datasource to the list.
Don't be scared to create specialised classes for your UI.
UPDATE: Forgot to mention. Avoid inheritance as much as possible.
First you can create an inheitance tree in your project, where base class holds a shared/common properties among set of dfferent types
Second you can retrieve from the query anonymous type and after map it to a known type by mapping them to a real type, like from Jon Skeet's blog Horrible grotty hack: returning an anonymous type instance
That means that you need by the way know which query what type returns (can not avoid that), but this can reduce amount of fraction you need to add to your code, like from example:
static class GrottyHacks
{
internal static T Cast<T>(object target, T example) //CAST TO SPECIFIED TYPE
{
return (T) target;
}
}
class CheesecakeFactory
{
static object CreateCheesecake()
{
return new { Fruit="Strawberry", Topping="Chocolate" };
}
static void Main()
{
object weaklyTyped = CreateCheesecake(); //ANONYMOUS TYPE GENERATION
var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
new { Fruit="", Topping="" }); //"MAPPING"
Console.WriteLine("Cheesecake: {0} ({1})",
stronglyTyped.Fruit, stronglyTyped.Topping);
}
}
In my model I have these entities:
public interface IOrder
{
string Name {get;set;}
List<IProduct> OrderedProducts {get;set;}
}
public interface IProduct {}
In partial class generated by linq-to-sql I map these properties on my entity properties:
public partial class Order : IOrder
{
List<IProduct> OrderedProducts
{
get { return this.L2SQLProducts.Cast<IProduct>.ToList(); }
set { this.L2SQLProducts = ??? }
}
}
How should setter look like?
EDIT: Purpose of this is to have interface that I could MOCK for unit testing or change linq-to-sql to another DAL.
汗了。。。
这样不怕性能问题吗?
如果我这样编码呢?
var a = xx.OrderedProducts[0];
var b = xx.OrderedProducts[1];
这样不是要该问了两次数据库吗?
translate to english:
what?
this can be slowly when run.
if I code like this:
var a = xx.OrderedProducts[0];
var b = xx.OrderedProducts[1];
Is that you will access to the database twice?
An EntitySet implements IList so rather than dealing with a derived List instance, why not use the interface IList and simply assign it directly, as in:
public interface IOrder
{
string Name {get;set;}
IList<IProduct> OrderedProducts {get;set;}
}
Alternatively, you could instantiate an entity set, and copy over the objects in the list to that entity set. There may be alternative consequences to this.
List<IProduct> OrderedProducts
{
get { return this.L2SQLProducts.Cast<IProduct>.ToList(); }
set {
this.L2SQLProducts = new EntitySet<IProduct>();
foreach (var entry in value)
this.L2SQLProducts.Add(entry);
}
}
HTH.