I have a basic table with a few FK references. So when I retrieve an entity for an update operation; that entity contains ICollections of related entites. My main viewModel contains Lists which correspond to these ICollections. However, since some other models represent 1-1 mapping, I have object instead of List. But inside the Entity they continue to be represented as ICollections.
This is giving me some problems when trying to map between viewModel and Entity. I am using Automapper for the mapping. I have
mapper.Map(viewModel, entity);
Currently I am leaving out the problematic models from this mapping and adding them separately. Is there a way to handle everything in one mapping? Is there a way to deal with the ICollections which ideally should be a single object?
EDIT
public class MainViewModel
{
public EntityVM1 vm1 { get; set; }
public List<EntityVM2> vm2 { get; set; }
public List<EntityVM3> vm3 { get; set; }
}
public class MainEntity
{
... some scalar props...
public virtual ICollection<Entity1> e1 { get; set; }
public virtual ICollection<Entity2> e2 { get; set; }
public virtual ICollection<Entity3> e3 { get; set; }
}
Entity1 and EntityVM1 are causing the problem.
Thanks
You can always override the default mapping system in the mapping config of AutoMapper, you should have a peek at the runtime polymorphism in the mapping inheritance section of the documentation.
If what you want on the entity is a straight object, why not take Automapper out of the equation and just force EF to map it using a one to one system... i.e
modelBuilder.Entity<MainEntity>()
.HasOne(p => p.Entity1)
.WithOne(i => i.MainEntity)
.HasForeignKey<Entity1>(b => b.MainEntityForignKey);
HAve a peek at the EF docs, under section one-to-one for more info
Related
I'm trying to share common properties with multiple entities by using multiple levels of inheritance, but I'm running into an error.
Cannot create a relationship between 'User.SupersCreated' and 'Super.CreatedBy' because a relationship already exists between 'User.BasicsCreated' and 'Basic.CreatedBy'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'Super.CreatedBy' first in 'OnModelCreating'.
The structure of my models is as follows.
public class EntityBase
{
public Guid Id { get; set; }
public Guid CreatedById { get; set; }
public User CreatedBy { get; set; }
}
public class Basic: EntityBase
{
public string BasicProperty { get; set; }
}
public class Super : Basic
{
public string SuperProperty { get; set; }
}
public class User : IdentityUser<Guid>
{
public ICollection<Basic> BasicsCreated { get; set; }
public ICollection<Super> SupersCreated { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasMany(x => x.BasicsCreated)
.WithOne(x => x.CreatedBy);
modelBuilder.Entity<User>()
.HasMany(x => x.SupersCreated)
.WithOne(x => x.CreatedBy);
}
The problem seems to be a result of Super Inheriting from Basic, or at least, the problem goes away when I remove this level of inheritance and make Super inherit from EntityBase (however than I'll lose the properties that exist in Basic).
Can anyone please help me understand why I'm getting this error and what should be done to fix it?
Edit
After considering this some more, I think I'm trying to abuse inheritance to do what it's not intended to do.
The database structure I was hoping to end up with, is:
Even though my Basic and Super tables share the same properties, with Super having it's own additional properties, there's no relationship between Basic data and Super data.
From having a look at Microsoft's tutorial on implementing inheritance, there's two options:
Table per type
Table per hierarchy
Neither of these are what I'm trying to achieve.
Perhaps I should be using interfaces to define the common properties that exist between unrelated entities. It seems like I need to back and re-evaluate my design anyway.
If some of the base classes of the entity is identified as entity (as with your Super and Basic), by default EF Core will try to use one of the database inheritance strategies.
If you don't want that (want to treat is just like non entity base class), then you have to configure that explicitly at the very beginning of the OnModelCreating, e.g. for your sample
modelBuilder.Entity<Super>().HasBaseType((Type)null);
or more generally using a loop similar to this
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
entityType.BaseType = null;
and then define explicitly the entity hierarchy if and where needed.
I'm having some issues with understanding how to property set up one-to-one relationships for a Web API using Entity Framework Core.
Consider the following objects:
public class Car
{
[JsonIgnore]
public Int32 CarId { get; set; }
public virtual Horn Horn { get; set; }
public virtual ICollection<Wheel> Wheels { get; set; }
}
public class Horn
{
[JsonIgnore]
public Int32 HornId { get; set; }
public Sound Sound { get; set; }
}
public class Sound
{
// some other props
}
When I perform a query in my repository, one-to-many is by default excluded unless I use .Include(), however, for one-to-one properties, they are by default included when I serialize.
e.g. It gets really messy because I query for a car and it returns all it's sub-components in the JSON response.
I can set car.Horn = null, etc, but that seems difficult to maintain for complex objects. I would like it to function like .Include(), but by default be excluded (in case I do want to query the full object).
Edit: Note, this issue is recursive, the car pulls in horn which pulls in sound. On a real world example like a user table, the automatically pulled in data when serialized is huge unless specifically nullifying the child properties.
EDIT2:
Here is an example of a repository call that by default bring back all one-to-one properties:
var group = _context.Car.Include(c =>
c.Wheels).SingleOrDefault(u => u.CarId == id);
Note the Include works as expected for many-to-one, but this query, even when the Include is removed, will recursively return all child objects that are one-to-one.
It does appear some type of lazy loading is being introduced in EF CORE 2.1
This article should give you a hint.
https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
Mainly:
Turn lazy loading off for serialization
Lazy loading and serialization don’t mix well, and if you aren’t
careful you can end up querying for your entire database just because
lazy loading is enabled. Most serializers work by accessing each
property on an instance of a type. Property access triggers lazy
loading, so more entities get serialized. On those entities properties
are accessed, and even more entities are loaded. It’s a good practice
to turn lazy loading off before you serialize an entity. The following
sections show how to do this.
EDIT:
Here is the way to disable lazy loading for all entities. But note you have to achieve this several way, so check the other options in the article...
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Context mapping
modelBuilder.Entity<SessionFeedbackModel>(entity =>
{
entity.HasOne(s => s.Session).WithOne(p => p.Feedback)
.HasForeignKey<SessionFeedbackModel>(s => s.SessionId).OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<SessionQuestionModel>(entity =>
{
entity.HasOne(e => e.SessionResult).WithOne(e => e.SessionQuestion)
.HasForeignKey<SessionQuestionResultModel>(e => e.SessionQuestionId)
.OnDelete(DeleteBehavior.Restrict);
});
Models
public class SessionQuestionResultModel
{
public int Id { get; set; }
public int SessionQuestionId { get; set; }
public SessionQuestionModel SessionQuestion { get; set; }
}
public class SessionFeedbackModel
{
public int Id { get; set; }
public int SessionId { get; set; }
public SessionModel Session { get; set; }
}
EF Core 1.x or 2.x does not support 1 to 1 very well or at all, but it can be done this way, this would be radically different for EF 6.x.x
My problem is to save existing objects that are part of a many-to-many relationship in a code-first database with EF6.
I am receiving objects from a web service and they look (simplified) like:
public class Car
{
[DataMember]
public virtual string CarId { get; set; }
[DataMember]
public virtual ICollection<Contract> Contracts { get; set; }
}
public class Contract
{
[DataMember]
public virtual string ContractId { get; set; }
[DataMember]
public virtual ICollection<Car> Cars { get; set; }
}
I have a code-first database and set the relationship with:
modelBuilder.Entity<Contract>().HasKey(t => new {t.ContractId});
modelBuilder.Entity<Car>().HasKey(t => new {t.CarId})
.HasMany(c => c.Contracts)
.WithMany(c => c.Cars)
.Map(x =>
{
x.ToTable("CarContracts");
x.MapLeftKey("CarId");
x.MapRightKey("ContractId");
});
When I get a list of cars I can save the first car and EF creates the relation table and the contracts successfully. On the second car the save fails saying "constraint failed UNIQUE constraint failed: Contracts.ContractId", since I'm trying to insert a Contract with the same Id as an already existing Contract.
The solutions I found to this problem is to set the Contract in the dbContext to Attached:
foreach (var contract in car.Contracts)
{
context.Contract.Attach(contract);
}
This throws the same exception as the previous save. When I try to modify the Contract-list of the second car I get a NotSupportedException, the only solution I can think of is recreating the car objects and attach the same Contract-object to them, which seems unnecessarily complicated.
Is there any way to tell EF that my two different Contract-objects actually are the same?
Is there any way to tell EF that my two different Contract-objects
actually are the same?
The only way is they actually are the same object. If they are different instances even having all the same data EF won't recognize them as same entity.
So the only solution is to replace all same id contract instances by a single one.
Or simpler: create an explicit entity representing the N:N relation and then simply build a list to insert as follows:
var toInsert = new List<CarContract>();
foreach(var car in cars)
{
toInsert.AddRange(car.Select(x=>new CarContract {CarId=car.Id,ContractId=x.Id}));
}
We have a huge database with 770 tables and want to do some performance testing with EF 6.1x.
We want to query only 5 of those 770 tables. Is it possible to create a "light" DBContext with only 5-6 entities/DBSets instead of using the full 770-tables-context?
When we use the full context, a simple query with 4 joins takes 45 seconds. Thats' 44 seconds too long.
We are using code-first (reverse engineered).
The problem:
When we create such a "light" version of the full context (i.e. 5 tables only), EF complains that all the other entities that are somehow related to these 5 tables have missing keys. We only map the keys, properties, relationships for those 5 tables, but not the rest.
Since the query written in LINQ only queries 5 tables, EF should simply ignore the other 765 tables, but it won't.
Why not? LazyLoading=true/false doesn't seem to have any bearing on this.
Note: Obviously one could create a view in the DB that does what we do in code with a LINQ query. The question is can it be done with a "light" DbContext as above.
There's the "light" version of the context:
public class ItemLookupContext : DbContext
{
static ItemLookupContext()
{
Database.SetInitializer<ItemLookupContext>( null );
}
public ItemLookupContext()
: base( "Name=ItemLookupContext" )
{
//Configuration.LazyLoadingEnabled = true;
}
public DbSet<Identity> Identities { get; set; }
public DbSet<Item> Items { get; set; }
public DbSet<Price> Prices { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Brand> Brands { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Configurations.Add( new IdentityMap() );
modelBuilder.Configurations.Add( new ItemMap() );
modelBuilder.Configurations.Add( new PriceMap() );
modelBuilder.Configurations.Add( new DepartmentMap() );
modelBuilder.Configurations.Add( new BrandMap() );
//ignore certain entitities to speed up loading?
//does not work
modelBuilder.Ignore<...>();
modelBuilder.Ignore<...>();
modelBuilder.Ignore<...>();
modelBuilder.Ignore<...>();
modelBuilder.Ignore<...>();
}
}
what you trying to something like "Bounded Context" which is one of DDD patterns
So, you can check this article by Julie Lerman, Shrink EF Models with DDD Bounded Contexts
Simply just create your DBContext for your tables. To prevent Entity Framework moaning about the not mapped tables, you have switch off the db initialization in your application. Put this in your global.asax/Startup.cs
Database.SetInitializer<YourDbContext>(null);
It tells EF to stop comparing your actual DB structure against your DbContext.
It also means that if someone changes your EF mapped tables, you have no chance of getting notified about that.
When you have a many-to-one relation between class A and class B:
public class A
{
public B b {get; set;}
}
public class B
{
public ICollection<A> As {get; set;}
}
and define following DbContext, EF automatically includes DbSet<B> to the DbContext:
public class MyContext : DbContext
{
...
public DbSet<A> As { get; set; }
}
So, if you want your light DbContext does not includes the related DbSets, simply use Ignore method:
public class MyContext : DbContext
{
...
public DbSet<A> As { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Ignore<B>();
}
}
It looks like you used a tool like Entity Framework Power Tools to generate the entity classes and mappings. This would have generated a class for each table in the database, a huge context, mappings for all these classes and all possible associations. This is way too much.
First remove all classes and mappings that you don't need. Then remove all associations to removed classes in the few classes you have left, not the primitive foreign key fields. Also remove all DbSets from the context except the few you need.
This slimmed-down class model will be consistent in itself. It won't have associations to all entities in the database, but it will be possible to filter by foreign key values that refer to entities outside the context.
If you generated/created the code in any other way this is still the crux: only use navigation properties to other classes in the class model. For other references use primitive foreign key properties.
I've got this question that's been bugging me and my vast and unfathomable intellect just can't grasp it. The case: I want to make multiple one-to-many relationships to the same entity using the fluent nhibernate automapper and export schema.
I have:
Base Class:
public abstract class Post<T> : Entity, IVotable, IHierarchy<T>
{
public virtual string Name
{
get; set;
}
public virtual string BodyText
{
get; set;
}
public virtual Member Author
{
get; set;
}
}
and Inheriting Class:
[Serializable]
public class WallPost : Post<WallPost>
{
public virtual Member Receiver
{
get; set;
}
}
The 'Member' properties of WallPost is a foreign key relationship to this class:
public class Member : Entity
{
public Member()
{
WallPosts = new IList<WallPost>();
}
public virtual IList<WallPost> WallPosts
{
get; set;
}
}
I hope you're with me until now. When I run the exportschema of nhibernate I expect to get a table wallpost with 'author_id' and 'receiver_id' BUT I get author_id, receiver_id,member_id. Why did the Nhibernate framework add member_id, if it's for the collection of posts (IList) then how do you specify that the foregin key relationship it should use to populate is receiver, i.e. member.WallPosts will return all the wallposts of the receiver.
I hope i made sense, if you need anything else to answer the question let me know and I'll try to provide.
Thanks in advance
P.s. If I change the property name from 'Receiver' to 'Member' i.e. public virtual Member Member, only two associations are made instead of 3, author_id and member_id.
E
THE SOLUTION TO THIS QUESTION FOR ANYONE ELSE WONDERING IS TO ADD AN OVERRIDE:
mapping.HasMany(x => x.WallPosts).KeyColumn("Receiver_id");
Most likely (I don't use auto mapping, I perfer to write my own classmaps) it's because the auto mapping assuming that your Member's "WallPosts" is controled by the wall posts. So it creates a member_id for that relationship.
Edit: Try adding an override in your fluent config, after AutoMap add:
.Override<Member>(map =>
{
map.HasMany(x => x.WallPosts, "receiver_id")
.Inverse;
});