Add/insert a new entry containing complex type in Entity Framework 7 Code first - ef-code-first

I am trying to add a new entry for my object model which has few complex type, using EF 7 code first. But it's not working and throwing exception.
Model:
public class Student
{
public int id {get; set;}
public string Name {get; set;}
public Address Address {get; set;} -> complex type
}
public class Address
{
public int Id {get;set;}
public string Location {get;set;}
}
Code first code:
class SchoolContext {
DbSet<Student> Students {get; set;}
DbSet<Address> Addresses {get; set;}
}
var context = new SchoolContext();
context.Students.Add(new Student { Id = 1, Name = "XYZ", Address = new Address { Id = 2, Location = "US" } } -> The add method has second parameter which says include dependent objects, and by default it is true.
context.SaveChanges();
This throws exception:
{"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_Student_Address_AddressId\". The conflict occurred in database \"School\", table \"dbo.Address\", column 'Id'.\r\nThe statement has been terminated."}
This error I think means, the Address object does not exists in database, and if I add the address first and then Student, it works. But this is just too much code, as in my real application objects has many complex types.
Ideally this should work, but it does not.

Based on this bug report, you should add item before.
It is considered as-designed. At least until RC2.

Related

Entity Frameworok generate guid as id and save it

I'm trying to save to my table Users let's say, string ID, string email, and string password. The problem is that ID must be a guid that I have to create it and save it and not SQL server. Any ideas how?
I searched but I only found how to make SQL server to create the guid.
First of all, tell Entity framework that you will generate the value of the primary key:
Use DatabaseGenerated Attribute
public class School
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name {get; set;}
...
}
The None option prevents values from being generated by the database automatically in cases where they would otherwise be created.
Furthermore, consider to overwrite DbContext.SaveChanges(). In this procedure ask the ChangeTracker for all elements that are Added. Generate an Id for every Added element. It might be dangerous to let others generate an Id, because they might be adding a constant value or just an auto-increment.
Another possibility would be to generate it within the Add function, but if you do that, then users could change your generated Id. So the proper place is within SaveChanges:
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
// This will fail: the added element doesn't have a property Id:
addedElement.Id = GenerateId();
}
return base.SaveChanges();
}
For this you have to be certain that every added element has a property Id. The simplest way is to create an interface and let all your tables implement this interface:
public interface IID
{
string Id {get; set;}
}
public class School : IID {...}
public class Student : IID {...}
public class Teacher : IID {...}
public class DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students{get; set;}
public DbSet<Teacher> Teachers {get; set;}
public override int SaveChanges()
{
var addedElements = this.ChangeTracker.Entries.Cast<IID>
.Where(entry => entry.State == EntityState.Added);
foreach(var addedElement in addedElements)
{
addedElement.Id = GenerateId(); // Every Added element implements IId
}
return base.SaveChanges();
}
private string GenerateId()
{
... // TODO: return unique ID, for instance a GUID
}
}

MongoDB is identifying field with ObjectID as null even thought the value exists

I have created a collection and added an index key by grouping 4 fields as unique.
Here are the example of field and value.
"username":string,
"companyID": ObjectID,
"areaCode": string,
"lotNum":string
The companyID's ObjectID, I'm getting it from another collection's document ID as it's ID.
I tried to insert through MongoDB .Net driver and encountered some problem.
Company company = new Company()
{
CompanyName = "xyz"
};
await _company.InsertOneAsync(company); //_company is IMongoCollection object
SomeClass someClass = new SomeClass()
{
UserName = "James",
CompanyID = company.ID, //This is the ObjectID generated by MongoDB
AreaCode = "ABC",
LotNum = "1234a"
};
await _someClass.InsertOneAsync(company); //_someClass is IMongoCollection object
So, the document object in MongoDB will look like below. I can actually view the document using Compass.
_id:ObjectID("5b062d5be75ed035f057bf06")
username:"James",
companyID: ObjectID("5b062d5be75ed035f057bf05"),
areaCode: "ABC",
lotNum:"1234a"
Now the problem is when I tried to find the document in SomeClass collection with {companyID:ObjectID("5b062d5be75ed035f057bf05")}, I'm unable to find it.
But if I use {companyID:null}. It's returning the document.
And I'm not able to add any new document with same username,areaCode and lotNum with a different companyID, "as duplicate key error collection" occurs eventhou the ID is from a newly created Company object have brand new ID. MongoDB is still saying it is NULL.
Am I doing something wrong? How can I fixed this problem.
Here are my data object for Company and SomeClass
public class Company
{
public ObjectId Id { get; set; }
public string CompanyName{ get; set;}
}
public class SomeClass
{
public ObjectId Id { get; set; }
public string UserName { get; set;}
[BsonRepresentation(BsonType.ObjectId)]
public string CompanyID { get; set; }
public string AreaCode{ get; set; }
public string LotNum{ get; set; }
}
when you retrieve a document from collection and get its ObjectId its a string.
You need to convert that to ObjectId type object.
new ObjectId("string objectid")
This is what we do in nodejs. Please check C# mongodb driver docs
As mentioned by Alex Blex in the comments under my question, it is due to case sensitivity.
Here's his answer:
It's case sensitive. "companyID" != "CompanyID". Basically companyID is undefined in any of your documents so all of them match criteria companyID:null

Entity Framework making incorrect PK-FK mapping on Code First migration

