sync framework and syncing multiple client tables to one server table - c#

Is it possible to sync multiple client tables to one server table? The issue I see it with the primary key being duplicated in the multiple clients. Is there a work around?
I tried to sync without the primary key but seems sync framework needs a key for it to work.
Or is my best bet to create multiple tables on the server and then create a view to combine the data without the primary key column?

An easy way to avoid such duplicates is to use:
a natural key
a PK providing service (you'll need some sort of number provider)
not recommended: a randomly generated PK (instead of the usual +1) (a guid works sometimes but is very tricky)
A natural key differs from the auto generated number: it's often an intrinsic property of the "thing" you are trying to store. Think of something like a license plate on a car.
By using this property, you can ensure that when you have this key, you have the same record.
Keep in mind, there are always some exceptions, even with license plates.
As for now:
You might be able to drop the insert identity, and reassign some numbers. But that will break the PK server / client relation (your server will have completely different PK than the clients). Over time this can give you a big headache.
An other way is to use a composite key:
Use your PK, but add a column like ClientID or ClientLocation, create a composite PK from both (not sure if the auto numbering will accept this). This might be the easiest option.
For the latter, in this example you can see that the combined key is unique:
//Client 1)
|---composite PK---|
| ID | ClientID |
|------|-----------|
1 cust1
2 cust1
3 cust1
//etc
//Client 2)
|---composite PK---|
| ID | ClientID |
|------|-----------|
1 cust2
2 cust2
3 cust2
//etc
//Server)
|---composite PK---|
| ID | ClientID |
|------|-----------|
1 cust1
1 cust2
2 cust1
2 cust2
3 cust1
//etc

Related

How to make a composite primary key bidirectional?

My problem is in the title. I want to make a composite primary key as bidirectional. What I mean by bidrectional? Let me explain:
I have a friendship table. Primary keys are: SenderId and ReceiverId
In OnModelCreating:
builder.Entity<Friendship>()
.HasKey(i => new { i.SenderId, i.ReceiverId });
So my friendship entities have primary keys like {SenderId, ReceiverId}. But not like {ReceiverId, SenderId}. I want to make keys to be both ways. In this way, my friendship requests will be unique and I will not create duplicate friendship requests as follows:
Is this possible?
NOTE: I know how to check if there is a entry with those IDs.But I want to implement the database to reject the new entry with the same IDs in both ways.
You need a key that conforms to a canonical form, i.e. always appear in the same order.
For example, instead of having a ReceiverID and a SenderID, you'd have two neutrally named fields (e.g. ParticipantA and ParticipantB) plus a constraint that ParticipantA must be less than ParticipantB. This way, you can set a constraint so that each pairing of IDs can only appear once, regardless of who is sending and who is receiving.
You'd then need to add a column that specifies direction of the relationship, e.g. who is the sender or if it is bidirectional.
Thus instead of
SenderID ReceiverID
-------- ----------
1111 2222
4444 3333
1234 5678
5678 1234
You'd have
ParticipantA ParticipantB Sender
------------ ------------ ---------
1111 2222 A
3333 4444 B
1234 5678 BIDIRECTIONAL
As #John Wu points out, you need a composite key where the containing key values are always in order, so (1, 2) and (2, 1) are both mapped to (1, 2), thus treated as equal.
However the proposed implementation requires too much changes to the database model and how you work with it. Also prevents having natural User.Senders and User.Receivers collection navigation properties if needed to obtain such information for a User.
So instead of modifying the existing primary data model, what you need is unique constraint (index) on a "normalized" composite key (User1Id, User2Id), where (in preudo code) User1Id = Min(SenderId, ReceiverId) and User2Id = Max(SenderId, ReceiverId).
The implementation of this is database specific. For SqlServer it can be implemented by creating two computed columns and then create unique constraint (index) on them, e.g.
// the two shadow properties mapped to the computed columns
modelBuilder.Entity<Friendship>()
.Property<Guid>("UserId1")
.HasComputedColumnSql("case when SenderId < ReceiverId then SenderId else ReceiverId end");
modelBuilder.Entity<Friendship>()
.Property<Guid>("UserId2")
.HasComputedColumnSql("case when SenderId < ReceiverId then ReceiverId else SenderId end");
// the unique index using them
modelBuilder.Entity<Friendship>()
.HasIndex("UserId1", "UserId2")
.IsUnique();

The LINQ query returns the same record in the list [duplicate]

Without getting into the "why", just understand this in inherited and what I have to work with :)
I have an EF6 edmx mapped to a view. There is no identifying column on it, so in order for EF to map the entity, the first not-null column was selected as the PK. The original thought behind this was it is read only no updates or deletes would be done. There is no filtering (ODATA sits on top of this), and the only - and I mean only - way this is used is select top N * from the entity.
There are 4 records in the view.
TypeCode | Contact | UserID | LocaleID | EntityName
---------------------------------------------------------
1 6623 1032 9 Jane
1 6623 1032 9 Jane
1 6623 1032 9 John
1 6623 1032 9 John
The problem I am seeing is that EF is mapping all 4 rows the same. All "John" names above become "Jane"
OK, putting aside the design decision, and the fact there is no identifying record on the view, why is EF mapping the last two rows wrong? My initial thought is that since the "PK" is set as TypeCode It doesn't know how to do it. But why would it be using the key column when just reading results from the database? I would have thought it only mattered for updates and deletes
If you query data by Entity Framework, the default behavior is that each materialized entity is tracked by its unique key. The unique key consists of any properties you told EF to use as key, or, alternatively, it inferred as key properties (TypeCode in your case). Whenever a duplicate entity key tries to enter the change tracker, an error is thrown telling that the object is already being tracked.
So EF simply can't materialize objects having duplicate primary key values. It would compromise its tracking mechanism.
It appears that, at least in EF6, AsNoTracking() can be used as a work-around. AsNoTracking tells EF to just materialize objects without tracking them, so it doesn't generate entity keys.
What I don't understand is why EF doesn't throw an exception whenever it reads duplicate primary key values. Now it silently returns the same object as many times as it encounters its key value in the SQL query result. This has caused many many people to get confused to no end.
By the way, a common way to avoid this issue is by generating temporary unique key values to the view by using ROW_NUMBER in Sql Server. That's good enough for read-only data that you read once into one context instance.

