I have a model edmx model and am using POCO like this:
public class ContactPerson
{
public virtual Guid ContactPersonId { get; set; }
public virtual string FullName { get; set; }
public virtual Guid PositionId { get; set; }
public virtual DateTime Birthday { get; set; }
public virtual Position Position { get; set; }
}
public class Position
{
public virtual Guid PositionId { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<ContactPerson> ContactPersons { get; set; }
}
When I try to serialize from a in _context.Positions select a using Json() method, it detects a circular reference. I belive this happens because Position have a reference to ContactPersons collection and ContactPerson at same time have a reference to Position.
The question is obvious, what to do?
Try to use ScriptIgnoreAttribute at ContactPerson.Position property. It will say JavaScriptSerializer not to serialize that property and it will break circular reference.
Related
I have a model that looks like this
public abstract class Item
{
public int ItemId { get; set; }
public String Title { get; set; }
public String Description { get; set; }
public DateTime PurchaseDate { get; set; }
public ICollection<String> Pictures { get; set; }
public Int32 MinimumPrice { get; set; }
public DateTime Deadline { get; set; }
public Boolean VisibleBids { get; set; }
public Boolean Handled { get; set; }
public DateTime PlacementDate { get; set; }
public ApplicationUser User { get; set; }
}
In my controller I do
db.Items.ToList()
This leaves the Pictures field for all fetched objects null because its how the entity framework works. What is a good solution to fetch them in one query?
I hope you already done with Navigation properties between your tables, Now you just need to make your collection virtual and use the concept of eager loading when you need data from both the tables
public virtual ICollection<String> Pictures { get; set; }
and use include in linq like
db.Items.Include("Pictures").ToList()
So here by making virtual navigation you are saying that only take the data of related entity when you needed and whenever you need the data use the Include for eager loading.
For setting navigation properties please have a look on the code.
Suppose the scenario where we have a Post and on this we have multiple comments like
class Posts
{
public int PostsId { get; set; }
public string PostsDescription { get; set; }
public virtual ICollection<Comments> Comments { get; set; }
}
class Comments
{
public int CommentsId { get; set; }
public string CommentsDescription { get; set; }
public int PostsId { get; set; }
public virtual Posts Posts { get; set; }
}
I am a MVC newbie so go easy on me please.
I am getting two errors when I try to add a migration. They are as follows:
EntityType 'Icon' has no key defined. Define the key for this EntityType.
EntityType: EntitySet 'Icons' is based on type 'Icon' that has no keys defined.
I am including the Icon inside another model, like so:
public class Icon
{
public string IconName { get; set; }
public string IconColor { get; set; }
public int BackgroundXPos { get; set; }
public int BackgroundYPos { get; set; }
public string IconColorHover { get; set; }
public int BackgroundHoverXPos { get; set; }
public int BackgroundHoverYPos { get; set; }
}
public class GalleryThumbnail : CSSBoxModel
{
[DisplayName("Thumbnail Image Outline Color")]
public string ThumbnailImageOutlineColor { get; set; }
[DisplayName("Thumbnail Menu Font")]
public CSSFont ThumbnailMenuFont { get; set; }
[DisplayName("Thumbnail Icon Color")]
public Icon ThumbnailIconColor { get; set; }
}
How is this Address class below any different which is working:
public class Address
{
public String Adress1 { get; set; }
public String Adress2 { get; set; }
public String Adress3 { get; set; }
public String City { get; set; }
public String County { get; set; }
public String State { get; set; }
public String Zip { get; set; }
public String Country { get; set; }
}
[Table("UserProfile")] //Could be PP empolyee, Subscriber or Subscriber's customer
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public bool? Gender { get; set; }
public Address Address { get; set; } //billing address
public Address ShipAddress { get; set; }
}
I did not add a key in either my Icon or Address class because I have no intention of storing specific data in my DB. They are merely to be used inside other classes. So wy is one neededing an ID and the other is not?
I have not created public DbSet Icons { get; set; } in my DB Context either.
Also can you tell me what it is called when you use a class inside another ( or instance of class inside a class as in these examples ) ?
Much appreciated!
Since the address entity has no key defined it the Entity Framework assumes it's a complex property, and your UserProfile table will be rendered with columns named Addres_Address1, Address_Address2, Address_Address3, Address_City, and so on...
Even though you haven't declared an EntitySetIcons DbSet on your context class, it's still being added implicitly because one of your other classes somewhere has an ICollection or IEnumerable property defined.
More info on Code Conventions here:
http://msdn.microsoft.com/en-us/data/jj679962.aspx
So, either decorate the collections as NotMapped like #Kamyar said or simply remove the references from any class already declared as a DbSet.
you can use [NotMapped] attribute in System.ComponentModel.DataAnnotations.Schema namespace in EntityFramework.dll:
using System.ComponentModel.DataAnnotations.Schema;
...
[NotMapped]
public Address Address { get; set; } //billing address
[NotMapped]
public Address ShipAddress { get; set; }
Regarding the naming, AFAIK these are called public properties as well.
I have multiple classes that I need to map into 1 class:
This is the source that I'm mapping from(view model):
public class UserBM
{
public int UserId { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string State { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
}
This is how the destination class is(domain model):
public abstract class User
{
public int UserId { get; set; }
public virtual Location Location { get; set; }
public virtual int? LocationId { get; set; }
}
public class Location
{
public int LocationId { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string State { get; set; }
public virtual int CountryId { get; set; }
public virtual Country Country { get; set; }
}
This is how my automapper create map currently looks:
Mapper.CreateMap<UserBM, User>();
Based on the documents on automapper codeplex site, this should be automatic but it doesn't work. Address, Address2, etc is still null. What should my createmap look like?
The reason is because AutoMapper can't map all those flat fields to a Location object by convention.
You'll need a custom resolver.
Mapper.CreateMap<UserBM, User>()
.ForMember(dest => dest.Location, opt => opt.ResolveUsing<LocationResolver>());
public class LocationResolver : ValueResolver<UserBM,Location>
{
protected override Location ResolveCore(UserBMsource)
{
// construct your object here.
}
}
However, i dont like this. IMO, a better way would be to encapsulate those properties in your ViewModel into a nested viewmodel:
public class UserBM
{
public int UserId { get; set; }
public LocationViewModel Location { get; set; }
}
Then all you have to do is define an additional map:
Mapper.CreateMap<User, UserBM>();
Mapper.CreateMap<LocationViewModel,Location>();
Then it will all work.
You should try and make use of AutoMapper conventions, where possible. And it's certainly possible to make your ViewModel more hierachical, to match the destinations hierachy.
EDIT strange this question has been asked by you.. seems to be a same question - i guess i am missing something...
Check this SQ Question
*
Define two mappings, both mapping from the same source to different
destinations
*
I think you need to make the property names similar to LocationAddress and LocationAddress2 on UserBM for their automatic projection to work, but I may be wrong.
Check out their page on Flattening they have property names that have both property names of the source concatenated like I indicated.
Simply follow the naming convention in your target class and prefix the address properties with Location since that's the property name in the source class:
public class UserBM
{
public int UserId { get; set; }
public string LocationAddress { get; set; }
public string LocationAddress2 { get; set; }
public string LocationAddress3 { get; set; }
public string LocationState { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
}
So I created the following related Classes and I'm trying to do Code-First approach. I want the Quote class to reference 3 instances of the User class by 3 different navigable property names, but when I do DBInitializer to populate and create the DB, the Quote table has 6 columns instead of the expected 3 columns, of which 3 are always null. The navigable properties point to those 3 null columns, so whenever I point to Quote.Manager or one of the other 3 properties, it returns null instead of the actual manager. How can I fix this?
Quote Class (I left a little off, but you get the point):
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace QuoteManager.Models
{
public class Quote
{
public int QuoteID { get; set; }
public virtual int StateID { get; set; }
public virtual State State { get; set; }
public virtual int CreatorID { get; set; }
public virtual User Creator { get; set; }
public virtual int AgentID { get; set; }
public virtual User Agent { get; set; }
public virtual int ManagerID { get; set; }
public virtual User Manager { get; set; }
}
}
User class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace QuoteManager.Models
{
public class User
{
public User()
{
this.Quotes = new HashSet<Quote>();
this.CreatedQuotes = new HashSet<Quote>();
this.ManagedQuotes = new HashSet<Quote>();
}
public int UserID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual ICollection<Quote> Quotes { get; set; }
public virtual ICollection<Quote> CreatedQuotes { get; set; }
public virtual ICollection<Quote> ManagedQuotes { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
}
Use the InverseProperty attribute to specify the other property participating in the relationship
public class Quote
{
public int QuoteID { get; set; }
public virtual int StateID { get; set; }
public virtual State State { get; set; }
public virtual int CreatorID { get; set; }
[InverseProperty("CreatedQuotes")]
public virtual User Creator { get; set; }
public virtual int AgentID { get; set; }
public virtual User Agent { get; set; }
public virtual int ManagerID { get; set; }
[InverseProperty("ManagedQuotes")]
public virtual User Manager { get; set; }
}
public class User
{
public User()
{
this.Quotes = new HashSet<Quote>();
this.CreatedQuotes = new HashSet<Quote>();
this.ManagedQuotes = new HashSet<Quote>();
}
public int UserID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual ICollection<Quote> Quotes { get; set; }
[InverseProperty("Creator")]
public virtual ICollection<Quote> CreatedQuotes { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Quote> ManagedQuotes { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
Similarly map the other relations.
Add the attribute [ForeignKey("Creator")] to the CreatorID and so on for the other 2 property pairs.
FINAL SOLUTION
Thanks to your reference to InverseProperty I found an amazing article that covers exactly what I wanted to accomplish using fluent API. This article was written in January, but I'm pretty sure CTP5 is now officially part of the MVC 3 and EF core.
Associations in EF Code First CTP5
Okay...I'm going to document what I found to work great! I hate it when people leave partial answers, so here we go.
There is a little redundancy here, but it works. My Quote Class looks like this:
[ForeignKey("Creator")]
public virtual int CreatorID { get; set; }
[InverseProperty("CreatedQuotes")]
public virtual User Creator { get; set; }
[ForeignKey("Agent")]
public virtual int AgentID { get; set; }
[InverseProperty("OwnedQuotes")]
public virtual User Agent { get; set; }
[ForeignKey("Manager")]
public virtual int ManagerID { get; set; }
[InverseProperty("ManagedQuotes")]
public virtual User Manager { get; set; }
Then my User class looks like this:
public virtual ICollection<Quote> CreatedQuotes { get; set; }
public virtual ICollection<Quote> OwnedQuotes { get; set; }
public virtual ICollection<Quote> ManagedQuotes { get; set; }
Finally, my DBContext class looks like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Quote>()
.HasRequired(a => a.Manager)
.WithMany()
.HasForeignKey(u => u.ManagerID);
modelBuilder.Entity<Quote>()
.HasRequired(a => a.Agent)
.WithMany()
.HasForeignKey(u => u.AgentID).WillCascadeOnDelete(false);
modelBuilder.Entity<Quote>()
.HasRequired(a => a.Manager)
.WithMany()
.HasForeignKey(u => u.ManagerID).WillCascadeOnDelete(false);
}
You can see the redundancy in the ForeignKey annotation in the Quote class and the Fluent API mapping in the DbContext class, but it's not hurting anything. I could probably do away with the annotations in the Quote class, but the Fluent API is necessary to set the cascading rule to false to prevent foreign key conflicts.
I have been able to navigate both directions with no problems and exactly as expected.
Thanks for all your help!
I am using EF4 CTP5. Here are my POCOs:
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
Is there a way to get Address to be a ComplexType for the Order class? After playing around with this, I'm guessing not, but maybe there's a way I haven't seen.
EDIT: In response to Shawn below, I gave it my best shot:
//modelBuilder.Entity<Order>().Ignore(o => o.BillingAddress);
//modelBuilder.Entity<Order>().Ignore(o => o.ShippingAddress);
modelBuilder.Entity<Order>()
.Property(o => o.BillingAddress.City).HasColumnName("BillingCity");
Fails at runtime with error "The configured property 'BillingAddress' is not a declared property on the entity 'Order'." Trying to use Ignore() doesn't work. Next, the Hanselman article is CTP4, but the CTP5 equivalent is:
modelBuilder.Entity<Order>().Map(mapconfig =>
{
mapconfig.Properties(o => new {
o.Id
, o.Total
, o.BillingAddress.City
});
mapconfig.ToTable("Orders");
});
Fails with error "Property 'BillingAddress.City' of type 'Order' cannot be included in its mapping."
I give up. Maybe the final release will have something like this. Or maybe I need to switch to NHibernate =)
All you need to do is to place ComplexTypeAttribute on Address class:
[ComplexType]
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
Alternatively, you can achieve this by fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Address>();
}
But you cannot have Address type as to be both an Entity and a Complex Type, it's one way or another.
Take a look at this blog post where I discuss this at length:
Associations in EF Code First CTP5: Part 1 – Complex Types
If you want Address to be in the same table as Order, you're going to have to tell EF that in the DbContext OnModelCreating override.
Take a look here: http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx