I am defining a lot of classes in my project and as an audit requirement, every entity in my project has to have to foreign keys to the "AspNetUser" table, one specifying the user that created the record, and one for the user that has updated it. The following is a sample of "Customer" entity, however, as I said there is tons of entities all with the same requirement:
public class Customer {
public int Id {get; set;}
// bunch of properties
[Required]
public string CreateUserId {get; set;}
[Required]
public string UpdateUserId {get; set;}
[ForeignKey("CreateUserId")]
public virtual ApplicationUser CreateUser {get; set;}
[ForeignKey("UpdateUserId")]
public virtual ApplicationUser UpdateUser {get; set;}
}
Now because there is two foreign keys to the AspNetUser table, when I want to create migration and update the database I get the good old error:
Introducing FOREIGN KEY constraint 'FK_dbo.Customers_dbo.AspNetUsers_UpdateUserId' on table 'Customers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
I also understand that by adding some codes to the "OnModelCreate" and remove the cascade effect for the "Customer" entity, I will be able to get around this, however, as I said, every entity in my application is going to have this two links to the AspNetUser table which means I have to add that piece of code to the OnModelCreate tons of times each time with the specific entity I'm talking about.
Also, I don't want to cancel the CASCADE DELETE effect as a whole, as it affects the functionality of the app in all of the parts. Any idea how can I efficiently get around this?
I remember back in the days when I did database programming, there was never any issues with one entity having two foreign keys to another one.
you should set WillCascadeOnDelete = False by fluentApi, Like This :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer >()
.HasRequired(c => c.CreateUser)
//.WithMany()
.HasForeignKey(c => c.CreateUserId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(c => c.UpdateUser )
//.WithMany()
.HasForeignKey(c => c.UpdateUserId )
.WillCascadeOnDelete(false);
}
Related
I am using EF code first approach. I need to rename my PK column name.
public class classname
{
[Key]
public int OldId { get; set; }
}
I need to change OldId to NewId. I tried renaming my OldId to NewId, I tried updating my database with Migration but that didn't help.
I tried renaming both in column name and model and again when I tried to update my database I still get error.
How can I rename my primary Key column without losing its data?
Is the question that OldId is not used in your code anymore but is still in your database, or is your problem that it OldId is in your code but has a different column name in your database?
You can use fluent API or data annotations if you want to tell entity framework that the name of property in your DbSet has a different column name in the database:
Fluent API Method
public class MyDbContext : DbContext
{
public DbSet<ClassName> ClassNames {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Tell entity framework that you are now building ClassName:
var classNameConfig = modelBuilder.Entity<ClassName>();
// ClassName has an int primary key in OldId:
classNameConfig.HasKey(className => className.OldId);
// "columnX" in the database corresponds with property OldId
classNameConfig.Property(className => className.OldId)
.HasColumnName("columnX");
}
}
Of course in this specific example this can be concatenated into one statement.
The nice thing about fluent API is that you disconnect your classes from the actual database representation. This hides how the actual database names its columns, column order, min size, max size etc from the user of the DbSets.
Just specify a different DbContext, and you can use the same classes with a different Db.
For instance, if you want to use the same entity framework classes in different databases:
class BaseContext : DbContext
{
public DbSet<MyClass> MyClasses {get; set;}
}
// context to be used with database X:
class DataBaseXContext : BaseContext
{
protected override void OnModelCreating(
DbModelBuilder modelBuilder)
{
// I want to use "columnX" for property A, which is optional:
modelBuilder.Entity<MyClass>
.Property(p => p.A)
.HasColumnName("columnX")
.IsOptional();
}
}
// context to be used with database Y:
class DataBaseXContext : BaseContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// I want to use "columnY" for property A, which is required:
modelBuilder.Entity<MyClass>
.Property(p => p.A)
.HasColumnName("columnY")
.IsRequired();
}
}
Data Annotations Method
If you are absolutely sure you want your classes to be used in only one database, with one pre-defined model, you could use Data Annotations.
However, your question shows that it is not uncommon that the same classes will be used in a different database model. So, although this method is very commonly used, I urge you to reconsider a decision to use Data Annotations.
public class classname
{
[Key]
[Column("ColumnX")]
public int OldId { get; set; }
}
changing the Identity column may lead to instability and you cant change anything with the column unless you kill all the Key constraints (all table relation ship)
t will be tooo complicated i would advice you that if data isnt Important then just delete the database and re run migraions it will recreate the database again
The name of the entity property can be different from the name of the database column.
If you need to change the name of the column on the database you can do it with the DBMS manager (SQL Server Enterprise Manager if you are using SQL Server) or running the proper DDL statement (sp_rename on SQL Server) then set the new column name on the property using the attribute ColumnAttribute
[Column("NewId")]
[Key]
public int OldId { get; set; }
If you need to change the name in the code but not on the database you can do a similar thing
[Column("OldId")]
[Key]
public int NewId { get; set; }
In this case you should do a refactor (rename with F2 if you have standard visual studio Keys or Ctrl+R, R if you are using resharper)
If you need to change both you can do both.
I am having .NET MVC 5 and Identity...
I am trying to get a one to one relationship for my Member class to my MemberInfo class..
So, My classes looks something like this:
IdentityUser is in the Microsoft.AspNet.Identity.EntityFramework namespace with string Id as its ID.
public class GRNUser : IdentityUser {
....
....
}
public class MemberUser : GRNUser {
public virtual Member MemberInfo {get; set; }
}
public class Member {
public int ID {get; set; }
public string MemberUserID {get; set; }
public virtual MemberUser MemberUser { get; set; }
}
In my Context, I have this
modelBuilder.Entity<Member>().HasRequired(m => m.MemberUser)
.WithOptional(u => u.MemberInfo);
So, the MemberUser and Member should be able to navigate to and from each other using the MemberUser's ID property and Member's MemberUserID property.
However, when my Database is created, it has an additional column "MemberUser_Id" instead of using my MemberUserID that I specified. How do I make it use "MemberUserID" that I specified?
I've tried using a few combination so of the ForiegnKey Data Annotation, but keeps on getting this error:
Member_MemberUser_Source: : Multiplicity is not valid in Role 'Member_MemberUser_Source' in relationship 'Member_MemberUser'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I don't know whether i understood you right or not, but i'l do my best to help.
(I'l assume that you work with code first migration)
If you want to make a one-to-one relation, why not try to make a standalone entity for your info which will have a foreign key for the user entity and that foreign key will be annotated as the primary key of the entity?
Another way is why just not add whatever attributes you like to the Application User entity and work with it?
In any case i might have misunderstood your purpose, so please feel free to explain further since your post is a bit confusing.
I have a complex graph with a collection child entities.The relationship between parent and child entities is one-many relationship and has an Independent association. I know that I cant persist my graph without manual synchronization which I want to avoid. I found GraphDiff solution promising and I started exploring it but I couldn't achieve what I intend to as my scenario is somewhat peculiar. This issue is purely related to GraphDiff but I also welcome other solutions.
public class person
{
public virtual int Id {get; set;} //primary key
public virtual string Name {get; set;}
public virtual ICollection<Car> Cars {get; set;}
}
public class Car
{
public virtual int PersonId {get; set;} // is a foreign key
public virtual Datetime BoughtDate {get; set;} // this property and above mentioned foreign key together makes a primary key in database
public virtual DateTime InsuraceStartDate {get; set;}
public virtual DateTime InsuraceExpiryDate {get; set;}
public virtual DateTime LastServiceDate {get; set;}
}
At any given time my Person.Cars collection may have
1. A new car object.
2. An existing car object with updated values.(insurance or service dates).
Id property on Person and PersonId on Car will be 0.
Person _person = GetPersonToAddOrUpDate();
int id = _person.id; //id will be 0.
At this point we are not sure the object we received has to added or updated. PersonId on child entities(Cars) will also be 0.Now if we call.
Context.UpdateGraph(_person, m => m.OwnedCollection(x => x.Cars));
throws exception at FindEntityMatching(entity).
To overcome this I have to update Id property on person.
var dbPersonId = Context.Single<Person>(x => x.Name == _person.Name).Id;
_person.id = dbPersonId != null ? dbPersonId : 0;
Context.UpdateGraph(_person, m => m.OwnedCollection(x => x.Cars));
It deletes all entities in dbPerson.Cars and adds _person.Cars to dbPerson.Cars and saves. It means all records in my Cars table in database are deleted and records from _person.Cars are inserted. I noticed this is happening because Now the child entities does not have a Id property. I have to set them manually.
foreach(var car in _person.Cars)
car.PersonId = _person.Id
Now if you call
Context.UpdateGraph(_person, m => m.OwnedCollection(x => x.Cars));
This will add entities in collection from transient object missing in the persistent object and updates the object matching in the persistent object and deletes rest of the entities from the persistent object.
var dbCars = conext.Where(x => x.Personid == _person.Id).Select(x).ToList();
say dbCars.Count is 8 and _person.Cars.Count is 3.(one new car and 2 old cars with updated values)
Once you run.
Context.UpdateGraph(_person, m => m.OwnedCollection(x => x.Cars));
2 old cars from _person.Cars matching in dbCars will be updated.
1 new car from _person.Cars not matching in dbCars will be added.
Now the count of dbCars must be 9 as we added a new car. Now check the count.
var dbCarCount = conext.Where(x => x.Personid == _person.Id).Select(x).ToList().Count();
dbCarCount will be 3. Rest of the 6 cars from dbCars are being removed.
I wish I am wrong as I like to use your solution.My work in this area is stalled. Please let me know If I'm missing something or you need more information. I know my write up is confusing as I tried to put all my testing experience with GraphDiff. Hope you address this issue.
If you have a disconnected graph and you want to merge it to the database then there are 3 things it should do:
Insert new entities
Update existing entities
Delete entities not in the graph
You appear to be having an issue with the last part.
If the graph has not been retrieved from the database in the first place then you are not using GraphDiff as it was intended. You will have to do your adding and updating manually.
There are two tools I've found for that:
Trackable-Entities
Lightweight client-server facilities that enable caching and storing disconnected graphs.
Supports PCLs, Xamarin and other platforms
MIT license
Breeze#
Full-fledged client-server facilities that enable caching and storing disconnected graphs.
Not supported in UWP
Very low maintenance
MIT licenced
I have an entity/class/table which is referenced from several other entities, and I use Fluent NHibernate to handle the ORM for me. In a few instances, it's a simple reference where I can store the foreign key ID as a column and handle the reference in that simple way, but in a few other instances I need to reference a list of these items, and it needs done for at least three classes I can think of. You can assume this setup will be copied to handle the other classes' relationships.
Here's how the common entity looks (the one that is owned by several other entities in HasManys):
public class Student {
public virtual int Id {get; set;}
public virtual string Name {get; set;}
}
And, here's what the ShopCourse entity looks like:
public class ShopCourse {
public virtual int Id {get; set;}
public virtual int Name {get; set;}
public virtual IList<Student> Students {get; set;}
}
Imagine that a couple other classes I have, such as specific courses, can "own" several students. In order to maintain that relationship I must create a table in my database that tracks the foreign keys between the two (for each entity that references Student) - no entity needed for this intermediate table, and Fluent won't need to think of it unless I hand it the string name of the table itself:
Table: ShopCourseStudents
int - ShopCourseId
int - StudentId
Lastly, here are my mappings. You can assume that the entities themselves map out fine - things such as the naming scheme for the Id are resolved and working correctly. The issue lies when I attempt to initialize any entity that has a HasMany of Student:
//Inside a FluentlyConfigure().Mappings(m => m.AutoMappings.Add() call:
.Override<ShopCourse>(map => {
map.HasMany(x => x.Students)
.Table("ShopCourseStudents")
.KeyColumns.Add("ShopCourseId")
.KeyColumns.Add("StudentId")
.Cascade.All();
})
The issue is that when I attempt to load a list of ShopCourses I get the Fluent error:
Foreign key (ABC123AF9:Student [ShopCourseId, StudentId]) must have
same number of columns as the referenced primary key (ShopCourses
[Id])
I do not override Fluent's mapping of Student as it's straightforward. For the purpose of this example, Student doesn't need to know which ShopCourses it belongs to, or any of the other courses that may own that particular Student record.
This seems like I'm doing something basic, wrong - what is it, exactly? Much obliged in advance!
So, the issue was with the custom code that I re-use with my projects, apparently the piece written to handle the ManyToMany convention is mostly broken. What I was looking for here was a ManyToMany relationship, not HasMany. The issue I had was that my code was forcing a reference on the child object (in this example, Student) to the parent, which I do not need and only complicates things. Removing that, and my ManyToMany then works:
.Override<ShopCourse>(map => {
map.HasManyToMany(x => x.Students)
.Table("ShopCourseStudents")
.ParentKeyColumn("ShopCourseId")
.ChildKeyColumn("StudentId")
.Cascade.All()
I want to create following table based on below class dictionary. I get exception when I add records. What's wrong with this mapping? This code works if I point Child property of class "B" to another class (example "C").
database table
table A {id, name}
table B {parentId, childId, Type}
Class and Mapping
Public class A
{
public int Id {get;set;}
public string Description {get;set;}
}
Public class B
{
[Key, Column(Order=0)]
public int ParentId {get;set;}
[Foreignkey("ParentId")]
public A Parent {get;set;}
[Key, Column(Order=1)]
public int ChildId {get;set;}
[Foreignkey("ChildId")]
public A Child {get;set;}
[Key, Column(Order=2)]
public string Type {get;set;}
}
UPDATE
Error Message is: Introducing FOREIGN KEY constraint 'B_Parent' on table 'B' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
Thanks,
Ashraf
After reading the following posts I found the solution.
http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
Entity Framework Code First - Defining Relationships/Keys
It's the SQL Server error. Class 'A' referencing twice in class 'B'. Code First attempt to turn on cascade delete for both Parent and Child columns in class 'B' which cause the exception.
Fix is manually override one of the cascade option to false in class B. I don't know how to set CascadeOnDelete option as dictionary attribute. But here is the fluent api.
HasRequired(x => x.Parent)
.WithMany()
.HasForeignKey(x => x.ParentId)
.WillCascadeOnDelete(false);
I wish EF team attempt to write a comprehensive guide for Code First configuration (Fluent API) manual for us. AdventureWorks-2008 database is a great candidate for that.
this exception is SQL server specific.It will go away if you turn off the cascade for the relationship.By default Ef will turn it on for you.you can do this through fluent api.
In the relationship just add the following configuration
.WillCascadeOnDelete(false);
hope this helps.