EF Frustration mapping a view to an object - c#

I have a table that hold records of part numbers for a specific project like so:
create table ProjectParts
(
PartNumber varchar(20) not null,
ProjectID int not null,
Description varchar(max) not null,
primary key nonclustered (PartNumber, ProjectID)
);
I have a view that will collect inventory information from multiple places, but for now I basically have a skeleton:
create view ProjectQuantities as
select distinct
pp.PartNumber,
pp.ProjectID,
0 as QtyOnHand,
0 as QtyOnOrder,
0 as QtyCommitted
from
ProjectParts pp;
So far, so good. I go into EF designer in Visual Studio (I already had an object model using the ProjectParts table) and update the model from the database. I select the ProjectQuantities view, click ok.
EF tries to divine the key on the table as a combination of all columns, but I fix that so the key for the object is the PartNumber and ProjectID columns. I check to make sure this validates, and it does.
Next, I add an 1:1 association between the ProjectPart object and the ProjectQuantity object in the EF UI and click OK. Now, when I try validating, I get the message Error 11008: Association 'ProjectQuantityProjectPart' is not mapped. Seriously? It can't figure this out? Alright, I select the link, go to the Mapping Details, and add the ProjectParts table. It adds both tables and meshes up the key relationships. My job is done. I run the validation.
No luck for me. Now I get the error Error 3021: Problem in mapping fragments starting at line (line number): Each of the following columnes in table ProjectParts is mapped to multiple conceptual side properties. The the message lists the ProjectID and the PartNumber columns and their references to the association I just created.
Well duh! Of course there are multiple references! it's a 1:1 compound key, it has to have multiple references!
This is stopping me from getting stuff done. Does anyone know a simple way to fix this so I can collect Quantity information when I'm collecting data about a project and its parts?
Thanks!

You may find this article useful http://blogs.u2u.be/diederik/post/2011/01/31/Building-an-Entity-Framework-40-model-on-views-practical-tips.aspx

Related

Introducing FOREIGN KEY constraint 'FK_CourseStudent_Students_StudentId' on table 'CourseStudent' may cause cycles or multiple cascade paths - why? [duplicate]

I have a problem when I try to add constraints to my tables. I get the error:
Introducing FOREIGN KEY constraint 'FK74988DB24B3C886' on table 'Employee' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
My constraint is between a Code table and an employee table. The Code table contains Id, Name, FriendlyName, Type and a Value. The employee has a number of fields that reference codes, so that there can be a reference for each type of code.
I need for the fields to be set to null if the code that is referenced is deleted.
Any ideas how I can do this?
SQL Server does simple counting of cascade paths and, rather than trying to work out whether any cycles actually exist, it assumes the worst and refuses to create the referential actions (CASCADE): you can and should still create the constraints without the referential actions. If you can't alter your design (or doing so would compromise things) then you should consider using triggers as a last resort.
FWIW resolving cascade paths is a complex problem. Other SQL products will simply ignore the problem and allow you to create cycles, in which case it will be a race to see which will overwrite the value last, probably to the ignorance of the designer (e.g. ACE/Jet does this). I understand some SQL products will attempt to resolve simple cases. Fact remains, SQL Server doesn't even try, plays it ultra safe by disallowing more than one path and at least it tells you so.
Microsoft themselves advises the use of triggers instead of FK constraints.
A typical situation with multiple cascasing paths will be this:
A master table with two details, let's say "Master" and "Detail1" and "Detail2". Both details are cascade delete. So far no problems. But what if both details have a one-to-many-relation with some other table (say "SomeOtherTable"). SomeOtherTable has a Detail1ID-column AND a Detail2ID-column.
Master { ID, masterfields }
Detail1 { ID, MasterID, detail1fields }
Detail2 { ID, MasterID, detail2fields }
SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields }
In other words: some of the records in SomeOtherTable are linked with Detail1-records and some of the records in SomeOtherTable are linked with Detail2 records. Even if it is guaranteed that SomeOtherTable-records never belong to both Details, it is now impossible to make SomeOhterTable's records cascade delete for both details, because there are multiple cascading paths from Master to SomeOtherTable (one via Detail1 and one via Detail2).
Now you may already have understood this. Here is a possible solution:
Master { ID, masterfields }
DetailMain { ID, MasterID }
Detail1 { DetailMainID, detail1fields }
Detail2 { DetailMainID, detail2fields }
SomeOtherTable {ID, DetailMainID, someothertablefields }
All ID fields are key-fields and auto-increment. The crux lies in the DetailMainId fields of the Detail tables. These fields are both key and referential contraint. It is now possible to cascade delete everything by only deleting master-records. The downside is that for each detail1-record AND for each detail2 record, there must also be a DetailMain-record (which is actually created first to get the correct and unique id).
I would point out that (functionally) there's a BIG difference between cycles and/or multiple paths in the SCHEMA and the DATA. While cycles and perhaps multipaths in the DATA could certainly complicated processing and cause performance problems (cost of "properly" handling), the cost of these characteristics in the schema should be close to zero.
Since most apparent cycles in RDBs occur in hierarchical structures (org chart, part, subpart, etc.) it is unfortunate that SQL Server assumes the worst; i.e., schema cycle == data cycle. In fact, if you're using RI constraints you can't actually build a cycle in the data!
I suspect the multipath problem is similar; i.e., multiple paths in the schema don't necessarily imply multiple paths in the data, but I have less experience with the multipath problem.
Of course if SQL Server did allow cycles it'd still be subject to a depth of 32, but that's probably adequate for most cases. (Too bad that's not a database setting however!)
"Instead of Delete" triggers don't work either. The second time a table is visited, the trigger is ignored. So, if you really want to simulate a cascade you'll have to use stored procedures in the presence of cycles. The Instead-of-Delete-Trigger would work for multipath cases however.
Celko suggests a "better" way to represent hierarchies that doesn't introduce cycles, but there are tradeoffs.
There is an article available in which explains how to perform multiple deletion paths using triggers. Maybe this is useful for complex scenarios.
http://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/
By the sounds of it you have an OnDelete/OnUpdate action on one of your existing Foreign Keys, that will modify your codes table.
So by creating this Foreign Key, you'd be creating a cyclic problem,
E.g. Updating Employees, causes Codes to changed by an On Update Action, causes Employees to be changed by an On Update Action... etc...
If you post your Table Definitions for both tables, & your Foreign Key/constraint definitions we should be able to tell you where the problem is...
This is because Emplyee might have Collection of other entity say Qualifications and Qualification might have some other collection Universities
e.g.
public class Employee{
public virtual ICollection<Qualification> Qualifications {get;set;}
}
public class Qualification{
public Employee Employee {get;set;}
public virtual ICollection<University> Universities {get;set;}
}
public class University{
public Qualification Qualification {get;set;}
}
On DataContext it could be like below
protected override void OnModelCreating(DbModelBuilder modelBuilder){
modelBuilder.Entity<Qualification>().HasRequired(x=> x.Employee).WithMany(e => e.Qualifications);
modelBuilder.Entity<University>.HasRequired(x => x.Qualification).WithMany(e => e.Universities);
}
in this case there is chain from Employee to Qualification and From Qualification to Universities. So it was throwing same exception to me.
It worked for me when I changed
modelBuilder.Entity<Qualification>().**HasRequired**(x=> x.Employee).WithMany(e => e.Qualifications);
To
modelBuilder.Entity<Qualification>().**HasOptional**(x=> x.Employee).WithMany(e => e.Qualifications);
Trigger is solution for this problem:
IF OBJECT_ID('dbo.fktest2', 'U') IS NOT NULL
drop table fktest2
IF OBJECT_ID('dbo.fktest1', 'U') IS NOT NULL
drop table fktest1
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'fkTest1Trigger' AND type = 'TR')
DROP TRIGGER dbo.fkTest1Trigger
go
create table fktest1 (id int primary key, anQId int identity)
go
create table fktest2 (id1 int, id2 int, anQId int identity,
FOREIGN KEY (id1) REFERENCES fktest1 (id)
ON DELETE CASCADE
ON UPDATE CASCADE/*,
FOREIGN KEY (id2) REFERENCES fktest1 (id) this causes compile error so we have to use triggers
ON DELETE CASCADE
ON UPDATE CASCADE*/
)
go
CREATE TRIGGER fkTest1Trigger
ON fkTest1
AFTER INSERT, UPDATE, DELETE
AS
if ##ROWCOUNT = 0
return
set nocount on
-- This code is replacement for foreign key cascade (auto update of field in destination table when its referenced primary key in source table changes.
-- Compiler complains only when you use multiple cascased. It throws this compile error:
-- Rrigger Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION,
-- or modify other FOREIGN KEY constraints.
IF ((UPDATE (id) and exists(select 1 from fktest1 A join deleted B on B.anqid = A.anqid where B.id <> A.id)))
begin
update fktest2 set id2 = i.id
from deleted d
join fktest2 on d.id = fktest2.id2
join inserted i on i.anqid = d.anqid
end
if exists (select 1 from deleted)
DELETE one FROM fktest2 one LEFT JOIN fktest1 two ON two.id = one.id2 where two.id is null -- drop all from dest table which are not in source table
GO
insert into fktest1 (id) values (1)
insert into fktest1 (id) values (2)
insert into fktest1 (id) values (3)
insert into fktest2 (id1, id2) values (1,1)
insert into fktest2 (id1, id2) values (2,2)
insert into fktest2 (id1, id2) values (1,3)
select * from fktest1
select * from fktest2
update fktest1 set id=11 where id=1
update fktest1 set id=22 where id=2
update fktest1 set id=33 where id=3
delete from fktest1 where id > 22
select * from fktest1
select * from fktest2
This is an error of type database trigger policies. A trigger is code and can add some intelligences or conditions to a Cascade relation like Cascade Deletion. You may need to specialize the related tables options around this like Turning off CascadeOnDelete:
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Entity<TableName>().HasMany(i => i.Member).WithRequired().WillCascadeOnDelete(false);
}
Or Turn off this feature completely:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Some databases, most notably SQL Server, have limitations on the cascade behaviors that form cycles.
There are two ways to handle this situation:
1.Change one or more of the relationships to not cascade delete.
2.Configure the database without one or more of these cascade deletes, then ensure all dependent entities are loaded so that EF Core can perform the cascading behavior.
please refer to this link:
Database cascade limitations
Mass database update to offset PKs: make a copy of the database instead.
Special use case: company A uses a database with the same schema as company B. Because they have merged, they want to use a single database. Hence, many tables from company B's database must have their primary keys offset to avoid collision with company A's records.
One solution could have been to define foreign keys as ON UPDATE CASCADE, and offset the primary keys having the foreign keys follow. But there are many hurdles if you do that (Msg 1785, Msg 8102, ...).
So a better idea that occurs to me is simply to make a copy of the database, DROP and re CREATE the tables that must have their PKs|FKs offset, and copy the data (and while doing so, offset the primary keys and the foreign keys).
Avoiding all the hassle.
My solution to this problem encountered using ASP.NET Core 2.0 and EF Core 2.0 was to perform the following in order:
Run update-database command in Package Management Console (PMC) to create the database (this results in the "Introducing FOREIGN KEY constraint ... may cause cycles or multiple cascade paths." error)
Run script-migration -Idempotent command in PMC to create a script that can be run regardless of the existing tables/constraints
Take the resulting script and find ON DELETE CASCADE and replace with ON DELETE NO ACTION
Execute the modified SQL against the database
Now, your migrations should be up-to-date and the cascading deletes should not occur.
Too bad I was not able to find any way to do this in Entity Framework Core 2.0.
Good luck!

