Customer class:
class Customer
{
int id;
int phone;
int fax;
PhoneNumber phone1;
PhoneNumber fax1;
}
Class PhoneNumber
{
int id;
int prefixid;
string number;
}
The PhoneNumber was auto-Generated by EF4, I've change the auto-generated names to phone1 and fax1
Both phonenumber id and customer id are identity columns in the DB.
Now I want to create a new Customer:
var newCustomer = Customer.CreateCustomer(0, CompanyID);
PhoneNumber fax = new PhoneNumber();
PhoneNumber phone = new PhoneNumber();
fax.Customers.Add(newCustomer);
phone.Customers.Add(newCustomer);
context.Customers.AddObject(newCustomer);
context.SaveChanges();
But now I get:
System.Data.UpdateException: {"Cannot
insert explicit value for identity
column in table 'PhoneNumber' when
IDENTITY_INSERT is set to OFF."}
Why isn't EF4 deal with the identity column of the related table as it should deal with a new entity ? How can I make this work ?
(EF4 Should've create 2 entiries in Phones table, get their identity and add it to the customer row of the new customer - at least that what I want to do)
I know I Can create 2 Phone records, and than create the customer one, but
a. I want it to be done in one act.
b. if cusotmer creation fails - I don't want empty records on the phones table...
Well these two things contradict each other:
Both phonenumber id and customer id are identity columns in the DB.
But then the error:
System.Data.UpdateException: {"Cannot insert explicit value for identity column in table 'PhoneNumber' when IDENTITY_INSERT is set to OFF."}
It may seem like this error is saying a duplicate identity is being set, but it's not - it's saying your trying to explicitly set the identity field.
What that is saying is that the ID column in PhoneNumber is not an identity field. Or at least EF does not think it is.
So i have four questions:
1) Have you confirmed the field is set to IDENTITY in the EDMX? Maybe something has overriden it?
2) What does the CreateCustomer method do? Does it just new up a Customer and set some properties? It should not touch the identity fields.
3) Have you setup the navigational properties/cardinalities properly on the EDMX? E.g 1 Customer .. 1-* Phone Numbers.
4) Try adding the Phone number to the Customer, not the other way around. It makes sense to add the "many" to the "one" - e.g Customer is the aggregate here:
var newCustomer = Customer.CreateCustomer(0, CompanyID);
newCustomer.phone1 = new PhoneNumber();
newCustomer.fax1 = new PhoneNumber();
context.Customers.AddObject(newCustomer);
context.SaveChanges();
I'd also suggest redesigning your model.
It's silly to have Customer with 1 Fax and 1 Phone Number, each with a property on there. It should be a Customer have many "PhoneNumber" entities.
You could use TPH on the PhoneNumber entity to discriminate between Fax and other types, and the above code would make much more sense:
var newCustomer = Customer.CreateCustomer(0, CompanyID);
newCustomer.Phones.Add(new FaxNumber());
newCustomer.Phones.Add(new MobileNumber());
context.SaveChanges();
Related
I have something like this:
modelBuilder.Entity<TransactionHistory>()
.HasOptional(history => history.Sender)
.WithMany()
.Map(s => s.MapKey("Sender"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<TransactionHistory>()
.HasOptional(history => history.Receiver)
.WithMany()
.Map(s => s.MapKey("Receiver"))
.WillCascadeOnDelete(false);
And in my table TransactionHistory, it creates unique identifiers at columns Sender and Receiver. I don't want those columns to be unique, what do i do?
TransactionHistory model:
public class TransactionHistory
{
public Account Sender { get; set; }
public Account Receiver { get; set; }
}
Edit: Ok. Apparently uniqueidentifiers are not the case. The problem is, that when i am adding transactionhistory item into database, i got the following error:
Violation of PRIMARY KEY constraint 'PK_dbo.Accounts'. Cannot insert
duplicate key in object 'dbo.Accounts'.\r\nThe statement has been
terminated.
An i add this item like that:
context.Transactions.Add(history);
context.savechanges();
(Transactions is a transactionhistory object)
EF uses uniqueidentifier as SQL column type for the foreign keys because the principal's (= Accounts) primary key is a uniqueidentifier - in C# it is a Guid, like public Guid AccountId { get; set; }.
It must choose this type because principal and dependent key types must match in a foreign key relationship in the database.
This does not mean that the foreign key column is unique (or has a unique index). Of course you can use the same uniqueidentifier value multiple times as the foreign key column value.
Entity framework works by storing a cache of a bunch of objects in DBContext. Even if all the properties are the same on your Account object (including your pk) entity framework will see this as a new object unless you remind it "hey, you already know about this". I can think of 2 ways to do this:
context.Entry(history.Sender).State = EntityState.Modified;
context.Entry(history.Receiver).State = EntityState.Modified;
or
Make sure when you set the Accounts on your history object that they are already attached to your dbcontext.
var sender = context.Accounts.FirstOfDefault(...your condition here...);
var receiver = context.Accounts.FirstOfDefault(...your condition here...);
history.Sender = sender;
history.Receiver = receiver;
Also EF does not load navigation/related entities unless you tell it to. So if you are editing history make sure you using .Include() to pull in your related objects.
Hope that helps.
I am a newbie, i read MVC Movie App tutorial and created an AddressBook based on that, i am using ADO.NET Entity DataModel for stroing values in Database table, as i do not know anyother way, i am storing following values in my table-
Id (primary key auto)
name
gender
phoneno(Heres the problem i want to add multiple phone numbers, i want to add text boxes on clicking "add" link )
not only i want to add text boxes on view but also store it in database table , how do i do that?
REMEMBER i am a newbie keep it simple
If you need more than one phone number for same Contact. It is a ONE to MANY relation ship. that means you need a seperate table to store your Phone numbers
I would create a new table called PhoneNumber like this structure
PHONE_NUMBER_ID (INT) PRIMARY KEY
PHONE_NUMBER (VARCHAR)
CONTACT_ID (INT) - Foreign key to the Contact table
Your sample data will look like
PHONE_NUMBER_ID PHONE_NUMBER CONTACT_ID
--------------- ------------ ----------
1 734578956 1
2 987546563 2
3 987645643 2
This means Contact 1 has one phone number and contact 2 has 2 phone numbers.
Now you need a Collection property in your Contact class to store the PhoneNumbers
public class Contact
{
public int ID { set;get;}
public string FirstName { set;get;}
//Other contact related proerpties
IList<string> PhoneNumbers { set;get;}
public Contact()
{
if(PhoneNumbers==null)
PhoneNumbers=new List<string>();
}
}
I am sure this question has been asked before, so I apologize in advance, but am not sure of the correct keywords to include in my searches...
I am having trouble understanding the proper pattern for updating (or even inserting) an object when one of its properties is a collection of other properties in a disconnected environment (like a website). My issue has to do with the idea that a web application is only returning a collection of id's as opposed to the full object. I think the best way to explain this is with code snippets.
Given the following objects
Public Class User
Public Property UserId As Integer
Public Property Username As String
Public Property Roles As ICollection(Of Role)
End Class
Public Class Role
Public Property RoleId As Integer
Public Property RoleName As String
Public Property Users As ICollection(OF User)
End Class
Public Class EFDbContext
Inherits Entity.DbContext
Public Property Users As Entity.DbSet(Of User)
Public Property Roles As Entity.DbSet(Of Role)
End Class
A database is created with 3 tables - Users, Roles, and RoleUsers.
I know I can easily do the following
Dim db = New EFDbContext()
Dim r1 = New Role() With { .RoleName = "User" }
Dim r2 = New Role() With { .RoleName = "Admin" }
db.Roles.Add(r1)
db.Roles.Add(r2)
Dim u1 = New User() With { .UserName = "test1", .Roles = New List(Of Role) }
u1.Roles.Add(r1)
db.Users.Add(u1)
db.SaveChanges()
And it will save both new roles to the database (giving them RoleId values of 1 and 2 respectively), a new user (giving it a UserId value of 1) and a new Role-User entry with RoleId 1 and UserId 1.
However, when dealing with a disconnected scenario like a website, most people would have a View Model to represent the input from the user which then gets mapped back to the Entities. In addition, for values representing the Roles, the data coming back would most likely only contain the unique key representing the Role. For example,
Public Class UpdatedUserViewModel
Public Property UserId As Integer
Public Property Username As String
Public Property RoleIds As ICollection(Of Integer)
End Class
...
...
Dim userEntity = db.Users.Find(user.Values.UserId)
AutoMapper.Mapper.Map(userValues, userEntity)
So while the userEntity.Roles collection may contain a single item, the mapper probably just added the entry with something like
ForMember(Function(u) u.Roles, Sub(m) m.MapFrom(Function(su) su.RoleIds.Select(Function(r) New Role() With {.RoleId = r})))
And now we come to the problem, when the SaveChanges() method is called, EF throws a Validation error because the .RoleName property is Nothing.
How does this situation get handled? Are we supposed to manually loop through the Roles and fetch each one from the database? Can we not use mapping tools? Do I give bogus values for the "missing" properties and then loop through and mark them as Unchanged?
I know this was long but I thought the walk-throughs would be helpful...
Thanks.
You can use this algorithm
Start with the root entities.
For each root entity, e.g. a of type A, set a's properties except for navigation properties (at least all the mandatory ones (non-nullables))
Add the As to the context.
Next prepare child entities (entities that must have exactly 1 A) e.g. b of type B.
Set b's properties (except navigations, at least all non-nullables).
For each b, add b to its a (e.g. a.Children.Add(b)).
Continue with child entities of above
...
Save and apply changes
If you have an entity with a non-nullable navigation that already exists in DB and has not yet been accessed via context, you can set the relationship by ID (assuming you've mapped the FK to a property in the model) instead of setting the entity itself.
If your IDs are not store generated, make sure you set them too. If they are, make sure they are defined as store generated in EDMX.
If you have FKs in the DB, make sure the EDMX is aware of them so that the inserts will happen in the correct order (or if using Oracle you can try using deferred constraints instead if you want).
I'm using EF 4.3 with CodeFirst and I have a supertype/subtype model similar to this one:
public Person {
long Id { get; set; }
[Required]
string Name { get; set; }
}
public Costumer : Person {
string SomeData { get; set; }
[Required]
string SomeRequiredData { get; set; }
}
This is a simplified version of the problem. The tables have several fields.
A person can be a "promoted" to Costumer later in the application. So Person is created first and then transformed to Customer.
The question is: After I create Person how can I "promote" it to Vendor without creating (or recreating) a new Person record?
If I do:
var costumer = new Costumer {
Id = [same id used before when Person was created],
SomeRequiredData = "Data"
};
The model gives an error saying that Name is required. I should not be required to repeat all required info from Person in the new Vendor instance since it's already there in the original Person record.
Can anybody help?
P.S. The model is configured to create 2 separate tables, one for each class...
After I create Person how can I "promote" it to Vendor without
creating (or recreating) a new Person record?
You most can't with EF because you cannot change the type of existing instance (you cannot cast Person to Customer). Because you cannot cast the entity you also cannot update its type in the database with EF. In the same time you cannot insert Customer instance with existing Id because this operation expect inserting both parent and child type (remember Customer is a Person in your model and because of that inserting Customer means inserting Person as well).
The reason why it doesn't work is that your domain model is wrong. You should not have Customer as subtype of the Person because in OOP it means exactly what happened now - you cannot change person to customer without creating a new person instance (because customer is a person). To support this scenario you must have only Person entity and this entity must have property describing its type.
Just using this as an example...
Here are the columns in my UserProfile table:
ProfileID (Primary key)
UserID (Foreign key)
Address
PhoneNumber
now, when I want to add a new user to the database using LINQ to Entities, here is what I'm doing:
UserProfile profileToAdd;
profileToAdd.ProfileID = 0;
profileToAdd.Address = "123 MyStreet";
profileToAdd.PhoneNumber = "123-4567";
/* How do I add in the UserID here? */
_myDB.AddToUserProfiles(profileToAdd);
A few questions...
Is there anything special about dealing with Foreign keys that I need to know, or can I assign it just as I did with Address or PhoneNumber?
The UserId is a Guid, and I need to retrieve it from the current user's UserId. I can't seem to get access to Membership class or User class (This is a C# Library so I'm guessing it needs a reference somehow, but my project is already referencing my Library so I can't reference back or I'll have a circular dependancy)
I don't quite understand how to deal with Guids. When implementing getProfileByUserName(string userName), here's my problem...
first off I can't retrieve the UserID, here's what I tried:
Guid currUser = (Guid)from user in _ myDB.aspnet_Users
where user.UserName == userName
select new { user.UserId };
But it says I can't cast it to a Guid for some reason.
If you can provide any insight to any of these questions I would greatly appreciate it!
Thanks,
Matt
If the database contains the proper constraints for the foreign key relationship, there should be a member in your UserProfile class, that points to a User object. The name might be a little weird, such as UserProfileUser or something like that.
However, you can change this name in the diagram. Just set a pointer to the user entity object and the framework will assign the correct id for you.