Entity Framework maps data wrong when the identity column is not unique

Without getting into the "why", just understand this in inherited and what I have to work with :)
I have an EF6 edmx mapped to a view. There is no identifying column on it, so in order for EF to map the entity, the first not-null column was selected as the PK. The original thought behind this was it is read only no updates or deletes would be done. There is no filtering (ODATA sits on top of this), and the only - and I mean only - way this is used is select top N * from the entity.
There are 4 records in the view.
TypeCode | Contact | UserID | LocaleID | EntityName
---------------------------------------------------------
1 6623 1032 9 Jane
1 6623 1032 9 Jane
1 6623 1032 9 John
1 6623 1032 9 John
The problem I am seeing is that EF is mapping all 4 rows the same. All "John" names above become "Jane"
OK, putting aside the design decision, and the fact there is no identifying record on the view, why is EF mapping the last two rows wrong? My initial thought is that since the "PK" is set as TypeCode It doesn't know how to do it. But why would it be using the key column when just reading results from the database? I would have thought it only mattered for updates and deletes
If you query data by Entity Framework, the default behavior is that each materialized entity is tracked by its unique key. The unique key consists of any properties you told EF to use as key, or, alternatively, it inferred as key properties (TypeCode in your case). Whenever a duplicate entity key tries to enter the change tracker, an error is thrown telling that the object is already being tracked.
So EF simply can't materialize objects having duplicate primary key values. It would compromise its tracking mechanism.
It appears that, at least in EF6, AsNoTracking() can be used as a work-around. AsNoTracking tells EF to just materialize objects without tracking them, so it doesn't generate entity keys.
What I don't understand is why EF doesn't throw an exception whenever it reads duplicate primary key values. Now it silently returns the same object as many times as it encounters its key value in the SQL query result. This has caused many many people to get confused to no end.
By the way, a common way to avoid this issue is by generating temporary unique key values to the view by using ROW_NUMBER in Sql Server. That's good enough for read-only data that you read once into one context instance.

SQL CE 3.5 - Select field from another table