Create and use table referencing two rows of one other table

A tale of three tables: using Entity Framework 6, I need a user-editable table Disallow to store conflicts between rows of another table Option of type Technology.
Like this:
Technology:
ID, PK, [int], not null
Description, nvarchar(40), not null
Option:
ID, PK, [int], not null
Name, nvarchar(20), not null
ADD CONSTRAINT (FK_Tech) FOREIGN KEY [ID] REFERENCES [Technology] (ID)
Disallow:
ID1, [int], not null
ID2, [int], not null
PRIMARY KEY (ID1, ID2) ASC
ADD CONSTRAINT [FK_Valid1] FOREIGN KEY [ID1] REFERENCES [Option] (ID)
ADD CONSTRAING (FK_Valid2) FOREIGN KEY [ID2] REFERENCES [Option] (ID)
As I understand it, after importing the database into the model the user should be able to add records from the EF model so that e can specify that when choosing technology options, it should be disallowed to choose two particular options together.
I have spent much time looking for help on using multiple foreign keys into a single table and was able to create the table successfully in SQL Server and I think I understand the issues there.
But when I attempt to import into EF, an association is shown, on the Options table, but nothing else. How can I add rows if there is no mapping to the table? And then I found a link telling me that there is no way to reference a foreign key. So I added two additional fields, but even after numerous attempts -- following the advice of many links on THAT topic -- failed to bring the table into the model. How can I ensure (1) that the IDs added to the Disallow table are found in Option, and (2) still reference them in EF?
Or perhaps better said, how can I accomplish what I want: rows of conflicts that my code will use to limit the selection of options?