I have the following 3 classes set up to be created in a SQL Server database using Entity Framework Code First migrations. The Survey object is the main table.
public class Survey
{
public int SurveyId {get; set;} //Primary Key
public string Description {get; set;}
public bool HasDevice {get; set;}
public bool HasProcess {get; set;}
public virtual Process Process {get; set;}
public virtual ICollection<Device> Devices {get; set;}
}
Each Survey can have multiple Devices (1-to-many)
public class Device
{
public int DeviceId {get; set;} //Primary Key
public string DeviceType {get; set;}
public int SurveyId {get; set;} //Foreign Key
public virtual Survey Survey {get; set;}
}
Each Survey should have only one Process (1-to-0..1)
public class Process
{
public int ProcessId {get; set;} //Primary Key
public string ProcessInfo {get; set;}
public int SurveyId {get; set;} //Foreign Key
public virtual Survey Survey {get; set;}
}
The Fluent API mapping for these classes looks like this.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("Survey");
modelBuilder.Entity<Survey>().HasOptional(x => x.Process).WithRequired(x => x.Survey);
modelBuilder.Entity<Survey>().HasMany(x => x.Devices).WithRequired(x => x.Survey);
}
The problem is that when I apply the code first migration, the ForeignKey property in the Process table (1-to-0..1) keeps getting set to the ProcessId field rather than the SurveyId. This means that every time I try to add a new Process record, I get the following error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Survey.Processes_Survey.Surveys_ProcessId". The conflict occurred in database "Backflow", table "Survey.Surveys", column 'SurveyId'.
The 1-to-many mapping for Device works just fine.
I thought initially that this was due to having all my PK fields just say Id, but even after adding in the additional label part, it still makes the incorrect PK-FK link. I have also tried avoiding the Fluent API by adding the DataAnnotation [Key, ForeignKey("xyz")] instead but it has the same result. Recompiling the project, restarting Visual Studio, and even creating a new project and a new database do not help.
Is there something in the Fluent API or DataAnnotations that I am missing to get this to join correctly? Also, manually fixing the FK in the database does make it work, but that kind of defeats the purpose of doing everything in Code First with migrations.
The fluent mapping of the 1-0..1 relationship is correct:
modelBuilder.Entity<Survey>()
.HasOptional(s => s.Process)
.WithRequired(p => p.Survey);
But Process shouldn't have a SurveyID property (and column). In EF6, the dependent part of a 1-0..1 relationship (here: Process) is supposed to have a primary key that also refers to its principal (here: Survey) as foreign key. So Process.ProcessID is both primary key and foreign key. Thus, one Process is uniquely tied to one Survey.
By the way, in the other mapping, I would also mention the foreign key: if configuration is chosen over convention, it better be complete.
modelBuilder.Entity<Survey>()
.HasMany(s => s.Devices)
.WithRequired(d => d.Survey)
.HasForeignKey(d => d.SurveyId);

How to sort data in an asp.net gridview by properties of sub-objects?

I have a List<Role> (see below) that I am binding to an asp.net gridview. I want to sort this data using SortExpression, such that it is sorted by two properties of sub-objects of the rows. Specifically, I want to sort by the Application's Name, then the ApplicationType's ApplicationTypeName.
How can I do this?
The classes here are:
public class Application
{
public string Name {get; set;}
public int Status {get; set;}
}
public class ApplicationType
{
public string ApplicationTypeName {get; set;}
public int ApplicationTypeStatus {get; set;}
}
public class Role
{
public Application oApplication {get; set;}
public ApplicationType oApplicationType {get; set;}
}
Edit: note that I was responding to the earlier verison of the question, before it related to gridview; still, this might be useful...
Worst case: you can use the approach here to pre-sort the list before binding it to the gridview.
Various options:
implement IComparable[<T>]
implement IComparer[<T>]
use an ad-hoc sort
I'm guessing you just need the last, so perhaps:
list.Sort((x,y) => {
int delta = string.Compare(x.Application.Name, y.Application.Name);
if (delta == 0) delta = string.Compare(
x.ApplicationType.ApplicationTypeName, y.ApplicationType.ApplicationTypeName);
return delta;
});
Alternatively, you can perhaps do it via LINQ in the source data - note however that this is done when creating a new list - it isn't an in-place sort of an existing list:
var list = source.OrderBy(x => x.Application.Name)
.ThenBy(x => x.ApplicationType.ApplicationTypeName)
.ToList();

The member with identity does not exist in the metadata collection. Parameter name: identity

We are using EF Code First 4.3.1.
We are developing an ASP.NET Web Role referring to multiple class libraries.
There are two class libraries each containing classes and an individual DBcontext.
Lets say the Library1 has classes A and B.
DBcon1: DbSet and DbSet
Lets say the Library2 has classes C and D.
Class C{
[Key]
public int CId{ get; set;}
[Required]
public virtual A referencedA {get; set;}
}
DBcon2: DbSet<C> and DbSet<D>
When I try to use the DBcon2 as such:
using (var con = new DBcon2())
{
C vr = new C();
vr.CId= 1;
vr.referencedA = DBCon1.As.First();
con.Cs.Add(vr);
con.SaveChanges();
}
I get an exception as:
"The member with identity does not exist in the metadata collection.
Parameter name: identity"
Both DBCon1 and DBcon2 are using the sane SQL Server Database "SampleDB".
Please point me in the right direction.
I got this error and fixed it by not trying to set the navigation property in the related table, just set the foreign key id instead
eg
public class Student()
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
public class Course()
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Main code:
var myCourse = new Course();
var myCourseId = 1;
var student = new Student() {
CourseId = myCourseId
// Course = myCourse <-- this would cause the error
}
Might not be your issue but maybe it will point you in the right direction and hopefully will help someone else.
The exception is a bit cryptic, but pretty clear if you realise that a context needs information about entities (metadata) to be able to write sql statements. Thus, your DBcon2 context has no clue where to find the primary key of an A, because it has no metadata about A.
You could however set an integer property A_Id (or the like), but then you'll have to write custom code to resolve it to an A.
Another option is to merge (parts of) the contexts, if possible.

Resources