ID based NPC system - entity-framework

I have a bunch of different npcs. They all have different properties and different AI, so I have made a separate class for each type, and derive it from a base class Entity. I want to make it so that I can assign an ID to each type of Entity, so I can just call CreateNewEntity(int id, Vector2 position). How would I do this? I also have a feeling that I'm designing this badly. Is that true

There are a couple of ways you could do this.
You could create an Attribute that will give the type some metadata such as ID
e.g.
public class RenderableEntity{
}
[EntityTypeAttribute(Name = "Wizard"]
public class Wizard : RenderableEntity{
}
you could then bundle them all in a namespace or a logical container and create the type as follows
pseudo:
//Create Entity
//Type Name
string entityName = "Wizard";
//Get the Type
from the namespace where the type has the custom attribute applied to it. Get the type
//Activator.Create(typeof(TheWizardType))
The other is that you could just get the Type where the name matches the string you passed into your create method.

Inheriting from a base entity sounds like a good approach. Anything that is shared between all your objects should be in the base entity, and anything that is specific to each type of NPC, should be in their own class. Things that have the same purpose, but different implementation (like AI, or CreateNewEntity) should be marked virtual, so the methods can be called from a list of base entities, but the correct implementation will run. CreateNewEntity should probably be a constructor.
example:
public class Entity
{
public int Id { get; protected set; }
public Vector2 Position { get; protected set; }
public int Strength { get; protected set; }
public Entity(int id, Vector2 position)
{
Id = id;
Position = position;
Strength = 1;
}
public virtual RunAI()
{
Position.X = Position.X + 1;
}
}
public class SuperHero
{
public SuperHero(int id, Vector2 position) : base (id, position)
{
Strength = 20;
}
public override void RunAI()
{
Position.X = Position.X + 500;
}
}

It sounds like the flyweight pattern would be beneficial here: http://www.primos.com.au/primos/Default.aspx?PageContentID=27&tabid=65
You probably want to differentiate the core state that makes each entity different and then put as much as possible in the shared instance.

Related

EF Core Hierarchy TPH map two entities to one

I have a base project, which has some basic entities. Let's use BaseUser as one. In the child project, it references base project and User class inherits from that BaseUser. The only difference is the User class has List<Blogs>, so no additional properties, no change in mapping.
When I query Users, it does not find any because they are created as BaseUser and the query has discriminator value of "User". I don't want to query "BaseUser" because I want the relational property of List<Blogs>.
Is there any way to tell EF to basically treat these classes as one? Is there a better way to handle the split? (obviously base project has no concept of blogs, so cannot move the List to the base)
Some sample classes as requested
/* Base Project (nuget package created) */
public class BaseUser {
public int UserId { get; set; }
public int Name { get; set; }
}
public class BaseContext : DbContext {
public DbSet<BaseUser> BaseUsers {get;set;}
}
public class BaseDataInstaller {
BaseContext _ctx;
public BaseDataInstaller( BaseContext ctx ){
_ctx = ctx;
}
public void Install(){
_ctx.BaseUsers.Add( new BaseUser { Name="Demo User 1" } );
_ctx.BaseUsers.Add( new BaseUser { Name="Demo User 2" } );
_ctx.SaveChanges();
}
}
/* Child Project (consumes nuget package)*/
public class User : BaseUser {
List<Blogs> Blogs { get; set; }
}
public class ProjectContext : BaseContext {
public DbSet<User> Users { get; set; }
}
public class SomeService {
ProjectContext _ctx;
public BaseDataInstaller(ProjectContext ctx){
_ctx = ctx;
}
//Finds 0 users
public void PrintUsers(){
var users = _ctx.Users.ToList();
users.ForEach( u=> Console.WriteLine(u.Name) );
}
//Finds Users
public void PrintBaseUsers(){
var users = _ctx.BaseUsers.ToList();
users.ForEach( u=> Console.WriteLine(u.Name) );
}
}
Comparing the SQL generated, there is a discriminator added
where Discriminator = 'BaseUser' or where Discriminator = 'User'
There are no properties which are different between the two, just the relationship with the blogs.
So is there a way to either make both have the same Discriminator value or another way to solve this?
UPDATE 1
The discriminator only appears if the DbContext knows about BOTH entities. if it only knows about the one, it is happy to map onto the table. Even if the child inherits from the base, it still doesn't need a discriminator. So I think the challenge is to re-work the base so the context doesn't know about the base. This does feel like a workaround though. Maybe the structure should change:
instead of User : BaseUser use a property
User
- int ChildUserId {get; set;}
- BaseUser BaseUser {get; set;}
- SomeObject SomeNavProperty etc
it will mean a new table for each inherited project, but would allow the project to add it's own specific data too...

DDD Entity Framework Value Type

I'm struggling with using EF6 with DDD principles, namely value objects attached to aggregates. I can't seem to get migrations to generate that reflect the model and I feel like I'm fighting the tooling instead of actually being productive. Given that a NoSQL implementation is probably more appropriate, this is what I'm stuck with.
The first thing that I ran into was the lack of support for interface properties on an EF entity. The work around for that was to add concrete properties to the entity for each of the implementations, but not to the interface. When I implemented the interface, I added logic to return the right one. I had to do this in order to get any migrations to create the properties for the Policies. See Fund.LargestBalanceFirstAllocationPolicy and Fund.PercentageBasedAllocationPolicy This was annoyance one.
The current annoyance and the genesis of the question is the PercentageBasedAllocationPolicy.AllocationValues property. No matter what I do, when running add-migration, I don't get any tables or fields to represent the AllocationValues. This is basically a collection of DDD value objects hanging off of another value object, which hangs off of an aggregate.
I'm convinced that the model and code are correct to do what I want, but EF keeps getting in the way. In MongoDB, when dealing with an interface property, it actually stores the object type in a string so that it knows how to rehydrate the object. I'm considering serializing the problem areas here to a blob and storing it on the object now, which is just as evil...
public interface IFund
{
Guid Id {get;}
string ProperName {get;}
IAllocationPolicy AllocationPolicy{get;}
void ChangeAllocationPolicy(IAllocationPolicy newAllocationPolicy)
}
public class Fund : IFund
{
public Fund()
{
}
public Fund(Guid id, string nickName, string properName)
{
Id = id;
Nickname = nickName;
ProperName = properName;
// This is stupid too, but you have to instantiate these objects inorder to save or you get some EF errors. Make sure the properties on these objects are all defaulted to null.
LargestBalanceFirstAllocationPolicy = new LargestBalanceFirstAllocationPolicy();
PercentageBasedAllocationPolicy = new PercentageBasedAllocationPolicy();
}
public Guid Id { get; private set; }
public string ProperName { get; private set; }
// Do not add this to the interface. It's here for EF reasons only. Do not use internally either. Use the interface implemention of AllocationPolicy instead
public LargestBalanceFirstAllocationPolicy LargestBalanceFirstAllocationPolicy
{
get; private set;
}
// Do not add this to the interface. It's here for EF reasons only. Do not use internally either. Use the interface implemention of AllocationPolicy instead
public PercentageBasedAllocationPolicy PercentageBasedAllocationPolicy
{
get; private set;
}
public void ChangeAllocationPolicy(IAllocationPolicy newAllocationPolicy)
{
if (newAllocationPolicy == null) throw new DomainException("Allocation policy is required");
var allocationPolicy = newAllocationPolicy as PercentageBasedAllocationPolicy;
if (allocationPolicy != null) PercentageBasedAllocationPolicy = allocationPolicy;
var policy = newAllocationPolicy as LargestBalanceFirstAllocationPolicy;
if (policy != null ) LargestBalanceFirstAllocationPolicy = policy;
}
public IAllocationPolicy AllocationPolicy
{
get {
if (LargestBalanceFirstAllocationPolicy != null)
return LargestBalanceFirstAllocationPolicy;
if (PercentageBasedAllocationPolicy != null)
return PercentageBasedAllocationPolicy;
return null;
}
}
}
public interface IAllocationPolicy
{
T Accept<T>(IAllocationPolicyVisitor<T> allocationPolicyVisitor);
}
public class LargestBalanceFirstAllocationPolicy : IAllocationPolicy
{
public T Accept<T>(IAllocationPolicyVisitor<T> allocationPolicyVisitor)
{
return allocationPolicyVisitor.Visit(this);
}
}
[ComplexType]
public class PercentageBasedAllocationPolicy : IAllocationPolicy
{
public PercentageBasedAllocationPolicy()
{
AllocationValues = new List<PercentageAllocationPolicyInfo>();
}
public List<PercentageAllocationPolicyInfo> AllocationValues { get; private set; }
public T Accept<T>(IAllocationPolicyVisitor<T> allocationPolicyVisitor)
{
return allocationPolicyVisitor.Visit(this);
}
}
[ComplexType]
public class PercentageAllocationPolicyInfo
{
public Guid AssetId { get; private set; }
public decimal Percentage { get; private set; }
}
A value type (in EF marked as ComplexType) will never have any tables. The reason being is that a value types are (by definition) really just values. They don't have any Id( otherwise they would be enities) thus you can't create a table for them.
also if i review the requirements for complex type in entity framework https://msdn.microsoft.com/en-us/library/bb738472(v=vs.100).aspx i notice that you can't use inheritance on complex types. Thus if you want to use complex type in your entity framework as you've shown here then you need to make your property a PercentageBasedAllocationPolicy instead of an IAllocationPolicy.
Alternatively you could turn it into an entity with automatic generated keys.

Adding Aspects to Derived Classes

I had wrote an aspect that works fine, but only on base classes, on derived classes do nothing.
Please, what I am missing?
The code is as follow
public interface INotifyOnChange : INotifyPropertyChanged
{
void OnPropertyChanged(string propertyName);
}
[Serializable]
[AspectConfiguration(AspectPriority = 10)]
[IntroduceInterface(typeof(INotifyOnChange), OverrideAction = InterfaceOverrideAction.Ignore, AncestorOverrideAction = InterfaceOverrideAction.Ignore)]
[MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict, AllowMultiple = false)]
public sealed class NotifyPropertiesOnChange : InstanceLevelAspect, INotifyOnChange
{
[IntroduceMember(Visibility = Visibility.Family, IsVirtual = true, OverrideAction = MemberOverrideAction.Ignore)]
public void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(Instance, new PropertyChangedEventArgs(propertyName));
[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public event PropertyChangedEventHandler PropertyChanged;
[OnLocationSetValueAdvice, MulticastPointcut(Targets = MulticastTargets.Property, Attributes = MulticastAttributes.Public | MulticastAttributes.Instance)]
public void OnSetValue(LocationInterceptionArgs args)
{
if (args.Value == args.GetCurrentValue())
return;
args.ProceedSetValue();
var notifyOnChange = args.Instance as INotifyOnChange;
notifyOnChange?.OnPropertyChanged(args.Location.PropertyInfo.Name);
}
}
I had also tested with Inheritance = MulticastInheritance.Multicast without success.
However if I have a base class like the one here below it works
[NotifyOnChange]
public class EuroRate
{
public string Currency { get; set; }
public decimal Rate { get; set; }
public DateTime Date { get; set; }
}
but if try on a derived class (removing the aspect from the base class of course) it don't works
public class EuroRate
{
public string Currency { get; set; }
public decimal Rate { get; set; }
public DateTime Date { get; set; }
}
[NotifyPropertiesOnChange]
public class RateModel : EuroRate
{
}
An aspect can only alter the declaration on which it is applied. In case of your aspect (TypeLevelAspect) this means that when applied on RateModel class it can only apply advices on properties declared directly by this class. Properties declared by the base will not be transformed (as you would need to transform the base itself).
In this case, the best option is to rely on the multicast inheritance itself, i.e. apply the aspect on the base class. This would mean that you need to work with the fact that the aspect is applied separately on each class and develop a mechanism for communication between these aspects.
Performance concerns
You might have noticed that this would cause multiple instances of the aspect to exist for a single instance of the target class (depending on levels of inheritance).
You can optimize this by using combination of an instance-level aspect that introduces the OnPropertyChanged method, the event and the interface. This aspect would not be applied directly but through IAspectProvider by the main aspect that would be mere TypeLevelAspect.
Ad your Multicast Inheritance note (see the documentation):
For TypeLevelAspect applied on type [MulticastInheritance.Strict] and [MulticastInheritance.Multicast] are essentially the same.
The reason is that multicast inheritance also takes into account the declaration on which it was multicasted (inherits the multicast itself); i.e. if you apply method level aspect on a type, multicast inheritance will cause derived classes to inherit the aspect for all of its methods.
On the other the strict inheritance will cause only the applied aspect to be inherited, i.e. only overriding methods will have the aspect.

ADO EF Code First Generic Intermediate Class Inheritance mapping

I've got the following requirement that works well in the OO space but I can't seem to get it to map back to the DB using ADO EF code first.
I have numrous products each will have different aspects (attributes but not in the sense of code attributes). For instance ring would have aspects such as mineral type = gold etc whilst a diamond would have an aspec of clarity = VVSI1.
As you can see the products very greatly in thier composition and I want a dynamic way of growing my system.
As such I've created a product class:
public class Product
{
public int id { get; set; }
public string Name { get; set; }
private List<ProductAspect> aspects = new List<ProductAspect>();
public List<ProductAspect> Aspects { get { return aspects; } set { aspects = value; } }
}
It has a list of ProductAspect which is the base class for all aspects moving forward:
public class ProductAspect
{
public int id { get; set; }
public string AspectName { get; set; }
}
I then inherit from the ProductAspect using a generic which alows me to be specific (strongly typed) about my Aspect Value:
public abstract class ProductAspect<T> : ProductAspect
{
public T AspectValue { get; set; }
}
I then create some Aspects that will allow me to decorate my product:
public class StringAspect : ProductAspect<string> { };
public class DecimalAspect : ProductAspect<decimal> { };
public class ImageAspect : ProductAspect<byte[]> { };
I then give the DbContext a try and have tried both TPH and TPC inheritance mappings.
Neither seem to work. The DB model that get's generated doesn't create a foriegn key to the StringAspect or DecimalAspect tables from the Aspect Table.
public class IxamDataContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductAspect> Aspects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
AspectMapping(modelBuilder);
}
private void AspectMapping(DbModelBuilder mb)
{
//TPH
//mb.Entity<ProductAspect>()
// .Map<StringAspect>(m => m.Requires("type").HasValue("sa"))
// .Map<DecimalAspect>(m => m.Requires("type").HasValue("da"));
//TPC
//mb.Entity<StringAspect>().ToTable("StringAspect");
//mb.Entity<DecimalAspect>().ToTable("DecimalAspect");
}
}
Resulting in the following exception for this Seeding code:
Product p = new Product();
p.Name = "Diamond";
p.Aspects.Add(new StringAspect() { AspectName = "History", AspectValue = "Old and long" });
p.Aspects.Add(new DecimalAspect() { AspectName = "Weight", AspectValue= 96.5M });
context.Products.Add(p);
context.SaveChanges();
Excpetion:
EntityType 'StringAspect' does not
exist in the EntitySet
'IxamDataContext.Aspects'. Parameter
name: entity
Any ideas from the EF code first pros out there?
Entity framework doesn't support intermediate non mapped types in inheritance hierarchy. It means that you can't have this inheritance: A (mapped) -> B (not mapped) -> C (mapped). EF also doesn't support mapping generic types. It means that you must remove your generic intermediate class from the hierarchy and move AspectValue to derived types with correct type.
Maybe it's to late, but I would offer you using ComplexType attribute it will allows you to extend your types as you wish.