Adding row to relational table - linq to sql

I have 3 primary Tables - Picture, Album and Collage. Collage and Album can have 1-many pictures. Pictures do not have to be in Album to be added to the Collage.
To define their relationship, I have AlbumPicture and CollagePicture tables.
My problem is when I try to add picture already uploaded to the Collage and hence to the CollagePicture table. It throws primry key violation error on PK_Picture since picture already exists.
CollagePictures.InsertOnSubmit(new CollagePicture {Collage = CollagePicture = existingPic});
I need to add one record in CollagePicture for existing picture and new Collage. Is there any way I can tell linq to not add picture if it already exists?
I am new to Linq and still learning.
EDIT:
sorry If I was not clear.
I assign PK to Picture as GUID when Pictures are uploaded. In CollagePicture I already have this pictureId with different collage
e.g.
CollagePictureId [PK]= 1
CollageId=1
PictureId = 1234567890123456
Now I want to add same Picture with different CollageId say,2. When I try to add anything to CollagePicture it tries to add to the Picture table as well. and that is when I get an exception. Hope this will clear things up.
I finally made it work..
Instead of this
CollagePictures.InsertOnSubmit(new CollagePicture {Collage = Collage, CollagePicture = existingPic});
It should be
CollagePictures.InsertOnSubmit(new CollagePicture {Collage = collage, CollagePicture.PictureId = existingPic.PictureId});
When you add an object to the relational table entity it adds the object in primary table as well. so just referencing Id makes sure that it does not do anything to the primary table.
It was easy and I should have tried that before..but Thanks for everyone's help though.
What primary key do you have on CollagePicture table? It's not really clear, what do you want to do when inserting new entry to CollagePicture. Do you mean that you add the existing picture to an existing collage? If so your primary key should be (PictureId, CollageId)
Without a detailed view of your data model, it's really hard to take a guess.
If you correctly configured your relations and LINQ to SQL, you could do something like the following:
collage.Add(Picture);
That way LINQ to SQL would add the right rows to the tables.
To check if the picure already exists in the Collage, you would do something like this.
collage.Pictures.Where(p => p.ID == pictureID).Count() > 0
If the PK of existingPic is 0 (assuming type int) then it is new, else it already exists.
if(existingPic.pkColumnId == 0)
CollagePictures.InsertOnSubmit(existingPic);

