I'm trying to new ICollections in my derived classes - should this be done in the object constructor or/and when I'm creating a new instance of an object in my Register ActionResult()?
Tenant inherits from UserProfile - simplified example:
public class Tenant : UserProfile
{
public Tenant()
{
this.ReferencePhotos = new List<ReferencePhoto>();
// ReferencePhoto extends Image
}
// A Tenant can have many ReferencePhotos
[ForeignKey("ImageId")] // Id of parent class
public virtual ICollection<ReferencePhoto> ReferencePhotos { get; set; }
}
I've tried to do the above but it results in an InvalidCastException:
Unable to cast object of type 'System.Collections.Generic.List'1[Namespace.Models.ReferencePhoto]' to type [Namespace.Models.ReferencePhoto]'.
Why is it trying cast from one object to another when they're the same?
In my Register ActionResult() I've also tried this (with and without setting ICollections in object contructor):
public ActionResult Register(RegisterModel model)
{
using (var db = new LetLordContext())
{
var tenant = db.UserProfile.Create<Tenant>();
tenant.ReferencePhotos = new List<ReferencePhoto>();
// Do I need to new it here at all/as well?
db.UserProfile.Add(tenant); // Exception being thrown here
db.SaveChanges();
Roles.AddUserToRole(model.UserName, "Tenant");
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Confirm", "Home", tenant);
}
}
The above also throws the same exception mentioned earlier. Can anyone offer any insight?
EDIT: Added UserProfile code
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
[Display(Name = "Username")]
[Required(ErrorMessage="Username is required.")]
public string UserName { get; set; }
[Display(Name = "First name")]
[Required(ErrorMessage = "First name is required.")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Account type is required.")]
public AccountType AccountType;
public virtual string AccountTypeString
{
get { return AccountType.ToString(); }
set
{
AccountType newValue;
if (Enum.TryParse(value, out newValue))
{ AccountType = newValue; }
}
}
}
EDIT: Added context code and ReferencePhoto code
public class LetLordContext : DbContext
{
public DbSet<LetLord.Models.UserProfile> UserProfile { get; set; } // 1 DbSet for superclass UserProfile
public DbSet<LetLord.Models.Image> Image { get; set; } // 1 DbSet for superclass Image
public DbSet<LetLord.Models.ResidentialProperty> ResidentialProperty { get; set; }
public DbSet<LetLord.Models.TenantGroupMember> TenantGroupMember { get; set; }
public DbSet<LetLord.Models.Viewing> Viewing { get; set; }
public DbSet<LetLord.Models.TenantPreferences> TenantPreferences { get; set; }
public DbSet<LetLord.Models.LandlordPreferences> LandlordPreferences { get; set; }
public DbSet<LetLord.Models.Address> Address { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
I'm using table-per-type inheritance hence only DbSets for base classes are implemented.
public class ReferencePhoto : Image
{
// 1:many with Tenant
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual Tenant Tenant { get; set; }
}
I can workaround this issue by not initialising the list in the object constructor or when creating a new entity. To achieve this I use a null coalescing operator in the GET ActionResult that returns a Tenant object in a partial view. In the partial, I check if Model.ReferencePhotos == null. If it is, a user can upload a photo. When a photo is uploaded, it is added to the list, I can confirm this by checking the database. However, when I login again, the if in the partial view mentioned previously throws the following exception:
Unable to set field/property ReferencePhotos on type System.Data.Entity.DynamicProxies.Tenant_...
with an inner exception:
Unable to cast object of type 'System.Data.Entity.DynamicProxies.ReferencePhoto_3DFB9F64061D55E5AF6718A74C97025F77EFB2BB9C2A6E43F5A6AF62A6A73E75' to type 'System.Collections.Generic.ICollection`1[LetLord.Models.ReferencePhoto]'."}
This may provide more insight.
Try removing the...
[ForeignKey("ImageId")] // Id of parent class
ForeignKey that should be set on the 'one' side (of one-to-many - sort
of), never on collections - and it's only defined on the ReferencePhoto (via UserId
which you already have attributed).
...and that might as well be adding extra foreign key - and resulting in that single instance vs collection error.
Some more info on how ForeignKey should be defined (ForeignKey vs InverseProperty etc.), and some good advices from gurus :)
Entity Framework 4.1 InverseProperty Attribute
How Should I Declare Foreign Key Relationships Using Code First Entity Framework (4.1) in MVC3?
For more complex scenarios (with many-to-many and manually defining relations in fluent code - which I recommend) - take a look at these detailed examples I made a while ago - it has most of the mappings you may need.
Many to many (join table) relationship with the same entity with codefirst or fluent API?
Code First Fluent API and Navigation Properties in a Join Table
EF code-first many-to-many with additional data
Related
I have a solution which uses Entity Framework to insert invoices to a database table. These invoices reference an order, which in turn also references an order item collection.
In this instance I am trying to add an order to the database, however the code is inside a new DbContext and so I need to attach the order and order items to the context, as these already exist in the database and shouldn't be re-added.
I've cut down the model properties for the sake of demonstration:
public class Invoice {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int InvoiceId { get; set; }
public string OrderNumber { get; set; }
...
public virtual List<InvoiceLineItem> LineItems { get; set; }
}
public class InvoiceLineItem {
[Key]
public int Id { get; set; }
...
public ShopifyOrderItem { get; set; }
}
public class ShopifyOrder {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public int OrderNumber { get; set; }
...
public OrderInvoiceStatus InvoiceStatus { get; set; }
public virtual List<ShopifyOrderItem> OrderItems { get; set; }
}
public class ShopifyOrderItem {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
...
[Required]
public virtual ShopifyOrder ShopifyOrder { get; set; }
}
In the invoice engine, I'm running the following code for each invoice to add it to the database:
ShopifyOrder order = await db.ShopifyOrders.SingleOrDefaultAsync(x => x.OrderNumber.ToString() == inv.OrderNumber);
if (order != null) {
// Attach marketplace entity to the invoice to avoid duplicate primary key exceptions
db.Marketplaces.Attach(inv.Marketplace);
db.Invoices.Add(inv);
order.InvoiceStatus = OrderInvoiceStatus.InProgress;
}
I've tried a number of methods to try and attach the states, however they all throw errors.
inv.LineItems.ForEach(li => {
db.Entry(li).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem.ShopifyOrder).State = EntityState.Modified;
});
The above code returns the following error on save:
EntityFramework: Saving or accepting changes failed because more than one entity of type 'TorroModels.ShopifyOrder' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model.
What is the best way to attach the LineItems/ShopifyOrderItems without trying to attach the ShopifyOrder connected property multiple times?
Sorry to say but it seems that you need to follow the best practice first when constructing a relationship. You may follow this link :
http://www.entityframeworktutorial.net/entity-relationships.aspx
In short :
Avoid using only "Id" in every entity, or you can use attributes to map between the physical name and the property name
It seems that you have circular references here, so maybe you could simplify it first
Next, you can read this link :
http://www.entityframeworktutorial.net/EntityFramework5/attach-disconnected-entity-graph.aspx
if you need to know more about what's the best practice of attaching entities, but in my opinion, just don't abuse this feature, because using normal CRUD should be sufficient most of the time.
I'm sorry I cannot help you more than this, because of lack of information I may need, and with my reputation I still cannot comment directly in your post to ask for it.
I recently delete a column ConversationId from my tables. When I start to debug my service and try to save I am getting an error:
Invalid column name 'ConversationId'.
Code:
public class AstootContext : DbContext
{
public AstootContext(DbContextOptions<AstootContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
public DbSet<ServiceRequest> ServiceRequests { get; set; }
}
And my entity looks like this:
public class ServiceRequest
{
public int Id { get; set; }
public int SenderUserId { get; set; }
public int PriceTypeId { get; set; }
public decimal Price { get; set; }
public bool IsAccepted { get; set; }
public DateTime Created { get; set; }
public int MessageId { get; set; }
}
All references to ConversationId were removed from the code, I've rebuilt, yet I'm still getting this error and I don't understand why.
This is my SQL Server table as you can see there is no ConversationId:
Is there a secret cache that I need to delete or something I have to run to update this?
EF Core is code based ORM, with the most important here being the M - Mapper. It doesn't matter what the actual database structure is, the important is what EF *thinks** it is based on your code model (entity classes and their properties, combined with data annotations, fluent configuration and set of conventions).
So the problem should originate from code. Since you've removed the explicit property, it should be caused by shadow property. And as explained in the documentation link, shadow properties are usually introduced by convention from relationships:
Shadow properties can be created by convention when a relationship is discovered but no foreign key property is found in the dependent entity class. In this case, a shadow foreign key property will be introduced.
The documentation also explains the naming rules applied in different scenarios.
A shadow property called ConversationId can be introduced in a several ways, but according to the provided information, the most likely cause is to have an entity class called Conversation defining one-to-many relationship with ServiceRequest by having a collection type navigation property:
public class Conversation
{
public int Id { get; set; }
// ...
public ICollection<ServiceRequest> ServiceRequests { get; set; }
}
Which according to your comment was indeed the case.
For completeness, here are some other possible scenarios generating such property:
(1) No collection navigation property in Conversation, reference navigation property in ServiceRequest:
public class Conversation
{
public int Id { get; set; }
// ...
}
public class ServiceRequest
{
// ...
public Conversation Conversation { get; set; }
}
(2) No navigation properties in Conversation and ServiceRequest, fluent configuration:
modelBuilder.Entity<Conversation>()
.HasMany<ServiceRequest>();
or
modelBuilder.Entity<ServiceRequest>()
.HasOne<Conversation>();
or variations of the above.
(3) No relationship involved, shadow property created through fluent configuration:
modelBuilder.Entity<ServiceRequest>()
.Property<int>("ConversationId");
I am trying to use ExpressMapper to map data entities to models.
If I map entity to a model directly (both of them having same properties) then it is working fine.
But if I map linked entities to model then I am getting an error
There was an error: System.MissingMethodException: No
parameterless constructor defined for this object.
Database structure:
ExpressMapper Registration:
Mapper.Register<DiscountDaysOfWeek, DiscountDaysOfWeekModel>()
.Member(dest => dest.DiscountDayId, src => src.DiscountDayId)
.Member(dest => dest.DiscountDaysOfWeekId, src => src.DiscountDaysOfWeekId)
.Member(dest => dest.DiscountId, src => src.DiscountId)
.Member(dest => dest.Discountday, src => src.DiscountDay.Day);
Invoked like this:
var disDays = discs.SelectMany(x => x.DiscountDaysOfWeeks)
.Map<IQueryable<DiscountDaysOfWeek>, IQueryable<DiscountDaysOfWeekModel>>();
Getting the error message at the invoke.
DiscountDaysOfWeekModel:
public class DiscountDaysOfWeekModel
{
public int DiscountDaysOfWeekId { get; set; }
public int DiscountId { get; set; }
public int DiscountDayId { get; set; }
public string Discountday { get; set; }
}
DiscountDayOfWeek (Generated by EF)
public partial class DiscountDaysOfWeek
{
public int DiscountDaysOfWeekId { get; set; }
public int DiscountId { get; set; }
public int DiscountDayId { get; set; }
public virtual DiscountDay DiscountDay { get; set; }
public virtual Discount Discount { get; set; }
}
DiscountDay(Generated by EF):
public partial class DiscountDay
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public DiscountDay()
{
this.DiscountDaysOfWeeks = new HashSet<DiscountDaysOfWeek>();
}
public int DiscountDayId { get; set; }
public string Day { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<DiscountDaysOfWeek> DiscountDaysOfWeeks { get; set; }
}
Sample working one: In the below working sample the model and entities are having same properties
Mapper.Register<DiscountPreventedPriceEnding, DiscountPreventedPriceEndingModel>();
var execPriceEndings = discs.SelectMany(x => x.DiscountPreventedPriceEndings)
.Map<IQueryable<DiscountPreventedPriceEnding>, IQueryable<DiscountPreventedPriceEndingModel>>();
Any help would be greatly appreciated.
I appreciate this is an extremely old question, but given that I just spent 4 hours debugging a similar There was an error: System.MissingMethodException: No parameterless constructor defined for this object error on ExpressMapper, I thought I'd chime in with my findings.
So we had a situation similar to yours, in that we had domain models like so (all the following is simplified examples):
public class Owner
{
public int? ID { get; set; }
public string Name { get; set; }
}
public class Animal
{
public int? ID { get; set; }
public string Name { get; set; }
public int? OwnerID { get; set; }
[ForeignKey("OwnerID")]
public Owner Owner { get; set; }
}
With the following view model (i.e. what our APIs send out and receive):
public class AnimalViewModel
{
public int? ID { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
}
With mappings like so:
Mapper.Register<Animal, AnimalViewModel>();
Mapper.Register<AnimalViewModel, Animal>();
On mapping to or from the domain model and the view model we'd get the MissingMethodException, despite the fact that both the view model and the domain model had public, default constructors. The solution was to manually map the related entities in the domain model and exclude them from ExpressMapper's mappings like so:
Mapper.Register<Animal, AnimalViewModel>()
.Ignore(a => a.Owner);
Mapper.Register<AnimalViewModel, Animal>()
.Ignore(a => a.Owner);
From reading EntityMapper's source code, it seems the MissingMethodException is a total red herring which has nothing to do with the actual issue. The actual issue seems to be that it can't figure out how to convert one type to another. In our case -- where complex objects were mapped to/from primitives as above -- it was sufficient to exclude the related objects from the mapper and do it manually.
EDIT:
Upon further investigation, we traced the root problem in our case back to the fact that EF proxy creation creates generated types (e.g. 'MyModel_14289012') which don't match the types registered in the mapper. To prevent this, apply the following to your context:
Context.Configuration.LazyLoadingEnabled = false;
Context.Configuration.ProxyCreationEnabled = false;
and manually include any nested/related objects required in your model like so:
Context.Animals
.Include(a => a.Owner);
This fetches the related entities, but as their actual type rather than the EF-generated type.
Entity Framework uses a parameterless constructor to instantiate classes and reflection to populate class properties. If you have constructors with parameters, then the default parameterless constructor is hidden and you have to add it to your Entity classes for Entity Framework to use.
But if I map linked entities to model then I am getting an error
If your child entities are missing the parameterless constructor and lazy loaded, then EF is failing when it attempts to instantiate the child entity which doesn't have a parameterless constructor.
Note: the parameterless constructor doesn't have to be public.
First off, I'm new to the Entity Framework and am migrating an existing project from a database framework that I wrote myself so I have a fair amount of flexibility in the solution I choose.
From what I've researched so far everything appears to be set up correctly. However, when my database is constructed, the table for a helper class I wrote has no columns in it (outside of its primary key). The most simplified version of the classes are included below with their relationships defined in the fluent API.
Classes
public class Concept
{
public long ID { get; set; }
[Index(IsUnique = true), MaxLength(255)]
public string Name { get; set; }
}
public class Tag
{
public long ID { get; set; }
public virtual Content Subject { get; set; }
public virtual Concept Concept { get; set; }
}
public class Helper
{
public long ID { get; set; }
public virtual Content Subject { get; set; }
public virtual List<Tag> Instances { get; set; }
// Helper functionality
}
public class Content
{
public long ID { get; set; }
public virtual Helper Helper { get; set; }
public Content() { Helper = new Helper() { Subject = this }; }
}
Context
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.HasRequired(t => t.Concept);
modelBuilder.Entity<Tag>()
.HasRequired(t => t.Subject);
modelBuilder.Entity<Helper>()
.HasRequired(t => t.Subject)
.WithRequiredDependent(c => c.Helper);
modelBuilder.Entity<Helper>()
.HasMany(t => t.Instances);
modelBuilder.Entity<Content>()
.HasRequired(c => c.Helper)
.WithRequiredPrincipal();
base.OnModelCreating(modelBuilder);
}
Program.cs
static void Main(string[] args)
{
Content content = null;
using (var context = new Context())
{
content = context.Content.Find(1);
if (content == null)
{
content = new Content();
context.Content.Add(content);
context.Helper.Add(content.Helper);
context.SaveChanges();
}
}
}
It's also worth mentioning that when the data is saved, the Helper is assigned an ID but on loading the parent class (Content) the second time around, the Helper is not lazy loaded as I would expect from the 'virtual' keyword. I suspect that this is caused by the same issue causing the absence of data in the table.
I have tried both the data annotation and fluent API approaches that EF provides but it seems that there is something fundamental that I am misunderstanding. I would like to retain this helper class as it helps organize the code far better.
As I have spent a fair amount of time researching these relationships / APIs, and scouring Google / SO without found anything to solve this issue in particular any help would be greatly appreciated!
Updated: Solution
Thanks to a question in the comments, I realized that I was expecting to see the keys of a many-to-many relationship in the tables for the entity types themselves (i.e. in the Helpers table). However, in a many-to-many relationship, the keys will always be placed in a separate table (concatenation of type names) which was not being previously created.
By adding '.WithMany();' to the Helper section of the OnModelCreating function as below
modelBuilder.Entity<Helper>()
.HasMany(t => t.Instances)
.WithMany();
the many-to-many relationship became properly defined and the HelperTags table generated as expected. This is due to the fact that the many-to-many relationship is one way (Helpers always refer to Tags, Tags never refer to Helpers). This is also why the 'WithMany' does not have any arguments (since no Helper properties exist in the Tag class). Fixing this simple oversight solved the problem!
You are probably working harder than you need to in the on ModelCreate. You should probably redesign your classes use Identifiers, like this:
public class Tag
{
public long Id { get; set; }
public long SubjectId { get; set; }
public long ConceptId { get; set; }
public virtual Content Subject { get; set; }
public virtual Concept Concept { get; set; }
}
You need to keep the ID names the EXACT same as the object names + Id and EF will magically link everything up. If you don't want them required then make the id nullable (C# 6 == long? SubjectId).
Also, I have changed the ID -> Id; I have no idea if this matters. At one point I remember having to do that to get things working (it was YEARS ago) and I have been doing it that way ever since.
Consider reading:
Entity Framework Code First Conventions
relationship Convention
In addition to navigation properties, we recommend that you include
foreign key properties on the types that represent dependent objects.
Any property with the same data type as the principal primary key
property and with a name that follows one of the following formats
represents a foreign key for the relationship:
<navigation property name><principal primary key property name>
<principal class name><primary key property name>
<principal primary key property name>
If multiple matches are found then precedence is given in the order
listed above.
Foreign key detection is not case sensitive.
Sample Code from MSDN:
In the following example the navigation properties and a foreign key are used to define the relationship between the Department and Course classes.
public class Department
{
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
// Primary key
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
}
I am having some trouble with one to one relationships in EF.
Normally, I would define the key side of a (one to many) relationship, like so:
[ForeignKey("OrderId")]
public virtual Order Order { get; set; }
But when I do this with a one to one relationship, it fails to determine the principal end...
Unable to determine the principal end of an association between the
types 'Retailer.ClientDB.EF.OrderOrigin' and
'Retailer.ClientDB.EF.Order'. The principal end of this
association must be explicitly configured using either the
relationship fluent API or data annotations.
Strangely, I don't get the error if I put the ForeignKeyAttribute on the id column, as I have done here with the relationship between Order and OrderOrigin.
[Table("ORDER_ORIGIN")]
public class OrderOrigin
{
[Key, Column("uidorder"), ForeignKey("Order")]
public int OrderId { get; set; }
[Column("importfilename")]
public string ImportFileName { get; set; }
public virtual Order Order { get; set; }
}
[Table("ORDERS")]
public class Order
{
[Column("uidorder")]
public int Id { get; set; }
[Column("uidstatus")]
public int OrderStatus { get; set; }
public virtual OrderOrigin OrderOrigin { get; set; }
}
But now, when I attempt to include() like this (which would normally work great)...
public List<ClientDB.EF.OrderOrigin> ListOrderOrigin_WithOrder(Common.Data.DBContext context)
{
return context.EFDatabase.OrderOrigin.Include("Order").ToList();
}
...it fails to include Order and errors with the following when I try and access Order (after the context has been disposed of):
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
Can anyone please advise how I can code one to one relationships in order to get this to work?
Thanks