Dynamic foreign key based on field value on another table - c#

I have a database with many tables. Two of them are like this :
Table A
{
id int,
fkTable int,
fkClass int
}
Table B
{
id int,
reference varchar(100)
}
The A.fkTable field is a foreign key on another table, without particular constrains. We can only know on which table the relation is by B.reference field. The A.fkClass field point to the B.id field.
For example, if B.reference = "Operations", we can determinate that A.fkTable will point to Operations.id. We talk about 20 possibilities for B.reference value.
I know it's possible for Entity Framework to retrieve an specific entity by a field value, but I know only the way by a field in the same table.
Is there a solution in order to avoid manual joining in the code and EF retrieve the good mapped-object in function of B.reference value ? I think I need to declare manually my 20 entities in order to explain to EF the relationship between fkClass and the good table, but i'm not sure how coding the switch case.
And I don't want to do two queries : one in order to get the reference, and another in order to join fkTable to the good entity.
Edit : may can I use HasDiscriminator ?
Edit2 : I can't modify the database. I can only modify my C# project which connects to the database. It's not code-first. It's model-first.

Related

Implement a "One-to-many" relationship with DataSets

I have two tables, one containing patient information, the other, the notes for each patient.
(One patient, many notes for a patient).
Given this, in the Designer (which you access by right-clicking on the chosen DataSet), how do I create a one-to-many relationship? I have never performed this before.
Secondly, for the patient notes table, how would I add a note to a patient record using SQL syntax? Note, this is not updating an existing one, but adding a completely new one to the patientNotes table using the unique patient ID number as the reference (so only that specific patient has that note added to them, not them and everyone else).
Very technically speaking, you don't need to do anything to create a one-to-many relationship. You just have to have the two tables set up as you have them and use them as you intend on using them. I work in data warehousing and unfortunately a great many of our relationships like this are not formalized with any sort of key or constraint.
The correct way to do it is to implement a foreign key constraint on the patient ID column on the patientNotes table. A FK will only allow you to insert data into patientNotes IF the patient ID exists in the patient table. If you would try to insert a note into your table that has a patient ID that doesn't exist in the patient table, the insert would fail and the SQL engine would give you an error. Note that the column on the patients table that you are creating the FK to must be a primary key.
Inserting data will really go as any other insert would:
INSERT INTO dbo.patientNotes (patientId, NoteText)
VALUES(4265, 'During his 8/14/2014 visit, Mr. Cottinsworth complained of chest pains. Evidently he has been wearing a lady''s corset to hide his large gut. Advised the very portly Mr. Cottinsworth to discontinue corset use'
You could toss that in a SP, put it in your code and use parameters for the patientId and NoteText, however you wanted to do it.
As far as doing this all in Visual Studio graphically, I can't be of much help there. I typically use the TSQL editor and type out what I want to do to the DB. I'm sure there are tutorials abound on how to set up FKs on Visual Studio.
Further reading:
http://msdn.microsoft.com/en-us/library/ms189049.aspx
http://www.scarydba.com/2010/11/22/do-foreign-key-constraints-help-performance/
what are the advantages of defining a foreign key

Navigation properties when datatypes does not match in Entity Framework

I am trying to map a legacy database into an Entity Framework Model. The database is very generic, and most data are stored in the tables "Object" and "Event". The columns are named things like "Date1", "Num11", "Text4". There are no explicit foreign keys defined in the database.
Here is a subset of the two tables:
CREATE TABLE [Object] (
[ObjectId] int not null identity(1,1) primary key,
[ObjectTypeId] int,
[Name] varchar(100)
);
CREATE TABLE [Event] (
[EventId] int not null identity(1,1) primary key,
[EventTypeId] int,
[Subject] text,
[Body] text,
[Date1] datetime,
[Num11] decimal(18,2)
);
For some values of EventTypeID, the Num11 field references an Object. I can easily write a join between the tables:
SELECT
ev.[EventId], ev.[Subject], ev.[Body], ev.[Date1] AS [CreatedDate],
p.[ObjectId] AS [PersonId], p.[Name] AS [PersonName]
FROM [Event] ev
LEFT JOIN [Object] p ON p.ObjectId = ev.Num11
WHERE ev.[EventTypeId] = 7
AND ev.[Date1] > '2013-04-07'
In the Entity Framework designer, I can create a separate Entities for each type of object, and rename the columns appropriately. The problems start when I try to create navigation-properties between the Entities, since the foreign key column type does not always match the primary key.
Neither SQL Server, nor Entity Framework, will allow me to create a foreign key reference between the columns.
How can I create navigation-properties between entities, when the FK an and PK datatypes does not match exactly? Something that enables me to include the related entity in a LINQ query, and hopefully be able to expose it in a OData service.
I can't make any changes to existing tables in the database, but if required, I could add views. Though I would need to be able to save the entities back to the database.
Not a pleasant design, but there are some options left. This is what you can do:
Create a 1:1 view on Event with an additional column that converts the decimal to an integer. The property should be marked as computed.
Use the property in a FK association with Object, so Object has a navigation property EventViewItems (if you like) that maps to the view. (You must add the association manually in the edmx designer and tweak the foreign key field).
Read the objects and events in one linq statement like db.Objects.Include(o => o.EventViewItems)
But you can't
Add events to db.Objects.EventViewItems, because you can't write into the FK field.
Add events to db.EventViewItems because there is no InsertFunction defined for a view. (Unless you hack the view into the model as a table).
So you'll have to include the original Event in the model too and use that to create/update/delete (CUD) individual Event objects.
Feels a but shaky, because you have to watch your steps not to run into runtime exceptions. On the other hand you'll have separate paths for reads and CUD. Call it CQRS and this design suddenly is cutting edge.
You can try this. Change the type of Num11 in the model to integer. In the efconfiguration of Event set the databasetyp of num11 to int with xx.HasColumnType("int").

C# Entity Framework: Update Query Does not work

I have this simple code : (update value)
I'm trying to update column "c"
using (MaxEntities ctx = new MaxEntities())
{
aa orders = (from order in ctx.aa
select order).First();
orders.c = 22;
ctx.SaveChanges();
}
this is the table :
CREATE TABLE [dbo].[aa](
[a] [int] NULL,
[b] [int] NOT NULL,
[c] [int] NOT NULL
) ON [PRIMARY]
and values inside :
but i get an exception :
The property 'c' is part of the object's key information and cannot be modified.
I'm new to EF.
any help will be much appreciated.
The property 'c' is part of the object's key information and cannot be modified.
That's why you can't edit it. Maybe you need to add id column as a key with identity specified
As explained in another answer EF must uniquely identify every entity. If you don't have PK in the database, EF will infer some key. Key is considered as fixed so if EF inferred c as part of the key (and it did it because it uses all non-nullable non-binary columns) you cannot change its value. Moreover EF takes all tables without primary key as readonly so even if you remove c from the key in the designer and modify c value you will get another exception when you execute SaveChanges.
The reason for the second exception is in the way how EF describes model and the database. When EF inferred key, it did it only for description of your entities and for context's internal needs but not for description of the database. When EF tries to save changes it builds UPDATE statement from database description and without information about real database PK columns it will not be able to identify correct record for update (every update in EF can affect only single record - EF checks ROWCOUNT). This can be solved by cheating EF and updating its database description = by describing some column in the table description as primary key. This leads to multiple problems:
You must have some unique column in the database otherwise this method will not work.
You must edit EDMX manually (as XML) to add this change
You must not use default MS EDMX designer for updating your model from database because it will delete your change
Simple advice: Either use database tables with primary keys or don't use Entity framework.
Primary key missing here. Add primary key in table and it work.
I believe if there's no PK at all, EF uses all of the fields/columns as part of the key info.Here's a nice explanation: by #SteveWilkes of why. But what do your entities look like? The other possibility is that it doesn't have a property because the association is inside a different entity, if this is a foreign key.
EDIT
This got me thinking. There are just going to be situations where you have to work with legacy tables having no PK, even if you would never create such a thing. What about views? EF is a mapper - it has to uniquely identify that record so it infers and defines this key. Yes, you could use stored procedures, but could you also hack the XML and remove the keys from the table definition?
AND EDIT AGAIN
After posting this, I see #Ladislav Mrnka already said a similar idea (cheating EF and updating its database description), so it has been done (WARNING: Consume at your own risk - never tried). Quick google got me this blog with clear instructions:
Close the model designer in Visual Studio if it is still open and re-open the .edmx file in an XML editor
Find the edmx:StorageModels -> Schema -> Entity Container -> EntitySet element that refers to the table in question
On the EntitySet element, rename the store:Schema attribute to Schema
Remove the store:Name attribute altogether
Remove the opening and closing DefiningQuery tags and everything in between them
Save and close the .edmx file
But really, who doesn't like a PK? Can you not add an id?

How can I Insert/Update into two related tables in one command?

A database exists with two tables
Data_t : DataID Primary Key that is
Identity 1,1. Also has another field
'LEFT' TINYINT
Data_Link_t : DataID PK and FK where
DataID MUST exist in Data_t. Also has another field 'RIGHT' SMALLINT
Coming from a microsoft access environment into C# and sql server I'm looking for a good method of importing a record into this relationship.
The record contains information that belongs on both sides of this join (Possibly inserting/updating upwards 5000 records at once). Bonus to process the entire batch in some kind of LINQ list type command but even if this is done record by record the key goal is that BOTH sides of this record should be processed in the same step.
There are countless approaches and I'm looking at too many to determine which way I should go so I thought faster to ask the general public. Is LINQ an option for inserting/updating a big list like this with LINQ to SQL? Should I go record by record? What approach should I use to add a record to normalized tables that when joined create the full record?
Sounds like a case where I'd write a small stored proc and call that from C# - e.g. as a function on my Linq-to-SQL data context object.
Something like:
CREATE PROCEDURE dbo.InsertData(#Left TINYINT, #Right SMALLINT)
AS BEGIN
DECLARE #DataID INT
INSERT INTO dbo.Data_t(Left) VALUES(#Left)
SELECT #DataID = SCOPE_IDENTITY();
INSERT INTO dbo.Data_Link_T(DataID, Right) VALUES(#DataID, #Right)
END
If you import that into your data context, you could call this something like:
using(YourDataContext ctx = new YourDataContext)
{
foreach(YourObjectType obj in YourListOfObjects)
{
ctx.InsertData(obj.Left, obj.Right)
}
}
and let the stored proc handle all the rest (all the details, like determining and using the IDENTITY from the first table in the second one) for you.
I have never tried it myself, but you might be able to do exactly what you are asking for by creating an updateable view and then inserting records into the view.
UPDATE
I just tried it, and it doesn't look like it will work.
Msg 4405, Level 16, State 1, Line 1
View or function 'Data_t_and_Data_Link_t' is not updatable because the modification affects multiple base tables.
I guess this is just one more thing for all the Relational Database Theory purists to hate about SQL Server.
ANOTHER UPDATE
Further research has found a way to do it. It can be done with a view and an "instead of" trigger.
create table Data_t
(
DataID int not null identity primary key,
[LEFT] tinyint,
)
GO
create table Data_Link_t
(
DataID int not null primary key foreign key references Data_T (DataID),
[RIGHT] smallint,
)
GO
create view Data_t_and_Data_Link_t
as
select
d.DataID,
d.[LEFT],
dl.[RIGHT]
from
Data_t d
inner join Data_Link_t dl on dl.DataID = d.DataID
GO
create trigger trgInsData_t_and_Data_Link_t on Data_t_and_Data_Link_T
instead of insert
as
insert into Data_t ([LEFT]) select [LEFT] from inserted
insert into Data_Link_t (DataID, [RIGHT]) select ##IDENTITY, [RIGHT] from inserted
go
insert into Data_t_and_Data_Link_t ([LEFT],[RIGHT]) values (1, 2)

Interesting LinqToSql behaviour

We have a database table that stores the location of some wave files plus related meta data. There is a foreign key (employeeid) on the table that links to an employee table. However not all wav files relate to an employee, for these records employeeid is null. We are using LinqToSQl to access the database, the query to pull out all non employee related wav file records is as follows:
var results = from Wavs in db.WaveFiles
where Wavs.employeeid == null;
Except this returns no records, despite the fact that there are records where employeeid is null. On profiling sql server i discovered the reason no records are returned is because LinqToSQl is turning it into SQL that looks very much like:
SELECT Field1, Field2 //etc
FROM WaveFiles
WHERE 1=0
Obviously this returns no rows. However if I go into the DBML designer and remove the association and save. All of a sudden the exact same LINQ query turns into
SELECT Field1, Field2 //etc
FROM WaveFiles
WHERE EmployeeID IS NULL
I.e. if there is an association then LinqToSql assumes that all records have a value for the foreign key (even though it is nullable and the property appears as a nullable int on the WaveFile entity) and as such deliverately constructs a where clause that will return no records.
Does anyone know if there is a way to keep the association in LinqToSQL but stop this behaviour. A workaround i can think of quickly is to have a calculated field called IsSystemFile and set it to 1 if employeeid is null and 0 otherwise. However this seems like a bit of a hack to work around strange behaviour of LinqToSQl and i would rather do something in the DBML file or define something on the foreign key constraint that will prevent this behaviour.
I think you should double-check your dbml file. Sounds like Linq doesn't know that employeeid is a nullable column. Or look at your .cs file. The attributes for this column should look like this:
[Column(Storage="_employeeid", DbType="Int")]
and not:
[Column(Storage="_employeeid", DbType="Int NOT NULL")]
try this:
var results = from Wavs in db.WaveFiles
where DbNull.Value.Equals(Wavs.employeeid)
another way and good practe a nice is to introduce a default employee where every wave file is associated to, that isnĀ“t associated to a real employee
The column is defined as:
[Column(Storage="_employeeid", DbType="Int")]
The way round it whilst leaving the association was to do a left join from the employee entity collection.

Categories