DataAnnotations MetadataType Class Ignores Base Class Properties

I've run into a bit of a wall in trying to use the .NET DataAnnotations feature to provide simple validations in a derived class. I am marking up my class with the standard annotations included in .NET 4 (from the System.ComponentModel.DataAnnotations namespace), then using the MS Enterprise Library v5 Validation Block to process the rules.
I have a number of objects derived from a common base class, which contains properties common to all of my objects. For validation purposes, I may have different rules for the various classes derived from this class.
Here's a simplified example:
public abstract class PersonBase
{
public int Id { get; set; }
public string Name { get; set; }
}
[MetadataType(typeof(CustomerMD))]
public class Customer : PersonBase
{
}
[MetadataType(typeof(ManagerMD))]
public class Manager : PersonBase
{
}
public class CustomerMD
{
[Required]
[StringLength(20, ErrorMessage="Customer names may not be longer than 20 characters.")]
public object Name { get; set; }
}
public class ManagerMD
{
[Required]
[StringLength(30, ErrorMessage = "Manager names may not be longer than 30 characters.")]
public object Name { get; set; }
}
// calling code
var invalidCustomer = new Customer {Id=1, Name=string.Empty};
var valFactory = EnterpriseLibraryContainer.Current.GetInstance<ValidatorFactory>();
var customerValidator = valFactory.CreateValidator<Customer>();
var validationResults = customerValidator.Validate(invalidCustomer);
// validationResults.IsValid should equal False, but actually equals True.
I have found that I can get the expected validation results if I push the annotations down to the base class, but then I lose the ability to fulfill different requirements for different types. Also, if I put class-specific properties on a derived class and provide metadata for these properties, I get results, but only for these properties, not the properties from the base class.
I haven't yet tried using the EntLib provided validation attributes; I'd prefer to keep the library this lives in free of dependencies from outside the core framework, if at all possible.
Am I missing something, or am I just out of luck here?
I think I have a workable solution for this.
It appears that the Metadata class will not provide validation of properties belonging to the superclass of the target object. In order to get Metadata to work with this, I needed to mark the superclass properties as virtual, then provide overrides for the properties that I wanted to validate.
Example (see question above for original example):
public abstract class PersonBase
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
[MetadataType(typeof(CustomerMD))]
partial class Customer : PersonBase
{
public override string Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
}
With the override in place, the validator works as expected. It's a little more work, but it will get the job done.
I also tried adding annotations to the base class as fallback default rules; this allows me to have a base set of rules and override them as needed on a case by case basis. Looking good.
I run into the same issue and couldn't make it annotate a base class with Attributes using MethadataType. Like Scroll Lock I did the overriding part for base class virtual properties. On top of it I made "shadowing" for the none virtual properties.
public class BaseClass
{
public virtual int Id {get;set;}
public string Name {get;set;}
}
public class DerivedClass
{
[SomeAttribute]
public ovveride int Id {get{ return base.Id;} set{ base.Id = value;}}
[SomeAttribute]
public new string Name {get{ return base.Name;} set{ base.Name = value;}}
}