Entity Framework Invalid Object Name Error

I'm using VS2008 to connect to a SQL server database in order to populate it in C#. It's going pretty well, I'm able to query, insert, and update all tables in my database successfully, except one. Anytime I try to query or insert into one table I get the following error:
Message = "Invalid object name 'DB_NewModelStoreContainer.DATATYPE'."
the query that generated this error is:
var test3 = (from o in context.DATATYPE
where o.DATETYPE_NAME == "Single"
select o).First();
(yes I know it should be DATATYPE, but that is not the problem =) )
Whenever I added the database to my project, for some reason it marked every attribute in the DATATYPE table as a primary key. I went into the xml of the .edmx and fixed this but I still get this error and I cannot find out why ><. Any help at all would be very greatly appreciated! Thanks in advance.
In order to work with EntityFramework ,there should be clearly mentioned PrimaryKey and Foreign Key if any exists.
If you don't have any primary Key ,make the unique column as primary key ,It should definately work. and I am wondering how could you are able to insert record without primary key
in edmx designer you might have misssed something or may be some mapping got screwed up after changes.so,modify the tables in your database to asign the primary key to every table and then try to generate the edmx from scratch.

Trying to do a hierarchical update results in an error "A foreign key value cannot be inserted"

I'm a bit of a noob with DAO and SQL Server and I'm running into a problem when I'm trying to insert values into two tables that have a relation. The table Photos has a gpsId field which has a foreign key relation with the id field of the GPSLocations table. I want to create a new Photos entry linked to a new GPSLocation, so the code looks something like this:
gpsRow = dataset.GPSLocations.AddGPSLocationsRow("0.0N", "3.2W");
dataset.Photos.AddPhotosRow(#"c:\path\file.jpg", gpsRow);
tableAdapterManager.UpdateAll(dataset);
However this results in the following error:
A foreign key value cannot be inserted
because a corresponding primary key
value does not exist. [ Foreign key
constraint name = photoToGps ]
I'm using SQL Server CE. Is my understanding correct that the TableAdapterManager should be handling this hierarchical update? I just dragged these tables onto the XSD view and relied on its automatic creation of the wrapper classes. Do I need to change anything about the relation (eg to make it a Foreign Key constraint)? I've noticed that under some circumstances the gps id is positive and sometimes negative, is that relevant?
EDIT:
I've also ensured that the update property is set to CASCADE, which results in the same error. Hierarchical updates are set to true and there is a foreign key constraint between the two tables in the designer.
It's just the configuration of your data set. Doubleclick the relation beween the tables in the Visual Studio's dataset designer, choose Both Relation And Foreigh Key Constraint option and in the Update Rule field choose Cascade option and that must be it.
Some information about the subject is in MSDN, you can look here http://msdn.microsoft.com/en-us/library/bb629317.aspx and go to the related topics.
I've managed to track down the source of this problem, which boils down to a limitation of SQL Server CE compared with the full SQL Server. It turns out the major hint that something wasn't right was because the ids were negative. The ids are negative in the DataSet before the row is inserted into the database, at which point it gets resolved to a positive index. The fact that it wasn't becoming a positive index happened because the TableAdapterManager normally does a batch statement of INSERT followed by a SELECT to update the id. However, SQL Server CE doesn't support batch statements, so this requires extra code to be written so that we simulate the SELECT step by responding to the RowUpdated event. This MSDN article explains the steps.
Did you enable Hierarchical Updates as described here?
Is there a foreign key constraint between the two tables (there should be a line on the XSD designer connecting them)? Since your fields are named differently it might not have been automatically added when you dragged the tables to the design surface.
Since the column photoToGps (foreign key) depends on the primary key (id), you cannot add a photoToGps unless there is a corresponding id present. So what you need to is individual updates, instead of doing an UpdateAll. First update the GPSLocations table, and then the other table. That way, you will have an id existing before you add a photoToGPS for it.

Categories

Resources