I have an ASP MVC Web Api project that outputs json using json.net. I have for example the following models:
public class ModelA
{
public int Id {get;set;}
public string Name {get;set;}
[JsonIgnore]
public int TypeModelId {get;set;}
public virtual TypeModel TypeModel {get;set;}
}
public class TypeModel
{
[JsonIgnore]
public int Id {get;set;}
public string Name {get;set;}
[JsonIgnore]
public virtual IList<ModelA> ModelAs {get;set;}
}
When I serialize a ModelA the output will be something like this:
[
{
"Id": 1,
"Name": "test",
"TypeModel": {
"Name": "testtype1"
}
}
]
Is it possible using json.net to have an output like this..
[
{
"Id": 1,
"Name": "test",
"TypeModel": "testtype1"
}
]
..or do I have to copy the contents of ModelA to a new class which stores the TypeModel relation as string instead of reference? Maybe there are better solutions?
As you say the only way to do this is with a DTO. This is because, as you indicated, the type of TypeModel is a class TypeModel and not a string. If you are using Linq you could also just use an anonymous type in the following way.
return db.ModelAs.Single(x=>x.Id == id).Select(x=> new{
x.Id,
x.Name,
TypeModel = x.TypeModel.Name
});
Actually this is not true, json.net can handle loop reference handling, dto is an old method, still good, but you can enable in the serializer the option to handle loopreferences as long as it is mvc5 and maybe also mvc4.
More details here: https://stackoverflow.com/a/23044770/1345207
Related
I have found many examples of how to persist an individual member of an entity as JSON, but what about if I want the entire class persisted as JSON? For example, if I have a class:
public class MyObj
{
public int Id {get; set;}
public int[] Array {get; set;}
}
and I want it persisted in the database as:
Table: MyObjTable
Row 1:
Id: 1
MyObj: {"id": 1, "array": [1, 2, 3]}
Row 2:
Id: 2
MyObj: {"id": 2, "array": [4, 5, 6]}
What would the DbContext class look like?
It's not really about the DbContext class. You'd need to provide a property getter and setter for "MyObj" and prevent mapping of Array:
public class MyObj
{
public int Id { get; set; }
[NotMapped]
public int[] Array { get; set; }
public string Obj
{
get
{
return JsonSerializer.Serialize(this);
}
set
{
MyObj clone = JsonSerialiser.Deserialize<MyObj>(value);
Array = clone.Array;
}
}
}
If you can wait until November 2021, then you are able to use a native feature available in EF Core 6 (.NET 6.0). By then properties can be decorated with a JsonColumn (name can be different until release). Complex (and also simple / primitive) values can then be serialized into json within a column in the database. You can read more in the devblogs.
I want to create a TimeStamp field in Inherited class like this:
[Table("TABLE_A")]
public class A
{
public int ID {get;set;}
public string Name {get;set;}
}
[Table("TABLE_B")]
public class B : A
{
public string Address {get;set;}
[TimeStamp]
public byte[] RowVersion {get;set;}
}
but failed, how can I do here ?
You will see error
Type 'B' defines new concurrency requirements that are not allowed for
subtypes of base EntitySet types.
That means exactly what error says. Entity Framework do not support concurrency checks in derived types. You will see same error if you'll add simple concurrency check instead of timestamp:
[Table("TABLE_B")]
public class B : A
{
[ConcurrencyCheck]
public string Address { get; set; }
}
If you will move concurrency checking to base class, then it will work, but only on base type. If you need checking to be performed on derived type, I think you should use Stored Procedure for updating entity.
UPDATE:
I updated the classes to look more like my actual model. When I remove the Assignable property from the Document base entity, everything works.
Is something like this possible to map in EF 4.1?
public abstract class Entity
{
public Guid Id {get;set;}
}
public abstract class Assignable:Entity
{
}
public class Contact: Assignable
{
public string Name {get;set;}
}
public class Partner: Assignable
{
public string Area {get;set;}
}
public abstract class Document: Entity {
public Guid AssignedToId {get;set}
public Assignable AssignedTo {get;set;
}
public class Submittal: Document
{
public string SomeProperty {get;set;}
}
Mapping:
public class EntityConfiguration<TEntity> : EntityTypeConfiguration<TEntity>
where TEntity : Entity
{
protected EntityConfiguration()
{
HasKey(e => e.Id);
Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class AssignableEntityMapping<TEntity>: EntityConfiguration<TEntity> where TEntity: Assignable
{
}
public class DocumentEntityMapping<TEntity>: EntityConfiguration<TEntity>
{
public DocumentEntityMapping()
{
HasOptional(e => e.AssignedTo).WithMany().HasForeignKey(e => e.AssignedToId);
}
}
public class ContactMapping: AssignableEntityMapping<Contact>
{
....
}
public class PartnerMapping: AssignableEntityMapping<Partner>
{
....
}
public class SubmittalMapping: DocumentEntityMapping<Submittal>
{
....
}
If this is possible how do I setup the mapping so that the AssignedToId is a foreign key to one of the concrete entities? I created a mapping for the Document entity and I get the error: "The property 'Id' is not a declared property on type..." I am guessing it is because EF doesn't know how to discriminate what the AssignedToId refers to?
I had a lot of extra architecture around the Contact and Partner entities that I could not use something like a TPH (Table Per Hierarchy) approach. EF doesn't know how to map the AssignedToId in the Document class since i was trying to use Table per Concrete Type (TPC) at the same time. I ended up just adding all the "assignable" types to the Document class for now. If there is another way around this, I'd like to know.
public abstract class Document: Entity
{
public Guid? AssignedContactId {get;set;}
public Contact AssignedContact {get;set;}
public Guid? AssignedPartnerId {get;set;}
public Partner AssignedPartner {get;set;}
}
I have a class Movement inheriting from TimeBlock. TimeBlock is provided for me and I can't change it. TimeBlock provides a DurationDescription property and I want to display it. However I always use LabelFor etc which means I need to have Display metadata on DurationDescription so I can have "Duration Desc." etc.
How do I add metadata to an inherited class with EF Code First. Am I supposed to use buddy metadata?
Yes, you need to use the MetadataTypeattribute. It will work fine also with inheritance like with partial classes:
public class Base
{
public string Prop1 { get; set; }
}
[MetadataType(typeof(ClassMetadata))]
public class Class : Base
{
[DisplayName("My prop 2")]
public string Prop2 { get; set; }
class ClassMetadata
{
[DisplayName("My prop 1")]
public string Prop1 { get; set; }
}
}
On the UI the properties will be displayed as "My prop 1" and "My prop 2".
I have a C# project that uses the EF CodeFirst approach. My problem is how EF is interpreting my classes and generating the database tables. EF is inferring too many things and my resulting db is not the way I would like. Specifically, it is generating additional id columns in one of my mapping classes.
Here are my POCO classes:
public partial class Attribute
{
public int Id {get;set;}
public string Name {get;set;}
public virtual ICollection<EntityAttribute> EntityAttributes {get;set;}
}
public partial class Grant
{
public int Id {get;set;}
public string Name {get;set;}
public virtual ICollection<EntityAttribute> EntityAttributes {get;set;}
}
public partial class Donor
{
public int Id {get;set;}
public string Name {get;set;}
public virtual ICollection<EntityAttribute> EntityAttributes {get;set;}
}
public enum EntityTypeEnum
{
Grant = 1,
Donor = 2
}
public partial class EntityAttribute
{
public int Id {get;set;}
public int EntityId {get;set;}
public int AttributeId {get;set;}
public int EntityTypeId {get;set;}
public EntityTypeEnum EntityType
{
get{return (EntityTypeEnum)this.EntityTypeId;}
set{this.EntityTypeId = (int)value;}
}
public virtual Grant Grant {get;set;}
public virtual Donor Donor {get;set;}
}
My mapping classes are typical but here is the EntityAttributeMap class:
public partial class EntityAttributeMap : EntityTypeConfiguration<EntityAttribute>
{
public EntityAttributeMap()
{
this.ToTable("EntityAttribute");
this.HasKey(ea => ea.Id);
this.Property(ea => ea.EntityTypeId).IsRequired();
this.Ignore(ea => ea.EntityType);
this.HasRequired(ea => ea.Grant)
.WithMany(g => g.EntityAttributes)
.HasForeignKey(ea => ea.EntityId);
this.HasRequired(ea => ea.Donor)
.WithMany(d => d.EntityAttributes)
.HasForeignKey(ea => ea.EntityId);
this.HasRequired(ea => ea.Attribute)
.WithMany(a => a.EntityAttributes)
.HasForeignKey(ea => ea.AttributeId)
}
}
All of my unit tests perform as expected. However, the table EntityAttribute gets rendered with DonorId and GrantId columns. I don't want this as I actually have dozens of other "EntityTypes" that will be used for this scenario. That is why I chose the EntityTypeEnum class.
What am I doing wrong? Or is there another way I should be mapping these so EF handles things the way I want. Thanks.
The EF doesn't support enums at all, as of V4 CTP 5. They might be included in the next release.
Having said that, the schema looks (to me; it's not clear from your post, and your intentions may be different) too close to an EAV for my comfort. For the usual reasons (Google it) I dislike these, and wouldn't want that sort of model even with enum support in the EF.
Why not map to another entity type instead of an enum?
If you ask a question in the form of "Here are my business needs; what is the best schema for this?" you may get a better answer.