First off, I am not a DBA, I am a C# developer. I am working on a pretty complex web application and I want to start with a solid database. So here is my issue:
I am trying to create a Settings table that will hold settings for multiple objects. ie: User settings, Season settings, League settings, Team settings.
Instead of creating a table for each of those I would like to keep everything in the Settings table but I can't figure out how to create the foreign key.
My approach is to have three columns in the Settings table: TableName PrimaryKey ID. These three columns would create a composite key that would reference the appropriate object. Is my approach considered bad practice or am I on the right track? Is there a way I can do this that will still work with entity framework?
Here is my way of handling this.
In this diagram, ConfigId is primary key which is used as an FK in Person table as EyeColor, BloodType, PersonType and many more. These columns also hold a Check constraint to ensure no value apart from eye color or corresponding column get stored based on ConfigType. However this comes at a cost of broader datatype which may be smallint or int instead of tinyint.
Only difference is, I am calling these settings as ConfigType in your case {User, Team, Season etc.} and any extension can be done by inheritance concept by creating another table using ConfigId as FK in child table.
If you use inheritance, by default EF will use one table for the entire hierarchy. This is known as Table Per Hierarchy or TPH
public abstract class Setting
{
public string Key { get; set; }
public string Value { get; set; }
}
public class UserSetting : Setting
{
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class SeasonSetting : Setting
{
public int SeasonId { get; set; }
public virtual Season Season { get; set; }
}
Related
UPDATE 09/06/2018
My models looks like this:
public class Activity
{
public int Id { get; set; }
public List<Additive> Additives { get; set; }
}
public class Additive
{
public int Id { get; set; }
public int ActivityId { get; set; }
public Activity Activity { get; set; }
}
Original Question
I'm developing a RESTful API and i want to do it the cleanest way possible.
Currently i'm working on data retrieving. So i got this
If i create a new resource called activity and it has an array of objects, lets say (hardcoded, not representative):
{
"name": "act1",
"objects": [
{ "obj1":"val1" },
{ "obj2":"val2" }]
}
I'll could add or delete objects from my array.
Now, my question is, if i want to access them with an url like this:
Api/activity/1/objects/2
Which is the correct way to tell MySQL that this object is my 2th object of the list?
Or maybe i should select ALL objects of Act1 on the backend and filter them to retrieve just the 2th one.
It's pretty important that resources in your REST service have stable urls. So to figure out what 'second' is, you really need to think about what it means to be second in the list.
Are you ordering by something? If so, it's probably a bad idea to use this structure because adding a new entry can change the order and what was 'second' before might no longer be in the future.
The best thing you can do is add some kind of new id for these entries so accessing a specific resource by uri will always return that resource (or 404, etc).
I finally found the answer i were looking for.
mysql two column primary key with auto-increment
This is a MyISAM engine feature.
For MyISAM and BDB tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.
In my case, this query does the job
CREATE TABLE additive (
id INT NOT NULL AUTO_INCREMENT,
activity_id INT NOT NULL,
other_column VARCHAR(30) NOT NULL,
PRIMARY KEY (activity_id, id)
) ENGINE=MyISAM;
I use EF6 and code first migrations. I have a two tables
public class Team {
public int Id { get; set; }
public string Name { get; set; }
}
public class TeamMember {
public int Id { get; set; }
public string Name { get; set; }
public Team Team { get; set; }
public int TeamId { get; set; }
}
I want to rename TeamMember to TeamMemberDeprecated and add a new tabled named TeamMember with some differences to the table layout. The main reason I am creating a new table is data. I want to save all of the data in the current TeamMember table so I need to rename it, and I want to transfer only the specific data I need to the new TeamMember table.
When I add the migration it looks like this
RenameTable(name: "dbo.TeamMember", newName: "TeamMemberDeprecated");
What it doesn't do is change the name of the foreign key property from FK_dbo.TeamMember_dbo.Team_TeamId to FK_dbo.TeamMemberDeprecated_dbo.Team_TeamId.
This presents a problem when I go to create the new TeamMember table because the foreign key FK_dbo.TeamMember_dbo.Team_TeamId already exists in the database.
I tried dropping the foreign key and renaming it, but this doesn't do anything and when I run the migration -verbose I see why it's expecting the foreign key to be null. I want to keep the data, but perhaps my approach is wrong.
Any suggestions are welcome, thanks.
Let EF rename the table, then go to SQL management studio and manually rename the foreign keys.
Go back to EF, make the changes, run the migration and let EF create the new foreign keys
Don't forget to backup your DB
In EF Core 5.?? there should be a few new commands (see EFCore GitHub Pull):
RenamePrimaryKey
RenameUniqueConstraint
RenameForeignKey
However, it seems that these will drop and recreate the key/constraint. In MSSQL this is not only unnecessary, but very costly. When the constraint is reapplied the database much check that each item is unique or present in the foreign table. If you can help it, do not do this.
Instead I recommend the below. You must specify the schema in the first parameter for the new name, but not in the second. The square brackets will help if any lunatic has put dots in table or constraint names:
migrationBuilder.Sql("sp_rename '[dbo].[PK_TeamMemberDeprecated]', '[PK_TeamMember]'");
It works the same for foreign keys:
migrationBuilder.Sql("sp_rename '[dbo].[FK_TeamMemberDeprecated_TeamID]', '[FK_TeamMember_TeamID]'");
I have pre-existing tables, using a kind of open schema. I have an Item table, and various entities are classified as Items, and then have properties stored in Item property tables. A single entity type may have fields stored in multiple tables. We expose entities with views. So, most entities correspond to a view, and then when we insert/update we have to systematically update the tables or use stored procedures.
I'm trying to determine if NHibernate will gain us anything over our custom-built repositories (which follow a factory pattern). Right now, I'm seeing great difficulty in getting NHibernate to deal with this kind of database schema. The way I see it, we'd either have to completely refactor our database to follow NHibernate's conventions, or completely refactor or entities somehow.
I'm not seeing much in the documentation about how to do this, except for the very simplest of examples that involve databases that more or less follow NHibernate's conventions.
Here's a representative database diagram. We have Episode as an entity that pulls info from Item, IP_Episode, IP_EpisodeBroadcastInfo, IP_Show, etc. to build all the fields that it needs.
You mention conventions. That is a Fluent NHibernate concept, and yes, what you are doing is not exactly in line with Fluent NHibernate's existing conventions. However, it is well within NHibernate's capabilities. NHibernate excels at being able to be mapped to all sorts of different database schemas. Don't feel constrained to the way Fluent NHibernate wants you to go. I'm not saying don't use Fluent NHibernate. If you are consistent and reasonable in your database schema, you can write your own conventions to match.
To illustate NHibernate's flexibility, let's assume we have a table structure similar to this:
create table Episode (
Id int not null primary key,
NumberInSeries int null
);
create table Show (
Episode_id int not null primary key,
Title nvarchar(100) not null,
foreign key (Episode_id) references Episode (Id)
);
create table Broadcast (
Episode_id int not null primary key,
InitialAirDate datetime not null,
foreign key (Episode_id) references Episode (Id)
);
One row in Episode corresponds to zero or one rows in Show and zero or one rows in Broadcast. You could model this type of relationship several different ways in .NET. Here are the various options available to you via NHibernate:
1. Inheritance
public class Episode
{
public virtual int Id { get; set; }
public virtual int? NumberInSeries { get; set; }
}
public class Show : Episode
{
public virtual string Title { get; set; }
}
public class Broadcast : Episode
{
public virtual DateTime InitialAirDate { get; set; }
}
Use this when you want to model a relationship that does not change. If an Episode is a Show, it is always a Show. Also, this modeling would imply that an Episode cannot be both a Show and a Broadcast. I don't believe this is what you want, but you may find it useful elsewhere in your model.
For more info, see...
Official documentation on inheritance mapping
Ayende's blog post on inheritance mapping
2. one-to-one
public class Episode
{
public virtual int Id { get; set; }
public virtual int? NumberInSeries { get; set; }
public virtual Show Show { get; set; }
public virtual Broadcast Broadcast { get; set; }
}
public class Show
{
public virtual Episode Episode { get; set; }
public virtual string Title { get; set; }
}
public class Broadcast
{
public virtual Episode Episode { get; set; }
public virtual DateTime InitialAirDate { get; set; }
}
This gives you more control over which tables actually contain a row associated with a given Episode, because you can set episode.Broadcast = null for example. It's also fine to have both Show and Broadcast information for a given Episode.
For more info, see...
Official documentation on one-to-one
Ayende's blog post on one-to-one
3. join
public class Episode
{
// These properties come from the Episode table...
public virtual int Id { get; set; }
public virtual int? NumberInSeries { get; set; }
// This one comes from the Show table.
public virtual string Title { get; set; }
// This one comes from the Broadcast table.
public virtual DateTime InitialAirDate { get; set; }
}
This is a nice and simple way to represent the data, but you do not get control over whether on not rows are inserted into the Show and Broadcast tables or not.
For more info, see...
Official documentation on join
Ayende's blog post on join
Since you said, "A single entity type may have fields stored in multiple tables", it sounds to me like join should be able to handle the way you currently have things modeled.
Is there any way to set-up a symmetric self-join relationship mapping in NHibernate? Suppose we have two tables:
Users
id
Relations
id
user1
user2
relation_type
The User and Relation classes should look like this:
class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<Relation> Relations { get; set; }
}
class Relation
{
public virtual int Id { get; set; }
public virtual User User1 { get; set; }
public virtual User User2 { get; set; }
// Let's leave the RealationType as string for the sake of brevity
public virtual string RelationType { get; set; }
}
I do NOT want the relations table to have two rows for the same relation. But the relation MUST be symmetric, which means if there's a relation between two users, A and B, the Relations collection of the user A must contain a relation with user B and the relations of user B must contain a relation to A.
It sounds almost like a challenge. But, can someone solve this? Please, if you can, post the xml mapping. I'm not using Fluent.
You can use Key-Many-To-One mapping and remove the Id field from the relation entity. Also you better use inheritance for different relation types.
I doubt it. If you think about the manual SQL query you'd need to write to pull a User & all his Relations out in an outer join query, you can see why NHibernate would struggle to generate something like this. Updates would be an even bigger headache - how do you decide which ids go in which field for a new Relation?
If you're stuck on this model, all I can suggest as a workaround is to map two private collections and implement a Union()ed read-only public collection. Implement update/remove methods that locate & modify the appropriate relation, and a round-robin Add() method. You won't have any NHibernate query support for queries on this collection.
Your other option is to change your data model so that User has a many-to-many relationship to Relation (eg a UserRelation table), rely on application code to enforce a 'two users per relation' rule, and add convenience methods like IList<User> GetRelations(RelationType)
I have a question that I may be over thinking at this point but here goes...
I have 2 classes Users and Groups. Users and groups have a many to many relationship and I was thinking that the join table group_users I wanted to have an IsAuthorized property (because some groups are private -- users will need authorization).
Would you recommend creating a class for the join table as well as the User and Groups table? Currently my classes look like this.
public class Groups
{
public Groups()
{
members = new List<Person>();
}
...
public virtual IList<Person> members { get; set; }
}
public class User
{
public User()
{
groups = new Groups()
}
...
public virtual IList<Groups> groups{ get; set; }
}
My mapping is like the following in both classes (I'm only showing the one in the users mapping but they are very similar):
HasManyToMany<Groups>(x => x.Groups)
.WithTableName("GroupMembers")
.WithParentKeyColumn("UserID")
.WithChildKeyColumn("GroupID")
.Cascade.SaveUpdate();
Should I write a class for the join table that looks like this?
public class GroupMembers
{
public virtual string GroupID { get; set; }
public virtual string PersonID { get; set; }
public virtual bool WaitingForAccept { get; set; }
}
I would really like to be able to adjust the group membership status and I guess I'm trying to think of the best way to go about this.
I generally only like to create classes that represent actual business entities. In this case I don't think 'groupmembers' represents anything of value in your code. To me the ORM should map the database to your business objects. This means that your classes don't have to exactly mirror the database layout.
Also I suspect that by implementing GroupMembers, you will end up with some nasty collections in both your user and group classes. I.E. the group class will have the list of users and also a list of groupmembers which references a user and vice versa for the user class. To me this isn't that clean and will make it harder to maintain and propagate changes to the tables.
I would suggest keeping the join table in the database as you have suggested, and add a List of groups called waitingtoaccept in users and (if it makes sense too) add List of users called waitingtoaccept in groups.
These would then pull their values from your join-table in the database based on the waitingtoaccept flag.
Yes, sure you need another class like UserGroupBridge. Another good side-effect is that you can modify user membership and group members without loading potentially heavy User/Group objects to NHibernate session.
Cheers.