Mapping foreign keys of subclasses - c#

I have following abstract class:
public abstract class ClauseComponent
{
public int ClauseComponentId { get; set; }
public abstract string[] Determinate(ClimateChart chart);
public abstract List<ClauseComponent> GiveCorrectPath(ClimateChart chart);
public abstract String GetHtmlCode(Boolean isYes);
public virtual void Add(Boolean soort, ClauseComponent component)
{
throw new ApplicationException();
}
public ClauseComponent()
{
}
}
The Clause class inherits from the abstract class:
public class Clause : ClauseComponent
{
public virtual ClauseComponent YesClause { get; set; }
public virtual ClauseComponent NoClause { get; set; }
public String Name { get; private set; }
public virtual Parameter Par1 { get; set; }
public virtual Parameter Par2 { get; set; }
public int Waarde { get; set; }
public String Operator { get; set; }
public Clause()
{
}
public Clause(String name, Parameter par1, String op, int waarde)
{
this.Name = name;
this.Par1 = par1;
this.Operator = op;
this.Waarde = waarde;
}
public Clause(String name, Parameter par1, Parameter par2)
{
this.Name = name;
this.Par1 = par1;
this.Par2 = par2;
}
}
This is the mapper of the abstract class (I dont have a mapper for the subclass):
public ClauseComponentsMapper()
{
ToTable("ClauseComponents");
// Primary key
HasKey(c => c.ClauseComponentId);
// Properties
Property(c => c.ClauseComponentId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
I have this in my DB:
Now I want to give a proper name to the mapping, how can I accomplish this?
I have never done the mapping on abstract classes and subclasses so I'm a little bit in the blue here.

One way is to create properties for the mapping columns, and in the mapping class, map the virtual property using the mapping column property.
E.g.
public class Clause : ClauseComponent
{
public int MyCustomPar1Id{ get; set; }
[ForeignKey("MyCustomPar1Id")]
public virtual Parameter Par1 { get; set; }
}
Or Fluent Api:
modelBuilder.Entity<Clause >().HasRequired(p => p.Par1 ) // Or Optional
.HasForeignKey(p => p.MyCustomPar1Id);

Related

Different parent classes map different subclasses problem

Profile.cs
public class TestConfigProfile : Profile
{
public TestConfigProfile()
{
CreateMap<BaseBO, BaseVO>();
CreateMap<A_BO, A_VO>();
CreateMap<SubBO1, SubVO1>();
}
public class A_BO
{
public BaseBO Sub { get; set; }
}
public class A_VO
{
public BaseVO Sub { get; set; }
}
public class BaseBO
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BaseVO
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubBO1 : BaseBO
{
public int Size { get; set; }
}
public class SubVO1 : BaseVO
{
public int Size { get; set; }
}
}
test code like this...
public void TestConvert()
{
TestConfigProfile.A_BO bo = new TestConfigProfile.A_BO();
bo.Sub = new TestConfigProfile.SubBO1()
{
Id = 1,
Name = "SubBO1",
Size = 4421
};
TestConfigProfile.A_VO vo = _mapper.Map<TestConfigProfile.A_BO, TestConfigProfile.A_VO>(bo);
}
The result is as follows, but it does not meet my expectations, how can I configure this? Also I don't want to use a parent class.
Successfully mapped to a subclass.
With AutoMapper, mapping inheritance is opt-in.
Therefore, when you map from BaseBO to BaseVO, you need to include the derived mappings.
public TestConfigProfile()
{
CreateMap<BaseBO, BaseVO>()
.Include<SubBO1, SubVO1>(); // Include necessary derived mappings
CreateMap<A_BO, A_VO>();
CreateMap<SubBO1, SubVO1>();
}
See this working example.

How do I create a generic List using abstract class?

I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.

Automapper mapping generic property from ISource => IDest, then specifying remaining mappings

My situation is I must merge the name of a related object to a name property on many different models. Those models have implementations which need their own configurations.
What I've tried is:
ProjectFrom.cs
public interface IProjectFrom {
int Id { get; set; }
}
public class ProjectFromConcreteA : IProjectFrom {
public int Id { get; set; }
public string SomeProp { get; set; }
}
ProjectTo.cs
public interface IProjectTo {
string Name { get; set; }
}
public class ProjectToConcreteA : IProjectTo {
public string Name { get; set; }
public string SomeProp { get; set; }
}
Profile.cs
CreateMap<IProjectFrom, IProjectTo>()
.ForMember(projection => projection.Name, expression => expression.ResolveUsing<ProjectionResolver>());
CreateMap<ProjectFromConcreteA, ProjectToConcreteA>()
.ForMember(concreteTo => concreteTo.SomeProp, expression => expression.MapFrom(concreteFrom => concreteFrom.SomeProp));
ProjectionResolver.cs
public class ProjectionResolver : IValueResolver<IProjectFrom, IProjectTo, string>
{
private readonly IDependency _dependency;
public ProjectionResolver(IDependency dependency)
{
_dependency = dependency;
}
public string Resolve(IProjectFrom source, IProjectTo destination,
string destMember, ResolutionContext context)
{
return _dependency.GetStringFor(source.Id);
}
}
However this does not seem to work as my Resolver is never used. Am I missing a step?

How to seed this entity model framework for complex relationship

I have the following scenario. We need to be able to fill forms for some tables, examples Companies (Empresa in Spanish), however we want the administrator to be able to extend the entity itself with additional fields.
I designed the following classes, and I need to seed at least one row, however its unclear to me how to seed one row of type CampoAdicional
Entity class:
public abstract class Entidad
{
[Key]
public int Id { get; set; }
}
Company Class (Empresas)
public class Empresa : Entidad
{
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public virtual ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
And the Additional Fields (Campo Adicional)
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
public virtual Entidad Entidad { get; set; }
}
However I dont know how to seed this class or table, because entity should be of subtype Company
Obviously the typeof doesnt compile
context.CampoAdicionals.Add(new CampoAdicional() { Entidad = typeof(Empresa), Id = 1, NombreCampo = "TwitterHandle", TipoCampo = Tiposcampo.TextoUnaLinea });
Update 1: Please note that the additional fields are for the entire entity company not for each company.
Unfortunately, I don't think you'll be able to use EF to automatically create that kind of relationship. You might be able to do something similar with special getters and such:
public class Entidad
{
// stuff...
public IEnumerable<CampoAdicional> CamposAdicionales
{
get { return CampoAdicional.GetAll(this); }
}
}
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
protected string EntidadType { get; set; }
// You will need some mapping between Type and the EntidadType string
// that will be stored in the database.
// Maybe Type.FullName and Type.GetType(string)?
protected Type MapEntidadTypeToType();
protected string MapTypeToEntidadType(Type t);
[NotMapped]
public Type
{
get { return MapEntidadTypeToType(); }
// maybe also check that Entidad.IsAssignableFrom(value) == true
set { EntidadType = MapTypeToEntidadType(value); }
}
public static IEnumerable<CampoAdicional> GetAll(Entidad ent)
{
return context.CampoAdicionals
.Where(a => a.EntidadType == MapTypeToEntidadType(ent.GetType()));
}
}

Table Per Inheritance and Repository

I'm using Table per Hierarchy (TPH).
For example we have a base class for all entities:
public abstract class Entity
{
public virtual int Id { get; set; }
public virtual bool IsTransient()
{
return Id == default(int);
}
}
And base class for several entitites:
public abstract class Event:Entity
{
[MaxLength(50)]
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
[MaxLength(100)]
public string ShortDescription { get; set; }
[Required]
public DateTime PublishDate { get; set; }
public int Duration { get; set; }
}
public class Film:Event
{
public string Director { get; set; }
public string ActorList { get; set; }
public override string ToString()
{
return Name;
}
}
public class Concert:Event
{
public string Genre { get; set; }
public override string ToString()
{
return Name;
}
}
My context:
public class MyContext:DbContext
{
public MyContext():base(ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString)
{
}
public DbSet<Event> Events { get; set; }
public virtual void Commit()
{
base.SaveChanges();
}
}
This is base repository:
public class GenericRepository : IRepository
{
//...
public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
{
return GetQuery<TEntity>().AsEnumerable();
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
}
private string GetEntityName<TEntity>() where TEntity : class
{
string entitySetName = ((IObjectContextAdapter)DbContext).ObjectContext
.MetadataWorkspace
.GetEntityContainer(((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, DataSpace.CSpace)
.BaseEntitySets.First(bes => bes.ElementType.Name == typeof(TEntity).Name).Name;
return string.Format("{0}.{1}", ((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, entitySetName);
}
}
Next, create context and repository:
var context = new MyContext();
EventRepository repository = new EventRepository(context);
var films = repository.GetAll<Film>();
But I get exception (in the GetEntityName method): the sequence does not have elements.
I think it because there are no Film table in the DB. How to solve this problem?
I don't see the need of GetEntityName in the repository you are showing. For GetQuery you can use the DbContext API directly and don't need to access the underlying ObjectContext or MetadataWorkspace:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
return DbContext.Set<TEntity>();
}
This returns a DbSet<TEntity> (which is an IQueryable<TEntity>). I am not 100% sure if that also works if TEntity is derived but the MSDN documentation about DbSet<TEntity> says: "The type can be derived type as well as base type." So, I would hope that the Set<TEntity>() method is allowed for derived types as well.

Categories

Resources