With the following database schema:
User: UserID (primary key)
UserInfo: UserInfoID (primary key), UserID (foreignKey), [Other columns with UserInfo data]
User and UserInfo have a 1-1 relationship. I want to map this schema to the following POCOs:
public class User
{
[Key]
public int UserID {get;set;}
public virtual UserInfo userinfo {get;set;}
}
public class UserInfo
{
[Key]
public int UserInfoID {get;set;}
public int UserID {get;set;}
[ForeignKey("UserID")]
public virtual User User {get; set;}
... Other properties of UserInfo...
}
Basically I want to be able to load a Users object, and have the related UserInfo object present as well. This seems like a simple task, but I have not been able to find the right combination of attributes and/or fluent API to accomplish this.
EDIT: I have found that making the UserInfo property in Users an ICollection, then everything is wired up properly. So my question now is, is there a way to avoid using an ICollection, as I know that a User will have either 1 or 0 associated UserInfo records.
For one to one scenarios you have to use the same property as key and fk on both entities.
Try using the UserId property as the Id of UserInfo entity and then like the Fk to the User property.
Related
I'm really confused about this. I just can't find a guide that is designed for noobs or dummies to understand, all i'm getting are advanced or atleast learned technical stuff that i forgot. (not really big into database when i was still learning)
So i posted an image of a rough outline i made, i've been at it for hours but my undecisiveness isnt going anywhere, i double guess myself everytime i feel like im on the right track.
In any case, how should i design the tables?
Say for example there's 3 types of account. 1 is for normal, 2 is for trainers and 3 is for gymowners.
I use the accounts table to get the access level of accounts that are logging in. So i'm really confused about this. I'm pretty sure all tables need to have a primary key. so i decided to each have id's on all table.
Like userid, trainerid, gymid.
So how do i FK them onto the account's table? do i add all 3 as an FK? what if it was a normal user then the trainerid(FK) and gymid(FK) would be empty, is that even acceptable for an FK to be null?
The accounts table also have an accountid(PK) not even sure if it is useful at all or since i only need to check the accesslevel col of the accounts table(i'll probably know when i'm deeply involved in it later for now i just cant see the use of a PK on the accounts table except the fact that you need an PK for every table).
So i'm thinking, should i just use the username as the foreign key? but can normal unique cols be foreign key? or do they need to be set to primary keys?
Also, additional question, in regards to the 3 types of accounts, they all basically have a profile, should i make another table that connects to them named profile?(one for each user type ofcourse like user_profile, trainer_profile, gym_profile).
Maybe one of the problems is that you immediately start to think in tables and IDs and Foreign Keys and so on. It is usually much easier if you think in objects and their relation towards each other.
It is a bit difficult to read your picture. From the text I gather you have the notion of an account, and apparently there are three types of accounts: normal, trainers and gym owners. Is there a difference between those accounts? Do gym owners have properties that normal users don't have? or is it just some kind of identification.
From your drawing it seems that a normal user has a user name, a password and an access level. Do trainers also have these properties?
If so, then in normal object oriented design, you would say it is an aggregation: trainers, normal people and gym owners all HAVE a user name, a password and an access level. aggregation is also quite often called composition. There is a small difference, but for this discussion it is not important.
Instead of aggregation/composition (the object HAs an account) you could also think of inheritance: the object IS an account: trainer accounts, gym owner accounts and normal accounts are objects that are all different kinds of Accounts, with a user name, a password and an access level.
In this example there is not a big difference between aggregation (a trainer has an account), or inheritance (a trainer account is an account). Usually the advise is to favor aggregation over inheritance enter link description here
Be aware: if the only difference between your three accounts is a value of a property, then consider making them all the same type, with a property AccountType which gives you information on whether it is a normal account, a gym owners account or a trainer account.
If the only difference between those three is the access level, then don't create an account type. The access level would serve as account type.
So far, I'm only talking object oriented design. Once you've designed your objects you might think of putting them in a database.
Let's assume a trainer, a gym owner and normal people really are different things, with different properties. Your class would look similar to:
public enum AccessLevel
{
None,
...,
FullAccess,
}
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public .. some trainer properties {get; set;}
// a trainer has an account
public Account Account {get; set;}
}
public class GymOwner
{
public ... some gym owner properties {get; set;}
public Account Account {get; set;}
}
If your design would be like this, you'd see that at least you'd have a gym owner table and a trainer table. But what to do with the Account.
One solution would be to put an account in a separate table and add something to the trainer and the gym owner, so that if you have a trainer you know which item in the account table belongs to this specific trainer.
This method is usually not used in this design. If object A and object B have a one to one relation: one A belongs to one B, and one B belongs to one A, then in fact they can be put into one table. If you use entity framework and you had defined the classes above, the account properties would be added as columns to the trainer table and as columns to the gym owner table. The advantage is that fetching the information about a trainer is faster, as this would involve accessing only one table.
About Primary Keys. Every element in every table should have exactly one primary key. This is the property that identifies the object. If you have the key, you have very fast access to all other properties. To make it easier for you to understand the role of Account I've left out the Id in my original code.
But now that we've decided that it would be best to let Trainers and Gym owners HAVE an account, there would be two tabled: Trainers and GymOwners. Those are the only elements that have a primary key. The classes would be like:
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public Account Account {get; set;}
}
public class GymOwner
{
public int Id {get; set;}
public ... some gym owner properties {get; set;}
public Account Account {get; set;}
}
Note that there is no table for Account, so Account does not need a key.
So when do you need a foreign key
If a Trainer would not have one account, but several accounts, maybe a lot of accounts, usually called a collection of accounts, then we can't save the accounts as columns inside the Trainer table anymore. We will have to put all accounts of the Trainer in a separate table and tell each account to which trainer it belongs. The account gets a foreign key to the primary key of the trainer to whom he belongs.
In database terms, this is called a one-to-many relation: one trainer has many accounts.
For entity framework the classes would be like:
public class Account
{
public int Id {Get; set;}
public int TrainerId {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public virtual ICollection<Account> Accounts {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Trainer> Trainers {get; set;}
public DbSet<Account> Accounts {get; set;}
}
Note that because Account has its own table it has gotten a primary key.
Beside it has also gotten a foreign key in property TrainerId.
I've also added a class derived from DbContext to access the tables. Until know I have only tables with Trainers and tables with Accounts. Each table is called a DbSet, the type of DbSet informs entity framework about the columns in the table.
Because Trainer has a virtual ICollection of Accounts, entity framework knows that there is a one-to-many relation between Trainer and Account, and because of the namer TrainerId, entity framework knows that TrainerId is the foreign key to the primary key of the Trainer that the account belongs to.
So if you have the id of a trainer, you get all accounts of this trainer using the following Linq statements:
int trainerId = GetMyTrainerId();
IEnumerable<Account> accountsOfTrainer = dbContext.Accounts
.Where(account => account.TrainerId == trainerId);
From the collection of Accounts, take all records where property TrainerId equals trainerId
So now you know how to design a primary key and let a foreign key point to it.
But what about gym owners? If a gym owner only has one account, just let it HAVE the one account (composition). But what if your gym owner also has a collection of accounts.
If you just add the gym owner accounts to the accounts table, then you get in trouble. To which Id does the foreign key point? To primary key in the Trainer table or in the Gym Owners table?
The safest way would be to create GymOwnersAccount and a TrainersAccount. They will each have their own table with a foreign key. A GymOwnersAccount will have a foreign key to the GymOwners table and a TrainersAccount will have a foreign key to the trainers table.
Here you could also decide to let the GymOwners account HAVE an account, but it seems more natural to say the a GymOwners account IS a special type of Account, and thus derives from Account.
public class Account
{
public string UserName {get; set;}
public string Password {get; set;}
public AccessLevel AccessLevel {get; set;}
}
public class GymOwnerAccount : Account
{
public int Id {get; set;}
public int GymOwnerId {get; set;}
}
public class TrainerAccount : Account
{
public int Id {get; set;}
public int TrainerId {get; set;}
}
public class Trainer
{
public int Id {get; set;}
public .. some trainer properties {get; set;}
// a trainer has an account
public virtual ICollection<Account> Accounts {get; set;}
}
public class GymOwner
{
public int Id {get; set;}
public ... some gym owner properties {get; set;}
public virtual ICollection<Account> Accounts {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Trainer> Trainers {get; set;}
public DbSet<Account> GymOwnerAccounts {get; set;}
public DbSet<Account> TrainerAccounts {get; set;}
}
There are other solutions possible, for instance give each account two foreign keys, one to gym owners and one to trainers, where you always need to set one foreign key to 0. It's easy to see that this might lead to maintenance problems, while it doesn't give you any benefits, so I would advice to stick with separate tables for GymOwnerAccounts and TrainerAccounts.
Now that I've entered the realm of inheritance in database there is a wealth of items you can configure. An article that helped me a lot to understand entity framework, and how classes, inheritance, composition is transferred to databases is Entity Framework Code First
I want to display a list of users in an XHTML page. I'm sending a request from my managedBean to the Business through an EJB (3.0) then another request to the DAO still through an EJB (3.0). I'm using JPA 2 and a MySQL database with an entity manager.
I'm sending the following request to my database
#Remote(IDaoUser.class)
#Stateless
Public class DaoUser implements IDaoUser
#PersitenceContext(unitName = "persistence_unit")
Private EntityManager em;
#Override
Public List<User> getAll() {
Query query = em.createQuery("SELECT u FROM User u");
List<User> users = query.getResultList();
return users;
}
At that point everything's fine and I get all my users in my list with all attributes especially id (primary key). My user class has one attribute (birthdate) and inherits from a super class Parent (name, forename, mail, phone...) which inherits again from another class called Who which has just one attribute called id (the primary key).
Then I return my List (users) to the business through an EJB and once in the business I realise all the id attributes are 0. I kept all the others attributes (name, forename...) except ids and I can't figure out why I'm losing these values.
There are well stored in my List in the DAO but all changed to 0 once in the business.
Here is my business class which is very simple
#Remote(IBusinessUser.class)
#Stateless
Public class BusinessUser implements IBusinessUser
#EJB
private IDaoUser proxy;
#Override
Public List<User> getAll() {
List<User> users = proxy.getAll();
return users;
}
Given the description of the problem, I would ask some questions
Are the parent classes entities themselves, i.e. are they annotated with #Entity.
You need to ensure that #Id is on your primary key. Do you have the #Id annotation on the primary key.
Experience has taught me to always have the #Id attribute in class or at least in a parent class tagged with the #MappedSuperclass. And some people still have problems with retrieving their id fields from the mapped super class.
So see the JEE documentation for using the MappedSuperclass annotation. This may require changing your object inheritance model but that shouldn't be a problem given your requirement.
Thanks for your help. Actually both parent classes are themselves entities. My super class "Who" is the only one having an id attribute (primary key) with the annotation #Id. I can't tag my super class with the #MappedSuperclass since it is associated to another class.
I am using Entity Framework and have a connection to a MySQL database. The id column is set to use StoreGeneratedPattern Identity and the column in the database has been set to auto-increment. When I create a new object and save it to the database, the item posts correctly in the database. However, after saving, the id of the object in C# remains 0 rather than reflecting the value than was assigned by the database.
The section of code is given below:
Group newGroup = new Group("MyGroupName", "Active");
dbContext.Groups.Add(newGroup);
dbContext.SaveChanges();
int testId = newGroup.id;
Even though "newGroup" saves in the database with a database-assigned id, when I read the id (such as I do when reading testId) the id is still 0.
Based on this, I have tried adding
dbContext.Entry(newGroup).Reload();
after SaveChanges() and I have also tried (based on this and this) adding
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
objectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, newGroup);
after SaveChanges() in an attempt to refresh the object (and thus the id) from the database, yet the problem remains. How can I get the id that was assigned by the database?
EDIT: Adding class definition for Group:
[Table("groups")]
public partial class Group
{
public Group()
{
this.user_groups = new HashSet<UserGroup>();
}
public long id { get; set; }
public string name { get; set; }
public string status { get; set; }
public System.DateTime created_at { get; set; }
public System.DateTime updated_at { get; set; }
public virtual ICollection<UserGroup> user_groups { get; set; }
}
Try decorating your id with the [Key] attribute.
It SHOULD be this attribute
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
However, this SHOULD be the default.
The [Key] attribute, should be unnecessary since the column name Id is magical... Although this might only be the case when using the accepted naming convention for C#.
I wonder if it might be the long that your id property is typed, or possibly the naming convention... you could try naming it Id.
I'm having the same problem with my project. What I did for a work around was to order the table (group) by the ID descending and select the first or default record, then select the ID column.
var newID = dbcontext.Groups.OrderByDescending(x => x.id).FirstOrDefault().ID
You can then assign that to whatever you need and save changes again. I know it's an old thread but hopefully this helps. Seems like there should be a better way to do it...
I encountered a strange situation.
I have a root entity (table) with refereance to another entity (view)
public class RootEntity
{
public int Id {get; set;}
public int SubEntityId {get; set;}
public SubEntity SubEntity {get; set;}
}
public class SubEntity
{
public int Id {get; set;}
}
When I set only the RootEntity.SubEntityId with existing SubEntityId All goes well.
But, when I set the Ref to as follow
RootEntity.SubEntity = attachedSubEntity
For whatever reason EF is trying to insert the attached SubEntity to the view and I get this
System.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column '****', table '****'; column does not allow nulls. INSERT fails.
I found the problem! SubEntity was fetched with AsNoTracking option.
I thought that using this option will solve the "Insert" problem but in fact he was the cause!
When I removed the AsNoTracking addition all goes well.
It's still weird because the problem occurred only when used UnitTesting (Nunit). But with WCF I not encountered the problem
I have got this weird error Cannot add an entity with a key that is already in use
But what is quite irritable about that error is that user gets no detais - Who? What? What table? What record is the culprit of this error?
It would be desperately complicated to determine it, in case you do many operations on LINQ objects before .Submit()
Is there any way to determine what certainly caused this error?
This error typically happens when you are creating a new record in a MetaTable with a foreign key relationship and the foreign key record already exists.
For example, let's say you have an Contact table and an Address table, and each Contact can hold multiple Addresses. The error occurs when you create a new Contact record and try to manually associate an existing Address record to that new Contact.
Assuming that the passed Address ID represents an existing Address record, this doesn't work:
public class Contact
{
public int Contact_ID { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public string Phone { get; set; }
}
public class Address
{
public int Address_ID { get; set; }
public string Street { get; set; }
public string CityState { get; set; }
public string ZIP { get; set; }
}
public void CreateNewContact(int addressID)
{
Contact contact = new Contact();
contact.Name = "Joe Blough";
contact.ContactAddress.Address_ID = addressID;
contact.Phone = "(555) 123-4567";
DataContact.SubmitChanges();
}
Historically, SQL developers are trained to just pass the ID value in order for the magic to happen. With LINQ-to-SQL, because the database activity is abstracted, we have to pass the whole object so that the LINQ engine can properly reflect the necessary changes in the ChangeSet. In the above example, the LINQ engine assumes that you are asking to create a new Address record, because it didn't have one to work with when the SubmitChanges was made and it has to respect the contract established by the foreign key relationship. It creates a blank Address record with the passed ID value. The error occurs because that ID value already exists in the data table and the ChangeSet has not flagged the Address delta as an Update.
The fix is to pass in the entire record, not just the ID value:
contact.ContactAddress = DataContext.Addresses.Where(a => a.Address_ID == addressID).Single();
Now, the LINQ engine can properly flag the incoming Address record as an existing one and not try to recreate it.
May be the column you trying to Attach(), Remove(), Add() or DeleteOnSubmit(), is a primary key and you are trying to add or attach the same value again.
Also you might be accessing a primary key or foreign key value column in a different method and it's not closed yet when you trying to call the above methods.
Above to these methods Attach(), Remove(), Add() or DeleteOnSubmit(), try to create a new instance of your datacontext again and run.
It sounds like you are doing an Table.Attach() and the entity you are attaching has a key value that L2S is already tracking. This has got nothing to do with a duplicate key in your physical database.
As explained on one of the answers above, this error is more likely due to trying to insert a record into the table with a repeated value on primary ID key field. You could solve the problem by selecting/creating a different primary key.