I'm working with databases for the first time (SQL CE 3.5) and I'm not certain how I define a relationship between tables where later ( I think) I'll have to use a join to select some value for one field from another table.
_________ __________ __________
MY TABLE| | TABLE A | | TABLE B |
--------- |---------| |---------|
OrderID | | a_Text | | b_Text |
--------- |---------| |---------|
a_Text |
---------
b_Text |
---------
When it is all implemented when I define a value for a_Text in [MY TABLE] I only want to be able to set a value for a_Text as defined in [Table A] (and again for b_Text).
What you want here is a Foreign Key Constraint on your a_Text field which enforces a link between MY_TABLE/TABLE_A for that particular field value.
This is a database relationship and as such should be defined at database-level and if required, at model-level. Most modern day ORM technologies e.g. EntityFramework/NHibernate, do a pretty good job at representing the same relationships at model-level, or at least make it very trivial to do so - EF will do it automatically if you create a context via a database directly.
It's pretty simple to create a relationship using SQLCE through the VS Designer - Walkthrough: Creating a SQL Server Compact Database gives an example of adding a relationship between two tables.
Based on your requirements I wouldn't recommend having your value field (a_Text) in TABLE A as the PK. One of the biggest concerns is if you update the key you need to cascade that change throughout the other referencing tables. It's much more flexible to introduce a surrogate key and make your a_Text field a unique key itself.
My Table Table A Table B
-------- ------- -------
OrderID a_ID a_ID
a_ID a_Text a_Text
b_ID

What is the best way to add/update/delete a lookup table in Microsoft SQL with ASP.NET C#?

I'm working on a local city project and have some questions on efficiently creating relationships between "parks" and "activities" in Microsoft SQL 2000. We are using ASP.NET C# to
I have my two tables "Parks" and "Activities." I have also created a lookup table with the proper relationships set on the primary keys of both "Parks" and "Activities." My lookup table is called "ParksActitivies."
We have about 30 activities that we can associate with each park. An intern is going to be managing the website, and the activities will be evaluated every 6 months.
So far I have created an admin tool that allows you to add/edit/delete each park. Adding a park is simple. The data is new, so I simply allow them to edit the park details, and associate "Activities" dynamically pulled from the database. This was done in a repeater control.
Editing works, but I don't feel that its as efficient as it could be. Saving the main park details is no problem, as I simply call Save() on the park instance that I created. However, to remove the stale records in the lookup table I simply DELETE FROM ParksActitivies WHERE ParkID = #ParkID" and then INSERT a record for each of the checked activities.
For my ID column on the lookup table, I have an incrementing integer value, which after quite a bit of testing has got into the thousands. While this does work, I feel that there has to be a better way to update the lookup table.
Can anyone offer some insight on how I may improve this? I am currently using stored procedures, but I'm not the best at very complex statements.
[ParkID | ParkName | Latitude | Longitude ]
1 | Freemont | -116.34 | 35.32
2 | Jackson | -116.78 | 34.2
[ActivityID | ActivityName | Description ]
1 | Picnic | Blah
2 | Dancing | Blah
3 | Water Polo | Blah
[ID | ParkID | ActivityID ]
1 | 1 | 2
2 | 2 | 1
3 | 2 | 2
4 | 2 | 3
I would prefer to learn how to do it a more universal way as opposed to using Linq-To-SQL or ADO.NET.
would prefer to learn how to do it a more universal way as opposed to using LINQ2SQL or ADO.NET.
You're obviously using ADO.NET Core :). And that's fine I think you should stick to using Stored procedures and DbCommands and such...
If you were using MSSQL 2008 you'd be able to do this using TableValued parameters and the MERGE statement. since you're using MSSQL 200 (why?) what you'd need to do is the following:
1. Send a comma delimited list of the Activity ids (the new ones) along with the ParkId to your stored proc. The ActivityIds parameter would be a varchar(50) for example.
In your stored proc you can split the ids
The strategy would be something like
1. For the Ids passed in, delete records that don't match
The SQL for that would be
DELETE FROM ParkActivities
WHERE ActivityId NOT IN (Some List of Ids)
WHERE ParkId = #ParkId
Since your list is a string you can do it like this
EXEC('DELETE FROM ParkActivities WHERE ActivityId NOT IN (' + #ActivityIds + ') AND ParkId = ' + #ParkId)
Now you can insert those activities that are not already in the table. The simplest way to do this would be to insert the ParkActivity ids into a temp table. To do that you'll need to split the comma delimited list into individual ids and insert them into a temp table. Once you have the data in the temp table you can insert doing a join.
The is a built-in user defined function in MSSQL 2000 that can do the split and return a Table Variable with each value on a seperate row.
http://msdn.microsoft.com/en-us/library/Aa496058
What is wrong with LinqToSQL and ADO.NET? I mean, could you specify your doubts about using those technologies
update
if LinqToSQL is not supported for 2000, you can easily upgrade to free 2008 express. It would be definitely enough for purposes you described.

Categories

Resources