I have an interface
internal interface IAttributeModel
{
string Name { get; set; }
int Id { get; set; }
string AttType { get; set; }
}
And a class implementing the interface
public class Currency:IAttributeModel
{
private string _type;
public Currency()
{
_type = "Currency";
}
public string Name { get; set; }
public int Id { get; set; }
string IAttributeModel.AttType
{
get { return _type; }
set { _type = value; }
}
}
Above class has implemented one property explicitly.
And my entity does look like the following.
public class ProviderAttribute
{
public int Id { get; set; }
public string Name { get; set; }
public string AttType { get; set; }
}
And I have created a simple mapping
Mapper.CreateMap<Entities.ProviderAttribute, Models.Currency>();
Mapper.CreateMap<Models.Currency, Entities.ProviderAttribute>();
Above mapping always setting overwriting Currency object's AttType property to null at the time of mapping. I this is probably happening because Currency is explicitly implementing IAttributeModel interface and my mapping is not able to find that.
How can I enforce my mapping to look into IAttributeModel interface.
Thanks.
You have to cast the Currency object to the Interface type IAttributeModel:
Mapper.Map((IAttributeModel)currency, providerAttribute);
And you have to let AutoMapper know how to map the interface:
Mapper.CreateMap<ProviderAttribute, Currency>();
Mapper.CreateMap<Currency, ProviderAttribute>();
Mapper.CreateMap<ProviderAttribute, IAttributeModel>();
Mapper.CreateMap<IAttributeModel, ProviderAttribute>();
Related
I am just getting started with Entity Framework Core within C#, and I'm trying to set up a class structure where one class has a field that is another class. I have found that, when the classes do not have constructors, the code runs fine. However, when I introduce a constructor as follows:
public class InterestEF
{
public InterestEF(string id, int numTimesInterestSelected, AdminEditEF lastEdit, ICollection<AdminEditEF> allEdits)
{
this.id = id;
this.numTimesInterestSelected = numTimesInterestSelected;
this.lastEdit = lastEdit;
this.allEdits = allEdits;
}
[Key]
public string id { get; set; }
public int numTimesInterestSelected { get; set; }
public AdminEditEF lastEdit { get; set; }
public virtual ICollection<AdminEditEF> allEdits { get; set; }
}
public class AdminEditEF
{
public AdminEditEF(string id, string adminIdEditedBy, DateTime dateEdited, string changesMade, string reasonsForChanges, string idOfEditedEntity, EntityTypeEdited entityTypeEdited)
{
this.id = id;
AdminIdEditedBy = adminIdEditedBy;
this.dateEdited = dateEdited;
this.changesMade = changesMade;
this.reasonsForChanges = reasonsForChanges;
this.idOfEditedEntity = idOfEditedEntity;
this.entityTypeEdited = entityTypeEdited;
}
[Key]
public string id { get; set; }
public string AdminIdEditedBy { get; set; }
public DateTime dateEdited { get; set; }
public string changesMade { get; set; }
public string reasonsForChanges { get; set; }
public string idOfEditedEntity { get; set; }
public EntityTypeEdited entityTypeEdited { get; set; }
}
public class MySQLEFContext : DbContext
{
public DbSet<AdminEditEF> AdminEdits { get; set; }
public DbSet<InterestEF> interests { get; set; }
public MySQLEFContext(DbContextOptions<MySQLEFContext> options): base(options) { }
}
I get the following error:
System.InvalidOperationException: 'No suitable constructor found for entity type 'InterestEF'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'lastEdit' in 'InterestEF(string id, int numTimesInterestSelected, AdminEdit lastEdit)'.'
Basically, I'm just wondering if it's possible to have a class that has classes as fields that also has a set of constructors with parameters I can call elsewhere within code?
Any help would be greatly appreciated.
Thank you so much for reading!
From the docs:
EF Core cannot set navigation properties (such as Blog or Posts above) using a constructor.
So you would need a constructor that looks like this:
public InterestEF(string id, int numTimesInterestSelected)
{
this.id = id;
this.numTimesInterestSelected = numTimesInterestSelected;
}
Or a parameterless one:
public InterestEF()
{
}
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.
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()));
}
}
I have some classes like this one below:
public class Driver
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string CreatedById { get; set; }
public DateTime? CreatedTime { get; set; }
public string UpdatedById { get; set; }
public DateTime? UpdatedTime { get; set; }
}
Please note the 4 last fields.
Everytime an entity is created/updated, I call a function to update 4 fields which are present in all my classes (CreatedTime, CreatedById, UpdatedTime, UpdatedById).
The example below is for the Driver class:
private void UpdateAdditionalData(EntityInfo entityInfo)
{
var userId = Current.User.Identity.Name;
var entity = (Driver)entityInfo.Entity;
...
if (entityInfo.EntityState == EntityState.Added)
{
entity.CreatedTime = DateTime.Now;
entity.CreatedById = userId;
}
if (entityInfo.EntityState == EntityState.Modified)
{
entity.UpdatedTime = DateTime.Now;
entity.UpdatedById = userId;
}
As you can see, I declare an entity variable which is casted with a prefix (Driver).
Now I would like to adjust this code to be able to reference any classes and not specifically the Driver class.
I tried something like:
private void UpdateAdditionalData(EntityInfo entityInfo)
{
var userId = Current.User.Identity.Name.ToUpper();
var typed = entityInfo.Entity.GetType().Name;
var entity = (typed)entityInfo.Entity;
...
So I declared a typed variable which is the name of the entity for casting my entity.
But it doesn't work, I got an error: the type of namespace 'typed' could not be found.
Any idea how can I accomplish this?
Thanks.
Bottom line of why this doesn't work is normally types are static, compile time data. You're trying to get it at runtime. This means using reflection and invocations which will be complicated.
I think what you want is an interface to work with that's shared across entities.
public interface Auditable
{
string CreatedById { get; set; }
DateTime? CreatedTime { get; set; }
string UpdatedById { get; set; }
DateTime? UpdatedTime { get; set; }
}
Then have your entity implement that interface. Any entity that implements that interface can be used...
var userId = Current.User.Identity.Name.ToUpper();
var entity = entityInfo.Entity as Auditable;
if (entity != null) { /* set the audit values */ }
If the entity doesn't implement the interface then you won't actually set the audit values.
Unless I misunderstand you, you need a base type for your entities?
public class Driver : EntityBase
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
Base type...
public class EntityBase
{
public string CreatedById { get; set; }
public DateTime? CreatedTime { get; set; }
public string UpdatedById { get; set; }
public DateTime? UpdatedTime { get; set; }
}
Then you could simply cast to your base type?
I have the following code files:
public interface IMod
{
string Name { get; set; }
string Description { get; set; }
bool Enabled { get; set; }
List<IClassFile> ClassFiles { get; set; }
}
public interface IClassFile
{
string Path { get; set; }
string FileName { get; set; }
bool Enabled { get; set; }
}
public class ClassFile : IClassFile
{
public string Path { get; set; }
public string FileName { get { return System.IO.Path.GetFileName(Path); } }
public bool Enabled { get; set; }
....
}
public class ZippedMod : IMod
{
public string Name { get; set; }
public string Description { get; set; }
public bool Enabled { get; set; }
public List<IClassFile> ClassFiles { get; set; }
....
}
public class ConfigurationBlock
{
public List<IMod> Mods { get; set; }
....
}
Throughout the course of my program, I add a few ZippedMods to the ConfigurationBlock, but now I want to serialize them. I tried doing:
using (var stream = new StreamWriter("config.xml"))
{
var ser = new XmlSerializer(typeof(ConfigurationBlock));
ser.Serialize(stream, configBlock);
}
But I get this error:
There was an error reflecting type 'MinecraftModManager.ConfigurationBlock'.
|-Inner Exception:
Cannot serialize member 'MinecraftModManager.ConfigurationBlock.Mods' of type 'System.Collections.Generic.List`1[[MinecraftModManager.IMod, MinecraftModManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details.
|-Inner Exception:
Cannot serialize member MinecraftModManager.ConfigurationBlock.Mods of type MinecraftModManager.IMod because it is an interface.
Help?
You can't serialize an interface due to the abstract nature of them. Lots of concrete types can implement the same interface, so it creates an ambiguity. You must use a concrete type.
You can't serialize an interface, only a concrete class with the default XmlSerializer. You may be able to implement IXmlSerializable to override this behavior.