is this value object - c#

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;}
}

Related

Abstract Class with 1 to 1 relationship Entity Framework

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.

Can you have AutoMapper only map properties that match explicitly

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.

Code first : Sequence contains more than one matching element - multiple classes with multiple references

I'm getting the "Sequence contains more than one matching element" error message while attempting to run the "Add-Migration" command. I believe that the issue is stemming from the fact that I have multiple classes that have multiple references to a shared class.
class User {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId {get; set;}
public string Name {get; set;}
// other stuff removed for display purposes
[InverseProperty("CreateUser")]
public virtual ICollection<Foo> FooCreateUser { get; set; }
[InverseProperty("ModifyUser")]
public virtual ICollection<Foo> FooModifyUser { get; set; }
[InverseProperty("CreateUser")]
public virtual ICollection<Bar> BarCreateUser { get; set; }
[InverseProperty("ModifyUser")]
public virtual ICollection<Bar> BarModifyUser { get; set; }
}
class Foo {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid FooId {get; set;}
[InverseProperty("FooCreateUser")]
public User CreateUser {get; set;}
[InverseProperty("FooModifyUser")]
public User ModifyUser {get; set;}
}
class Bar {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid BarId {get; set;}
[InverseProperty("BarCreateUser")]
public User CreateUser {get; set;}
[InverseProperty("BarModifyUser")]
public User ModifyUser {get; set;}
}
What is wrong with the above code?
Do I have to change the field names in the [Bar] and [Foo] database tables to be unique?
i.e. change [Foo][CreateUser] to [Foo][Foo_CreateUser]?
I need to have a "CreateUser" and "ModifyUser" field on multiple database tables and it doesn't make any sense (from a database design perspective) to have all of those fields uniquely named.
How do I tell CodeFirst to do that?

Automapper self referencing model projection

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.

How to avoid circular references with AutoMapper?

I have the following models (and corresponding DTOs):
public class Link
{
public int Id {get; set;}
public int FirstLinkId {get; set;}
public int SecondLinkId {get; set;}
public virtual Link FirstLink {get; set;}
public virtual Link SecondLInk {get; set;}
}
public class OtherObject
{
public int Id {get; set;}
public int LinkId {get; set;}
public string Name {get; set;}
public virtual Link Link {get; set;}
}
In my scenario, I can have a Link object where FirstLink and/or SecondLink can be null, references to other objects, or references to the same object.
Now I want to load an OtherObject entity from the db using EF. I load the entity itself and also the Link object associated with it. This is done perfectly by EF.
In this particular case, both FirstLink and SecondLink are the same as Link, therefore, when automapping from model to dto it just keeps on mapping into oblivion.
My mapping is:
Mapper.CreateMap<OtherObject, OtherObjectDto>().Bidirectional()
.ForMember(model => model.LinkId, option => option.Ignore());
where Bidirectional() is this extension:
public static IMappingExpression<TDestination, TSource> Bidirectional<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
return Mapper.CreateMap<TDestination, TSource>();
}
Is there way to tell Automapper not to map further down the tree in this case?
The way I would handle this is to create separate DTO objects for the children:
public class Employee
{
public int Id {get; set;}
public string Name { get; set; }
public Employee Supervisor {get; set; }
}
public class EmployeeDto {
public int Id {get; set;}
public string Name { get; set; }
public SupervisorDto Supervisor { get; set; }
public class SupervisorDto {
public int Id {get; set;}
public string Name { get; set; }
}
}
Mapper.CreateMap<Employee, EmployeeDto>();
Mapper.CreateMap<Employee, EmployeeDto.SupervisorDto>();
Don't let your DTOs be recursive/self-referential. Be explicit in your structure on how deep you want it to go.
EF can't do recursive joins, you're only doing one level, so don't make your DTOs go nuts with infinitely deep relationships. Be explicit.

Categories