I have seen many Questions on foreign key constraints problem and what I got is that
By default, the following constraints are not copied to the client: FOREIGN KEY constraints, UNIQUE constraints, and DEFAULT constraints
in this document: http://msdn.microsoft.com/en-us/library/bb726037.aspx
So, it appears I have to "manually" create the relationships, once the schema is created on the client.
Once relationship has been created on client side, what if I make any changes in tables on server side, I have to recreate all relationships on client side again and again. Is not it'd be a headache. Is there anyway to write code or script to create foreign key constraints on client side that can be just copied. and if we make any changes on server side tables schema that could be done on client side by changing the script.
I am using a modified version of the sample http://code.msdn.microsoft.com/Database-Sync-SQL-Server-7e88adab#content
Sql Server Express to Sql Server over WCF Service.
I used the script from SQL Authority to generate a Alter Table script to add all the foreign keys http://blog.sqlauthority.com/2008/04/18/sql-server-generate-foreign-key-scripts-for-database/
When the client calls the WCF Service GetScopeDescription() to get the Schema for the client I run the above Stored Procedure to get all the Foreign Key relationships to add. The SQL script returned I put in a string in the DbSyncScopeDescription.UserComment field, which holds the script and transports it to the client at the same time as the Schema. Then client side after syncing scope/schema I can run the script generate the relationships.
DbSyncScopeDescription dbSyncScopeDescription = sqlSyncProviderProxy.GetScopeDescription();
sqlSyncScopeProvisioning.PopulateFromScopeDescription(dbSyncScopeDescription);
sqlSyncScopeProvisioning.Apply();
string alterDatabaseScript = dbSyncScopeDescription.UserComment;
This is specific to static database schema/relationships. When schema/relationship modifications are needed I will drop the client database first.
Sync Framework doesnt automatically pick up schema changes made to the tables being synched. whether it's just an FK, column name/type/length change, you will have to re-provision (not unless you want to hack your way on the sync objects).
if you want full schema fidelity, i suggest you create the database objects yourself (table, constraint, sp, triggers, etc...) and not let Sync itself create the tables for you.
and btw, there is no Sync Framework 4.0
I got easy way to add foreign key constrains simply make txt file of foreign key sql commands and give ';' after each sql command and use below code it works perfectly...
private void FunAddForeignKeys()
{
SqlConnection clientConn = new SqlConnection(lconString);
if (clientConn.State == ConnectionState.Closed)
clientConn.Open();
System.Data.SqlClient.SqlCommand Command = new System.Data.SqlClient.SqlCommand(GetSql("ForeignKeyQueries.txt"), clientConn);
try
{
Command.ExecuteNonQuery();
MessageBox.Show("Foreign keys added");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// Closing the connection should be done in a Finally block
clientConn.Close();
}
}
private string GetSql(string Name)
{
try
{
// Gets the current assembly.
Assembly Asm = Assembly.GetExecutingAssembly();
// Resources are named using a fully qualified name.
Stream strm = Asm.GetManifestResourceStream(Asm.GetName().Name + "." + Name);
// Reads the contents of the embedded file.
StreamReader reader = new StreamReader(strm);
return reader.ReadToEnd();
}
catch (Exception ex)
{
MessageBox.Show("In GetSQL: " + ex.Message);
throw ex;
}
}
I've got a solution that create client side table via synchronization and then add code to generate foreign key constraints. its easy way rather than to generate all tables ourselves and then add constraints to them. just copy lines for relations and that's all.
Related
I have a .net core web api. Db is PostreSQL. I have a simple POST request that create an entity with two fields:
public class ClientDto{
public string Name {get;set;}
public int ClientId{get;set;}
}
ClientId - FK foreign key to table Clients.
Some client (Postman for exapmle) execute request, but in data model send ClientId that not exists in db.
I have global exeption handler and there I handle db exception, but exception object don't include separated information.
I would like to show to user beautiful message something like "Client with id = 1 not exists".
What the best practis to handle db exceptions?
May be before save object in db I need check if client with id = 1 exists in db? But it is an additional query.
May be before save object in db I need check if client with id = 1 exists in db? But it is an additional query.
I'd do this.
If your client doesn't give you good information in its exception then your probably better to do the additional query. If you're querying on an indexed field (which i'd expect given you are using a foreign key) then it will be a very quick query.
Exception throwing and catching is fairly expensive anyway and i'd probably be happy enough with the extra call.
I am using a SQLite database EFCore 2.0 preview in UWP Project.
The address table is split into to different entities
Delivery address,
Invoice Address
using
modelBuilder.Entity<Project>().OwnsOne(p => p.DeliveryAddress);
which works great for setting up the database, with migrations, creates the different table in the database. With test data that I have put in manually works great at reading data from these tables. But how do I save changes to the DeliveryAddress table. Nothing is getting persisted to the database, when I save the using:
public void UpdateDeliveryAddress(Project modifiedProject)
{
using (var db = new SteelFrameCalculatorDataContext())
{
db.Entry(modifiedProject).State = EntityState.Modified;
db.SaveChanges();
}
}
Project being the parent entity
2017-06-11T23:21:10.9242463+01:00 Warning 8 Microsoft.EntityFrameworkCore.Model.Validation
The key {'ProjectId'} on entity type 'Project.DeliveryAddress->Address' contains properties in shadow state - {'ProjectId'}. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.ModelValidationShadowKeyWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider.
Using the following allowed in to save updates to the database. Assume the UpdateRange(entity) sets all to modified. Not sure if this is the correct way, but it works.
using (var db = new SteelFrameCalculatorDataContext())
{
db.UpdateRange(modifiedProject);
db.SaveChanges();
}
Have you tried setting the state of the child object? Looks like you're only setting the parent Project state.
Adding this should do it:
db.Entry(modifiedProject.DeliveryAddress).State = EntityState.Modified;
db.Entry(modifiedProject).Reference(a=>a.DeliveryAddress).TargetEntry.State = EntityState.Modified;
I have a database with three tables: Word, Idiom, WordIdiom that stores many to many relation between this two tables. WordItem includes only foreign keys for Word and Idiom tables.
After that, I have created Entity model, based on database. I have filled two tables with relevant content, and now I want to add cross-links between these tables.
So, I have written this code:
using (var db = new IdiomsDictionaryEntities())
{
var allIdioms = from idiom in db.Idioms select idiom;
foreach (var idiom in allIdioms)
{
string[] words = idiom.IdiomText.Split(new[] { " ", "-" }, StringSplitOptions.None);
foreach (var word in words)
{
var wordItem = db.Words.SingleOrDefault(exWord => exWord.WordString.ToLower().Equals(word));
if (wordItem == null)
{
Console.WriteLine("Idiom: " + idiom.IdiomText + ", missing word: " + word);
continue;
}
idiom.Words.Add(wordItem);
db.SaveChanges();
}
}
}
But when I run this code, I'm getting following error:
An unhandled exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll
Additional information: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
Inner-inner exception:
Unable to update the EntitySet 'WordIdiomMatch' because it has a DefiningQuery and no element exists in the element to support the current operation.`
As it is my first time with Entity Framework, I'm really don't know how to fix this. I have tried to add [ForeignKey()] property to Entity Framework models, but probably have done it wrong. I have also tried to add a primary key for WordIdiom, but it brakes even more things, as in this case I cannot even add items to Word and Idiom tables.
I have solved the problems, with help of #KerryRandolph and #AntoinePelletier
I was trying to update entities derived from a many-to-many relationship using Pure Join Table - meaning no other columns except foreign keys are allowed.
If you add a Primary Key column to a Join Table, you lose all of the entity framework advantages and have to implement insertion operation manually.
Proper solution was to alter the join table on the DB to make a PK that includes BOTH of the foreign ID columns.
First of all, i see that you have 2 add() for the same purpose. Witch is wrong. Imagine what it would look like in the data base :
wordItem.Idioms.Add(idiom);
ok now X and Y are linked by the link table as "X-Y" record.
idiom.Words.Add(wordItem);
And now... it would create another record that link these as "Y-X" witch is useles, if there is already an "X-Y" record then X is linked to Y with this single record and the other way around too.
And i'd say... usualy the primary key of a link table is the combination of the two foreign keys it contain, so the double add would crash anyway.
Using Entity Framework 5.0, and MS SQL Server 2012. Implementing some cell level encryption functionality. This requires a specific set of actions to successfully encrypt/decrypt data:
Open encryption key (ex. executing OPEN SYMMETRIC KEY TSQL command)
Execute the following SELECT DECRYPTBYKEY(key_guid, encryptedDate) AS Something FROM SomeTable
The question is: how, under Entity Framework 5.0 I can run OPEN KEY command right after the new connection to a DB is established? After the key is opened, it will stay open until this DB session is active.
Thank you
Maybe you can subscribe to the connection's StateChange event:
this.Database.Connection.StateChange += this.Connection_StateChange;
// "this" is the DbContext.
private void Connection_StateChange(object sender, StateChangeEventArgs e)
{
if(e.CurrentState == ConnectionState.Open)
{
// your commands here using sender as SqlConnection.
}
}
I just started to play with the entity framework, so I decided to connect it to my existing SQL Server CE database. I have a table with an IDENTITY(1, 1) primary key but when I tried to add an entity, I've got the above-mentioned error.
From MS Technet artice I learned that
SQL Server Compact does not support entities with server-generated keys or values when it is used with the Entity Framework.
When using the Entity Framework, an entity’s keys may be marked as server generated. This enables the database to generate a value for the key on insertion or entity creation. Additionally, zero or more properties of an entity may be marked as server-generated values. For more information, see the Store Generated Pattern topic in the Entity Framework documentation.
SQL Server Compact does not support entities with server-generated keys or values when it is used with the Entity Framework, although the Entity Framework allows you to define entity types with server-generated keys or values. Data manipulation operation on an entity that has server-generated values throws a "Not supported" exception.
So now I have a few questions:
Why would you mark key as server-generated if it is not supported and will throw an exception? It's hard to make sence from the quoted paragraph.
When I've tried to add StoreGeneratedPattern="Identity" to my entity's property, Studio complained that it is not allowed. What I'm doing wrong?
What is the best workaround for this limitation (including switching to another DB)? My limitations are zero-installation and using entity framework.
When I hit this limitation, I changed the type to uniqueidentifier
Use uniqueidentifier or generate a bigint/int key value manually is your best option.
Something like this perhaps ...
private static object lockObject = new object();
private static long nextID = -1;
public static long GetNextID()
{
lock (lockObject)
{
if (nextID == -1) nextID = DateTime.UtcNow.Ticks; else nextID++;
return nextID;
}
}
This assumes that you don't generate more than one record per tick during an application run (plus the time to stop and restart). This is a reasonable assumption I believe, but if you want a totally bullet proof (but more complex) solution, go read the highest ID from the database and increment from that.
SQL CE version 4.0 fixed this problem with its Entity Framework provider.
I just hit this issue too... mostlytech's answer is probably the best option, GUIDs are very easy to use and the risk of key collision is very low (although not inexistant).
Why would you mark key as server-generated if it is not supported and will throw an exception? It's hard to make sence from the quoted paragraph.
Because SQL Server (not Compact) supports it, and other third parties may support it too... Entity Framework is not only for SQL Server Compact ;)
In my case, all of my classes have the primary key named "ID"
I created an interface
public class IID
{
public Int32 ID { get; set; }
}
Then I create an extension method
public static Int32 GetNextID<T>(this ObjectSet<T> objects)
where T : class, IID
{
T entry = objects.OrderByDescending(u => u.ID).FirstOrDefault();
if (entry == default(T))
return 1;
return entry.ID + 1;
}
Then when I need a new ID, I just do this:
MyObject myobj = new MyObject();
myobj.ID = entities.MyTable.GetNextID();
the other option is to use SqlCeResultSet on the tables that have the identity column.
i have a primary key named ID with data type of INT32 and have Identity Column
Just do this
MyEntity Entity = new MyEntity();
String Command;
command = "Insert into Message(Created,Message,MsgType)values('12/1/2014','Hello World',5);
Entity.ExecuteStoreCommand(command);
--Exclude the primary key in the insert Statement
--Since the SQLCE do not support system generated keys
--Do not use LINQ because it supplies a default value to 0 for Primary keys that has a
data type of INT