I've got a problem handling self referencing model in my application using Automapper Projections. This is how my models look like:
public class Letter
{
public int? ParentId {get; set;}
public Letter ParentLetter {get; set;
public int Id {get; set;}
public string Title {get; set;}
public string Content {get; set;}
public DateTime? ReceivedTime {get; set;}
public DateTime? SendingTime {get; set;}
public List<Destination> Destinations {get; set;}
public List<Letter> Responses {get; set;}
}
public class LetterView
{
public int? ParentId {get; set;}
public int Id {get; set;}
public string Title {get; set;}
public string Content {get; set;}
public DateTime? ReceivedTime {get; set;}
public DateTime? SendingTime {get; set;}
public List<DestinationView> Destinations {get; set;}
public List<LetterView> Responses {get; set;}
}
public class Destination
{
public int Id {get; set;}
public string Name {get; set;}
..
}
public class DestinationView
{
public int Id {get; set;}
public string Name {get; set;}
}
// The mapping:
CreateMap<Destination, DestinationView>
CreateMap<Letter, LetterView>
My problem is with mapping Letter to LetterView. The problem is somewhere with the Responses, I just can't figure out what should be changed.
Whenever running unit tests and asserting mapping configurations everything works, as well as mapping a letter with multiple responses to a view model.
However, whenever I get a letter with it's resposnes from the database (Entity framework 6), the projection to LetterView throws a stackoverflow exception.
Can anyone please explain me why this happens only on projection? What should I change?
A couple of options here, but usually the best choice is to set a max depth on the Responses. AutoMapper will try to spider the properties, and you've got a self-referencing DTO. First try this:
CreateMap<Letter, LetterView>()
.ForMember(d => d.Responses, opt => opt.MaxDepth(3));
Another option is to pre-wire your DTOs with a specific depth. You'd create a LetterView, and a ChildLetterView. Your ChildLetterView would not have a "Responses" property, giving you exactly 2 levels of depth on your DTO side. You can make this as deep as you want, but be very explicit in the DTO types in where they are in the hierarchy with Parent/Child/Grandchild/Greatgrandchild type names.
You probably have lazy loading enabled on your DbContext. Circular references may produce stack overflow exception. The best way to avoid it is to disable lazy loading :
context.ContextOptions.LazyLoadingEnabled = false;
// Bring entity from database then reenable lazy loading if needed
context.ContextOptions.LazyLoadingEnabled = true;
However, you will need to include all required navigation properties since EntityFramework will not brought them back while lazy loading is deactivated. Don't forget to re enable it after if you need it for other requests.
Related
I have a many-to-many relationship with EF Core 2.x.
I have created 3 classes:
public class Place
{
public string Name {get; set;}
public int Id {get; set;}
public ICollection<PlaceUser> Users {get; set;}
}
public class User
{
public string Name {get; set;}
public int Id {get; set;}
public ICollection<PlaceUser> Places {get; set;}
}
public class PlaceUser
{
public int UserId{get; set;}
public User User{get; set;}
public int PlaceId{get; set;}
public Place Place{get; set;}
}
public class PlaceDto
{
public string Name {get; set;}
public int Id {get; set;}
public ICollection<PlaceUserDto> Users {get; set;}
}
In my dbcontext, I set up the relationship. Everything works well.
But when I want to Map my Dto Place to my Place Object in my object place I have a recursivity and overflow exception:
I have:
Place
|-> Users
|-> User
|-> Place
|-> Users
|-> ...
I tried in my config of mapper to use depth but it's not working.
The only workaround I have found is:
if(place!=null && place.Users!=null)
{ // I set all the place.Users[i].Place = null; and place.Users[i].User=null;}
But it's an ugly solution and not convenient at all.
So which solution can I use?
Thanks,
I added the automapper config:
configuration.CreateMap<Place, PlaceDto>().MaxDepth(1);
https://stackoverflow.com/a/48824922/8101300
https://stackoverflow.com/a/57134333/8101300
CreateMap<Foo, Bar>().PreserveReferences();
Using entity framework I've been trying to create this relationship. Basically I have 1 object which has a Result. The Result object is abstract, as it has to be one of the 3 classes that inherit from Result, i.e. Approved, Rejected, or Modified:
I'm trying to create the table structure using Entity Framework. Originally I was going for a TPCT (Table Per Concrete Type) structure, so there would be no Result table, but I wanted to keep the link back in the Action table if I wanted to reference the Result, so now I'm attempting just TPT structure. I find TPCT is cleaner, but ultimately if TPT is the only way to achieve what I want, I'm fine with it.
I've tried variations of the following for my model structure:
public class Action
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
public int Result_Id {get; set;}
[ForeignKey("Result_Id")]
public virtual Result Result {get; set;}
public string Description {get; set;}
}
public abstract class Result
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
[Required]
public int Action_Id {get; set;}
[ForeignKey("Action_Id")]
public virtual Action Action {get; set;}
public string Comment {get; set;}
public class Approved : Result
{
public string Thing {get; set;}
}
public class Rejected : Result
{
public string Stuff {get; set;}
}
public class Modified : Result
{
public string Whatever {get; set;}
}
}
And then I've tried the following 2 strategies in my context file to either implement TPT:
modelBuilder.Entity<Approved>().ToTable("Approved");
modelBuilder.Entity<Rejected>().ToTable("Rejected");
modelBuilder.Entity<Modified>().ToTable("Modified");
Or for TCPT:
modelBuilder.Entity<Approved>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Approved");
});
modelBuilder.Entity<Rejected>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Rejected");
});
modelBuilder.Entity<Modified>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Modified");
});
Everytime I try to add the new migration, whatever I try, I'm faced with this error:
Unable to determine the principal end of an association between the types 'Result' and 'Action'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
The one time I was able to have it work was if I removed this reference from in the Action class:
public int Result_Id {get; set;}
[ForeignKey("Result_Id")]
public virtual Result Result {get; set;}
But I would really like to keep that reference there so then when I go into my DB to grab that Action object, I can immediately tell if there is a Result associated to it, without having to go through all 3 Result tables to see if there is a reference to that Action (which is why I think I need to have TPT...)
Any help to get this working would be greatly appreciated!
With a lot of research and trial and error, I discovered what I needed to get the result I wanted. It's TPCT DB structure, and the Action object is able to keep the reference to Result. Here are the model classes:
public class Action
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get; set;}
public virtual Result Result {get; set;} //just virtual here, as Action is the dependent and Result is the principal-- i.e. this Result isn't required
public string Description {get; set;}
}
public abstract class Result
{
//got rid of the Result_Id, since it's 1:1 the Action_Id can be the Key
[Required, Key] //added "Key"
public int Action_Id {get; set;}
[ForeignKey("Action_Id")]
public Action Action {get; set;} //removed this virtual, as Action is Required for Result, that makes Result the principal
public string Comment {get; set;}
public class Approved : Result
{
public string Thing {get; set;}
}
public class Rejected : Result
{
public string Stuff {get; set;}
}
public class Modified : Result
{
public string Whatever {get; set;}
}
}
And here is the fluent API code from the context:
//this gave me TPCT like I wanted
modelBuilder.Entity<Approved>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Approved");
});
modelBuilder.Entity<Rejected>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Rejected");
});
modelBuilder.Entity<Modified>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Modified");
});
//this defined the principal-dependent relationship I was missing
modelBuilder.Entity<Action>()
.HasOptional(a => a.Result)
.WithRequired(a => a.Action)
.Map(x => x.MapKey("Action_Id"));
And then it worked! Hopefully this example can assist someone else.
Is there a way to have AutoMapper only map properties that match explicitly? My Model has a property UserAccountId and also has a navigation property UserAccount that has an Id property, if the user posts UserAccountId, I want AutoMapper to map UserAccountId and leave UserAccount.Id null; I'd love to avoid using Ignore since that will prevent me from explicitly posting UserAccount.Id as well. I can't find any information on controlling AutoMapper's name matching strategy...
public class Role {
public int Id {get; set;}
public int UserAccountId {get; set;}
public UserAccount UserAccount {get; set;}
...
}
public class UserAccount {
public int Id {get; set;}
...
}
public class RoleViewModel {
public int Id {get; set;}
public int UserAccountId {get; set;}
}
public ActionResult AddRole(RoleViewModel viewModel) {
var model = GetModel(viewModel.Id);
Mapper.Map(viewModel, model);
//Do not infer and map UserAccount.Id
}
Don’t use AutoMapper for this case. It wasn’t built to support this type of scenario.
Or use ForPath(...).Ignore, but I just wouldn’t use my library for this scenario.
I have the following model:
public class Customer
{
public int Id {get; set;}
public string Name {get; set;}
public int AddressId {get; set;}
public virtual Address Address {get; set;}
public virtual ICollection<CustomerCategory> Categories {get; set;}
}
public class CustomerCategory
{
public int Id {get; set;}
public int CustomerId {get; set;}
public int CategoryId {get; set;}
public virtual Category Category {get; set;}
}
public class Address
{
public int Id {get; set;}
public string Street{get; set;}
public virtual PostCode PostCode {get; set;}
}
From the above, and using GraphDiff, I want to update the customer aggregate as follows:
dbContext.UpdateGraph<Customer>(entity,
map => map.AssociatedEntity(x => x.Address)
.OwnedCollection(x => x.Categories, with => with.AssociatedEntity(x => x.Category)));
But the above is not updating anything!!
What is the correct way to use GraphDiff in this case?
GraphDiff basically distinguishes two kinds of relations: owned and associated.
Owned can be interpreted as "being a part of" meaning that anything that is owned will be inserted/updated/deleted with its owner.
The other kind of relation handled by GraphDiff is associated which means that only relations to, but not the associated entities themselves are changed by GraphDiff when updating a graph.
When you use the AssociatedEntity method, the State of the child entity is not part of the aggregate, in other words, the changes that you did over the child entity will not be saved, just it will update the parent navegation property.
Use the OwnedEntity method if you want to save tha changes over the child entity, so, I suggest you try this:
dbContext.UpdateGraph<Customer>(entity, map => map.OwnedEntity(x => x.Address)
.OwnedCollection(x => x.Categories, with => with.OwnedEntity(x => x.Category)));
dbContext.SaveChanges();
one article has messages. So I have dilemma in terms how should I construct Message object. As entity or value object having ddd approach in mind.
Message should not be ever tracked independent, without Article object. As far as I know value object have no identity, and knowing that how can I track article messages?
If my poco is
public class Article {
public int id {get; set;}
public string Name {get; set;}
public Message Message {get; set;}
}
public class Message{
public string body {get; set;}
}
should I add Article inside Message to establish tracking of article message. If I add Article to Message class do I break Value object definition?
Update:
my goal is to allow visitors to leave message(s) to specific article. Message should not be tracked independently but should be tracked trough article object.
I have dilemma in terms how should I construct Message object
At the very least it should be:
public class Message
{
public int Id {get; set;} // MessageId
public Article Article { get; set; } // Owner
public string body {get; set;}
}
depending on your framework, you may want to add/manage the foreign key property as well. EF will understand:
public class Message
{
public int Id {get; set;} // MessageId
// use the 'virtual' keyword and/or add an ArticleId and/or use some Attributes.
public int ArticleId { get; set; }
public virtual Article Article { get; set; } // Owner
public string body {get; set;}
}