I have an audit trailing system in my project from http://doddleaudit.codeplex.com/.
As you can see on this image it records the EntityTable - which is the table name, and the EntityTableKey - which is the primary key
I would like to associate the audit records with the tables it had recorder, then query the result in linq to sql. But the problem is if the audit table has record for orders and record for products it will never know just by the primary key, where does the record belong, thus i need to use the table name as part of the key.
So the question is: Is it possible to create a relation that will have a composite primary key that contains the table name in it?
AuditRecord to Orders
AuditRecord to Products
You could do it, but I would recommend a bit different approach.
Don't use char/varchars/nvarchar in your PK/FK, it bloats the index. I would rather create another table that will hold TableId/TableName pairs of all your tables (you can use sys.tables.object_id for your id if you wish) and have a FK in AuditRecords to it. Then establish composite key between AuditRecords and AuditRecordFields (Id, TableId).
Another thing:
EntityTable and AssociationTable should be of sysname type
AuditDate can be of type Date (available from SQL Server 2008)
EDIT:
If you like to access audit records from each object, you can create a base class for your audited objects and implement following method (beware, it's untested):
public IQueryable<AuditRecord> AuditRecords
{
get
{
MyContext ctx = new MyContext();
var ars = from ar in ctx.AuditRecords
where ar.EntityTable.Equals(this.GetType().Name)
select ar;
return ars;
}
}
Related
I have 3 tables in my Database, one for student and other for the courses and the third one to store what every student select from courses. I want to prevent the student from selecting the same course more than once. what condition should I provide in Insert statement in the third table?
Thanks
Your StudentCourse table should have a unique constraint on the (StudentId, CourseId) table.
As an alternative, you can create the Primary Key on your StudentCourse table as a composite key on (StudentId, CourseId).
While it follows that every table in your database must have a Primary key constraint, often its an auto generated value useful when carrying out most database maintenance tasks. However the primary key itself will not protect you from user generated or user captured data that may contain duplications. Enter the “Unique” constraint! This is a very powerful table-level constraint that you can apply to your table against a chosen table column, which can greatly assist to prevent duplicates in your data. For example, say you have a “Users” table and in it, you have an EmailAddress column, surely it would be strange to capture 1 or 2 users who have an identical email address.
This is my entity that I will insert into the database:
public sampleEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PrimaryKey { get; set; }
[Required]
public string Ticket { get; set; }
}
Ticket is in the format like
string.Format("{0:yyyyMMdd}-{1}", DateTime.Now, PrimaryKey);
So when I add this entity to the context, primaryKey always is 0, so Ticket value always be '20170315-0'
For now my solution is
// first add row and save
SampleEntity sample = new SampleEntity {
Ticket=DateTime.Now.ToString("yyyyMMdd")
};
context.Samples.Add(sample);
context.SaveChanges();
// then find it out and update
var latest = context.Samples.OrderByDecending(p => p.PrimaryKey).First();
latest.Ticket += sample.PrimaryKey.ToString();
context.SaveChanges();
How can I set Ticket value according to primaryKey value when SaveChanges() without update?
You have DatabaseGeneratedOption.Identity option for primary key.
That means that only SQL Server know that ID, and that knowledge materializes only in the moment of the actual INSERT into database (as related column in database is some kind of IDENTITY column).
To understand that think, for example, of two applications which are inserting new records into database simultaneously - they will receive different keys, but you cannot know for sure which application receive which key.
Entity Framework will generate two requests for SaveChanges - first one is INSERT, and another one is SELECT to receive that generated key.
Only after that your code will know actual key and can be able to use it for your ticket calculations - so basically you cannot avoid another UPDATE with EF.
What you can do though is to change Primary Key type for something controlled by your code rather than by database - for example, random GUID; in this case you will know ID before insert and can use it in any way you want.
But using say GUID for primary key results in other complications, which in most cases won't worth it, like non-sequential insert results in often index rebuilds, still some probability of keys collision, more space to keep column etc.
Another option would be to have calculated column OR similar logic for ticket column in application, so you will have separate Date column and separate Id column, but for ticket you will either always apply concat logic whenever you need it, of create calculated column which will only return values (and thus will be read-only for database and EF).
I am trying to delete records from a DB which has no primary key. The following works:
using (myDataContext db = new myDataContext ())
{
db.ExecuteCommand("DELETE FROM myTable WHERE TradeDate = {0}", date);
}
(where date is an input to the function). But when I try convert it to LINQ
using (myDataContext db = new myDataContext ())
{
db.myTable.DeleteAllOnSubmit(db.myTable.Where(t => t.TradeDate.Date == date.Date));
db.SubmitChanges();
}
I get the following error because the table doesn't have a primary key:
Additional information: Can't perform Create, Update, or Delete operations on 'Table(myTable)' because it has no primary key.
I found the following old posts about this issue
DB:4.44:Dml Operations Using Linq Query For A Table Without Primary Key zm
Dml Operations using linq query for a table without primary key.
But I don't understand how to implement the fix they suggest (i.e. setting another key to IsPrimary).
Is there a way to do this using LINQ? Bear in mind that adding a PK to the actual SQL table is not an option (unless I just add a row counting identity column).
Without a primary key the two interfaces aren’t emitted: INotifyPropertyChanging and INotifyPropertyChanged and so LINQ to SQL doesn’t know that your record has changed. Do the following:
Open the LINQ Designer.
Open the properties window for the table you want to delete a record from.
Click on any of the columns in the entity you want to delete and you'll see a property labeled "Primary Key".
Change the value to true for column you want to use as a primary key.
Please, use the unique column as a Primary Key in the EF model.
Otherwise use DataContext.ExecuteCommand()
As others have pointed, you need to add a primary key to your table. And then execute the query.
Else you can try to delete the row manually like this:
var query = myTable.AsEnumerable().Where(r => r.Field<Date>("TradeDate") == date.Date);
foreach(var row in query.ToList())
row.Delete();
I am new to SQL and today I got assigned an important task - to create a migration script for data in a table. From my understanding, a migration script is copying data from table A and move it to other tables B and C and so on. This seems to be frequent when database designs change constantly and the team wants to preserve data.
My task:
I have a JobOffer table, with the CityId field. Now the team wants to delete that field, and to preserve information they will add the CityId to the Address table and connect both tables using an intermidiary table called Location (this allows a JobOffer to have several Addresses).
I have no idea on how to perform this task. An analogy in c# of what I prentend is this:
foreach (var row in JobOffer)
{
int addressId;
if (!Address.Contains(row.CityId)){
addressId = Address.add(row.CityId);
Locaion.add(row.JobOfferId, addressId);
}
else
{
Locaion.add(row.JobOfferId, Address.get(row.CityId));
}
}
How do I do it in SQL?
You need three tables - one for the candidates, one for the addresses (locations) and one that links the two. The third table is necessary because what you described is a many to many relationship. A single candidate may have multiple locations and a single location may house multiple candidates.
When I created similar to yours it took two scans of the input data:
The first checked if I had all the locations. If any were missing I inserted it into the location table.
The second scan inserted data into the candidate and canditatelocs table. At this point I knew for sure that I had an address for every candidate in the locations table.
Here is a description of the tables:
create table candidate (candidateid int identity primary key, idate datetime default getdate(), name varchar(200))
create table candidatelocs (candidateid int, locid int)
create table locations ( locid int identity primary key, city varchar(500), state varchar(500))
I have 2 tables; PriceList and PriceListDetail with one to many realitonship. After inserting a new PriceList and I need to copy PriceListDetail's of an existing PriceList.
var pricedetails= db.PriceListDetails.Where(p => p.PriceList Id == SomeExistingPriceListID);
All I need is to change priceListID of pricedetails above and Insert them to PriceListDetail table. When I modify priceListId of pricedetails and try to insert them I get 'cannot insert entities that already exists'.
Obvious solution is to create new entities and copy values one by one from pricedetails then insert.
Is there a way to avoid one by one copying? Maybe create duplicate rows then modify duplicated ones?
Entity frameworks uses another key than your primary key, and it is called an entity key.
So having your PriceListDetails, all you have to do is to change the primary key ID and set the entity key to NULL (NULL means a new row).