I have an Events table whose goal is to store actions done by web site users. An action basically alters or create a new row in a table X. This will allow me to store an history of all actions done by a user. As such, Events contains:
a primary key column
a text to describe the event (ex: "posted a comment")
a discrimator column if needed
a foreign key column to another table A
a foreign key column to another table B
....
a foreign key column to another table N
A row in the Events table will have only one of the foreign key columns set, all others will be null (so they are all nullable). The table behaves like an indirection table to the actual table concerned by the event. I don't know if a discriminator is needed since all the information is contained in the foreign key columns.
Tables A to N can be anything. Their domain model class can have a common interface if necessary (IEventRecordable).
My question is:
Is a mapping possible between the Events table and an Event class? Is it especially feasible with fluent nhibernate? Can it be done without having to create many derived classes of Event (I don't want to create so many empty subclasses)? The Event class would ideally be as follows:
public class Event
{
public virtual int Id { get; set; }
public virtual IEventRecordable ActualEvent { get; set; }
public virtual string EventDescription { get; set; }
DateTime EventDateTime { get; set; }
}
Many classes among the domain model classes could implement IEventRecordable (which is mainly an empty interface). It could be the User table or a BlogComment table...
Thanks
If you forget about the multiple foreign key columns, you can make this an <any> mapping.
Related
I have multiple projects that return the same OData entities through a API endpoint. Now i want to call all of the projects and store them in my calling projects database with entity framework.
To add them to the db the ID gets overwritten but i want to save the id that the entity has in the projects database as well. so i can still access them if need be and to check if the data isn't already in my database. Because of this i need to add another MainProjectID and projectID column to the entity.
I tried making a new class that has a reference to the entity i want to save but this used new id's for the entities. I also tried inheriting the class but this gave me key conflict issues, and generics don't work either in entity framework(i'm not saying they should). So i'm kinda at a loss right now.
I basically want to save the id as a non-key. Is there any way i can do this without writing entirely new classes and parsing them manually ?
Any help would be greatly appreciated.
We have multiple alternatives here:
In a distributed system, best way to cope with these kinds of ID clashes is to make IDs globally unique. If you can modify how IDs are generated, that would be my choice to go. You can use a UUID (or Microsoft implementation GUID) that will produce a universal unique identifier. Or if that seems like an overkill you can devise a simple mechanism that combines ID with projectID. However you should ensure that the method you will use will not produce any collisions (no two different id-projectId pair will map to same value).
This will ensure that same entity is used throughout your application and no overlaps occur if you try to put records from different sources into the same table. You only need to implement a mechanism to record which ID originated from which source. You can use a reference entity at aggregator for this purpose. You also need to disable auto increment nature of the ID column so that your global unique values are used in table.
You can use different entities for producing and aggregating applications. I don't know your application, but that seems like an OK approach to me since the aggregating application has a different idea about the entity. The aggregating application cares for which application produced the entity, that might make putting the source application identifier into the entry justifiable. Your entities will only differ in that and when you receive the OData object from API you'll need copy all other properties and put project identifier yourself.
You can use the previous solution, but you can use a derived class in order to not to repeat your object properties. This is a better design alternative. However with this method you'll have some problems with the primary key (as you've stated you had). Consider this example
public class Base {
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Name")]
public string Name { get; set; }
}
public class Derived : Base {
[Key]
public int projectId {get; set; }
}
If you don't put [Key] to Derived then you'll have only ID as primary key. When you put [Key] to Derived then you'll have only projectId as primary key. You need to define a composite key and you can do this by removing the [Key] annotation from projectId and using the onModelCreating override of DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Derived>().HasKey(a => new { a.ID, a.projectId })
.Property(c => c.ID).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
modelBuilder.Entity<Derived>().Property(c => c.projectId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
}
You can mix these alternatives. You can drop the primary key on ID field, and then you can insert a new Entity that will model 1-M relationship between ID's and project ID's.
In learning the code-first EntityFramework methodology, I don't understand why you need "duplicate" references between two data sets, i.e. a Navigation Property as well as an explicitly defined foreign key.
For example, an "Enrollment" has a one-to-one relationship with a "Course" and a "Student". In the student's model class, you define a navigation property like so:
public virtual ICollection<Enrollment> Enrollments { get; set; }
This will create a column for foreign keys to Student in the Enrollments table if you do a migration.
But in the "Enrollment" class, you also have a property representing the Student's foreign key like so:
public int StudentID { get; set; }
So my question is: what is the purpose of defining this foreign-key relationship on both ends? I have seen it done where only the Navigation Property is defined on one end, and also where the relationship is only defined on the other end. What is the reason for both?
The navigation property allows you to access the collection of referenced entities when you query the entity.
The single ID property is the reference to the column that actually holds the value of the foreign key reference.
I have a simple model of events that need to be owned by a single use. What I want to do is just store the user's ID in the events table as a foreign key. Then, on lookup, I'll query for all the events that are owned by that user. Simple stuff.
The problem is that I'm new to ASP.NET MVC and the Entity Framework and as such, it seems that it's not that easy. Or rather, there seems to be a more enlightened way to do things and, as I'm trying to piece information together, I'm thinking this is above my current skill level.
Will I be shooting myself in the foot if I go the "easy" way and just store the user's ID as a string? If so, I'd really appreciate pointing me to some recommended articles or videos that describe this concept and how to do it.
I think your question is vague, but I also think I understand what you are asking. It seems like you are asking if you should create the relationship between the user table and the events table or simply just store the user id in the events table without the relationship.
In short order, yes, you will be "shooting yourself in the foot" in multiple ways. First, having foreign keys between related entities enforces integrity into your database. Meaning you can't tie an event to a non-existent user. Second, SQL uses these foreign keys to better index and query your tables. Lastly, you lose a lot of the abilities of Entity Framework such as being able to do the following:
var user = db.Users.First(x=>x.Id == userId);
var userName = user.Name;
var events = user.Events.ToList();
In this example, I only have to reference my DataContext once, yet, can access any properties or related entities to that object. Of course, this works both ways.
var event = db.Events.First(x=>x.Id == eventId);
var eventOwner = event.User;
With that said, this article should give you some really good guidance on doing relationships and code first. Using your example, it should be as easy as the following:
public partial class User{
public User(){
this.Events = new HashSet<Event>();
}
public Guid UserId {get;set;}
public string Name {get;set;}
//...other properties as needed
public virtual ICollection<Event> Events {get;set;}
}
public partial class Event{
public int EventId {get;set;}
public Guid UserId {get;set;}
//...other properties
public virtual User User {get;set;}
}
As for storing something as a string that is actually a GUID, the foreign key will not allow you to map to columns of different data types. Either they both need to be varchar or that both need to be Guid (uniqueidentifier in SQL). Keep with the GUID, better indexing than a random set of characters.
I have two tables named as Profile and ProfileHistory.
Each record in ProfileHistory has to belong to a profile in Profile table, so there is a foreign key relation between two tables. Besides, in ProfileHistory table, there is a column named as ManagerId which also relates to Profile table with foreign key relation.
Profile table structure
Id int primary key
....
....
ProfileHistory table structure
Id int primary key
ProfileId int foreign key to Profile table
ManagerId int foreign key to Profile table
....
My question is:
Since currently I only know this, I am creating my entity model from database.
Model and therefore entity classes are created with navigation properties in
ProfileHistory entity like following:
public virtual Profile Profile { get; set; }
public virtual Profile Profile1 { get; set; }
It is so confusing. Because it is not clear which navigation property for which relation.
Even it is worse if I have more relations between two tables. navigation property names are becoming Profile, Profile1, Profile2, etc.
I was expecting to have the name of the navigation properties related with its foreign key relations.
How can I make my navigation property names something that related to its foreign key relation, in my case "from Profile1 to ProfileManager" ?
Thank in advance for your kind helps.
Muharrem
You can always rename the properties in model diagram. The name can be found in Properties window when you click on a navigation property.
I haven't tested it, but you can map a property to a column using an attribute:
[Column(“BlogDescription", TypeName="ntext")]
public virtual Profile Profile { get; set; }
[Column("Profile1", TypeName="int")]
public virtual Profile ProfileManager { get; set; }
Change the type and the name of the column as it is in the database.
The way I usually solve this is to add properties through partial classes that better represent what I'm after. This way if I need to delete the entity from the diagram and re-add it, I don't lose any renamed columns from the model.
The downside to this is that you need to remember that you cannot use them in Queries because EF won't know how to translate it into a SQL query. But if you've already got your Profile object, it's a lot easier to access myProfile.Manager than myProfile.Profile1.
So, for example, if EF created this for you:
public partial class ProfileHistory
{
public virtual Profile Profile { get; set; }
public virtual Profile Profile1 { get; set; }
}
I would end up creating a partial class like this to re-map the columns:
public partial class ProfileHistory
{
public Profile Manager
{
get
{
return this.Profile1;
}
set
{
this.Profile1 = value;
}
}
}
I did face the same problem some time ago. Well, it is even bigger then just confusing names. If you have navigation properties to another table, like Profile, Profile1, Profile2, next you delete/edit the corresponding foreign keys you may end up having those mixed. And if you used EntitySQL to query data you'll end up having bugs because of incorrect data retrieved/wrong table join conditions...
What I did was changing the t4 template and modified the way properties are generated. When property code text is being written you have the information about association and foreign key related to it. Foreign key names are unique in database and I named those with following pattern
FK_[Table]_[Meaning]
...
FK_ProfileHistory_InitialProfile
FK_ProfileHistory_UpdatedProfile
Next, having this information, I named the properties with the [Meaning] part of the foreign key name.
The following are two partial tables in which I am trying to define a foreign key relationship.
public class Form
{
[Key, Column("FormID")]
public System.Guid FormGUID { get; set; }
[Column("PatGUID")]
public Nullable<System.Guid> PatientGUID { get; set; }
}
public class Patient
{
[Column("PatGUID")]
public System.Guid PatientGUID { get; set; }
[Key, Column("PatID")]
public int PatientID { get; set; }
}
I've eliminated all but the relevant information, fields, navigations, etc. for this example; hopefully not too much.
We have a table Form, with a FK of PatGUID to a Patient table with field PatGUID.
The Patient table has a PatID int KEY field.
We have requirements to rename our fields for our code first entity models; the relevant fields in this example needing changed is PatGUID being changed to PatientGUID.
The difficulty I am having is trying to define this foreign key using either annotations or fluent.
So the end result I need is:
Primary Key Table: Patient, Field: PatGUID (renamed PatientGUID)
Foreign Key Table: Form, Field: PatGUID (renamed PatientGUID)
This doesn’t seem like it should pose a large problem but with the combination of Patient.PatGUID not being the primary key and the PatGUID fields being renamed to PatientGUID has not enabled the WCF Data Service to properly create a reference with a proper reference thus a proper select/join of:
SELECT … FROM [dbo].[Form] AS [Extent1]
INNER JOIN [dbo].[Patient] AS [Extent2] ON [Extent1].[PatGUID] = [Extent2].[PatGUID]
EF doesn't yet support relationships where the principal's key is not the primary key but some other column with a unique key constraint. It is on the feature request list but neither implemented nor on the road map for the next release (EF 6). If it gets implemented at all (in EF 7 maybe) expect to wait a year or more until it's ready for production.
In your particular model EF doesn't recognize any relationship between Form and Patient at all because Patient.PatientID is marked as [Key], not Patient.PatientGUID, and EF treats Form.PatientGUID as an ordinary scalar property, not as an FK to Patient.
In theory you could fake Patient.PatientGUID as the [Key] property in the model although it is not the primary key in the database if you don't create the model from the database or the database from a code-first model, that is, if you map between model and (existing) database manually. But I am not sure if this wouldn't cause subtle problems anywhere else.
The alternative is to write manual join statements in LINQ if you want to fetch Patients and related Forms. You can then join two entities using arbitrary properties, not only key properties. This is, in my opinion, the cleaner and less "tricky" approach. However, the downside is that you won't have navigation properties - references or collections - between Patient and Form and you can't use features like eager loading (Include), lazy loading or comfortable "dotted path syntax" (like Form.Patient.SomePatientProperty, etc.) in your LINQ queries.