Entity Framework Code First Use foreign key as part of composite primary key - ef-code-first

Using Code First, I created a model representing a Donor. I used a migration to generate a table to store all the Donor objects. I used annotations to specify the primary key to be a composite of two properties of my model, Name and TeamId.
I added a navigation property to the model called HourlySnapshots which is an ICollection<HourlySnapshot> representing a one-to-many relationship. (HourlySnapshot is another domain model I created.) I ran the migration and it generated another table to store all the HourlySnapshot objects. As expected, it added two columns that weren't in my model to store the foreign key composed of Name and TeamId.
In order to initialize the HourlySnapshots table, I included a conventional Id property in the HourlySnapshot object to be used as a primary key. What I'm trying to do is switch the primary key of the HourlySnapshots table from the Id column to a composite of the foreign key (which is a composite of Name and TeamId) and another property of HourlySnapshot called Timestamp. In other words, make it a composite of three columns Name, TeamId, and Timestamp.
Can you think of a way to do this with Code First? I can easily do this by opening the table definition and editing there, but I would like to adhere to the Code First workflow, so that the migrations include all the changes.

After some more research, I was able to accomplish this with the Fluent API. I don't think it's possible using annotations or conventions. Here is what I ended up with in my ApplicationDBContext class...
public DbSet<Donor> Donors { get; set; }
public DbSet<HourlySnapshot> HourlySnapshots { get; set; }
public DbSet<DailySnapshot> DailySnapshots { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Donor>()
.HasKey(d => new { d.Name, d.TeamId });
modelBuilder.Entity<HourlySnapshot>()
.HasKey(s => new { s.Name, s.TeamId, s.Timestamp });
modelBuilder.Entity<Donor>()
.HasMany(d => d.HourlySnapshots)
.WithOptional()
.HasForeignKey(s => new { s.Name, s.TeamId });
modelBuilder.Entity<DailySnapshot>()
.HasKey(s => new { s.Name, s.TeamId, s.TimeStamp });
modelBuilder.Entity<Donor>()
.HasMany(d => d.DailySnapshots)
.WithOptional()
.HasForeignKey(s => new { s.Name, s.TeamId });
base.OnModelCreating(modelBuilder);
}

Related

Unwanted unique constraint in many to many relationship

I'm trying to set up a Tagging tool for images. Basically I have two tables, one for pictures, and one for tags. Both are connected with a many to many setup. I can already add a single tag to a picture, and the same tag to different pictures. However, when I try to add a second tag to an image I get an exception complaining about a unique constraint that I simply don't see.
public class MediaEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<TagEntity> Tags { get; set; }
}
public class TagEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<MediaEntity> MediaEntities { get; set; }
}
public void updateMedia(MediaEntity model)
{
using (var db = new MediaContext(_dbLocation))
{
db.Update(model);
db.SaveChanges();
}
}
public class MediaContext : DbContext
{
private const string DB_NAME = "PT.db";
private string _path;
public DbSet<MediaEntity> MediaTable { get; set; }
public DbSet<TagEntity> TagTable { get; set; }
public MediaContext(string path)
{
_path = path;
ChangeTracker.AutoDetectChangesEnabled = false;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={Path.Combine(_path, DB_NAME )}");
}
As far as I can tell my setup should create a normal many-to-many relationship, and it the database I also see pretty much this. EF automatically creates a TagTable, MediaTable, and MediaEntityTagEntityTable. But when I try to add a second tag I get this:
SqliteException: SQLite Error 19: 'UNIQUE constraint failed:
MediaEntityTagEntity.MediaEntitiesId, MediaEntityTagEntity.TagsId'.
Data from the table showing I can have the same tag on different pictures:
MediaEntitiesId
TagEntitiesId
1B48E85B-F097-4216-9B7A-0BA34E69CBFF
CF581257-F176-4CDF-BF34-09013DCEAA27
CE33F03F-5C80-492B-88C6-3C40B9BADC6C
CF581257-F176-4CDF-BF34-09013DCEAA27
523178A1-C7F8-4A69-9578-6A599C1BEBD5
0C45C9D1-7576-4C62-A495-F5EF268E9DF8
I don't see where this unique constaint comes in. How can I set up a proper many-to-many relationship?
I suspect the issue you may be running into is with the detached Media and associated Tags you are sending in. You are telling EF to apply an 'Update' to the media, but the DbContext will have no idea about the state of the Tags attached. Assuming some tags may have been newly attached, others are existing relationships. If the Context isn't tracking any of these Tags, it would treat them all as inserts, resulting in index violations (many to many) or duplicate data (many to one / one to many)
When dealing with associations like this, it is generally simpler to define more atomic actions like: AddTag(mediaId, tagId) and RemoveTag(mediaId, tagId)
If you are applying tag changes along with potential media field updates in a single operation I would recommend rather than passing entire entity graphs back and forth, to use a viewModel/DTO for the tag containing a collection of TagIds, from that apply your tag changes against the media server side after determining which tags have been added and removed.
I.e.:
public void updateMedia(MediaViewModel model)
{
using (var db = new MediaContext(_dbLocation))
{
var media = db.Medias.Include(x => x.Tags).Single(x => x.MediaId = model.MedialId);
// Ideally have a Timestamp/row version number to check...
if (media.RowVersion != model.RowVersion)
throw new StaleDataException("The media has been modified since the data was retrieved.");
// copy media fields across...
media.Name = model.Name;
// ... etc.
var existingTagIds = media.Tags
.Select(x => x.TagId)
.ToList();
var tagIdsToRemove = existingTagIds
.Except(model.TagIds)
.ToList();
var tagIdsToAdd = model.TagIds
.Except(existingTagIds)
.ToList();
if(tagIdsToRemove.Any())
media.Tags.RemoveRange(media.Tags.Where(x => tagIdsToRemove.Contains(x.TagId));
if(tagIdsToAdd.Any())
{
var tagsToAdd = db.Tags.Where(x => tagIdsToAdd.Contains(x.TagId)).ToList();
media.Tags.AddRange(tagsToAdd);
}
db.SaveChanges();
}
}
Using this approach the DbContext is never left guessing about the state of the media and associated tags. It helps guard against stale data overwrites and unintentional data tampering (if receiving data from web browsers or other unverifiable sources), and by using view models with the minimum required data, you improve performance by minimzing the amount of data sent over the wire and traps like lazy load hits by serializers.
I always explicitly create the join table. The Primary Key is the combination of the two 1:M FK attributes. I know EF is supposed to map automatically, but since it isn't, you can specify the structure you know you need.

Do navigation properties have to be explicitly set in EF Core?

So let's say I have two models:
public class Simple
{
public int SimpleId { get; set; }
public int TestId { get; set; }
public Test Test { get; set; }
}
public class Test
{
public int TestId { get; set; }
public string Name { get; set; }
}
And I want to add a new Simple like this:
public void AddSimple()
{
var simple = new Simple
{
SimpleId = 11,
TestId = 1 //Assume that this exists in the DB with name = "Testtest"
};
_repo.Add(simple);
}
When I get this from the database like so:
public async override Task<Simple> Get(int id)
{
var simple = _entries
.Include(n => n.Test)
.FirstOrDefault(n => n.SimpleId == id);
return simple;
}
Is EF Core supposed to automatically infer and get Simple.Test from Simple.TestId?
Because right now the way I have to create new Simple records is by setting Simple.Test explicitly like this:
public void AddSimple()
{
var simple = new Simple
{
SimpleId = 11,
TestId = 1 //Assume that this exists in the DB with name = "Testtest",
Test = _testRepo.GetById(1)
};
_repo.Add(simple);
}
Or else, the Simple.Test navigation property will be null. This doesn't seem like the way it's supposed to be done.
The simple answer is "No", you don't need to set navigation properties, but you arguably "should". The behaviour you will see will behave more like "It Depends". What you see will depend on whether the DbContext is tracking relative entities or not.
For a simple example with a Parent and Child entity where a Child has a Parent reference. An existing Parent ID #1 exists, and we aren't adding a duplicate child.
using (var context = new AppDbContext())
{
var child = new Child { Name = "Sven", ParentId = 1 };
context.Children.Add(child);
var parent = child.Parent; // Null
context.SaveChanges();
parent = child.Parent; // Still null.
child = context.Children.Include(x => x.Parent).Single(x => x.Name == "Sven");
parent = child.Parent; // Returns Parent /w ID 1.
}
The "depends" behavior is whether the DbContext isn't already aware of the referenced entity. For example if we do this:
using (var context = new AppDbContext())
{
var tossThis = context.Parents.Single(x => x.ParentId == 1);
// Read for demonstration, the context is now tracking parent Id 1, we're not actually going to use it...
var child = new Child { Name = "Sven", ParentId = 1 };
context.Children.Add(child);
var parent = child.Parent; // Returns Parent /w ID 1.
}
The reference for child.Parent will match tossThis even though we didn't explicitly set it. EF will provide that reference when Adding child because it is tracking it. If the DbContext happens to be tracking an entity that you reference by ID when creating a new related entity, EF will automatically associate these related entities as soon as it associates the new entity.
This can lead to some inconsistent behaviour with your entity state if you have code logic called against a newly created or updated entity. When you set FKs but not navigation properties, the navigation properties might be available, or they might not. Exposing FKs also adds a second source of truth for dealing with associated references. For instance, what is the ID of a child's parent assumed to be? child.ParentId or child.Parent.ParentId? Some code may use one or the other. This introduces opportunities for unexpected behaviour if code changes a Parent reference. For example moving a child's parent:
var child = context.Children.Include(x => x.Parent).Single(x => x.Name == "Sven");
child.ParentId = 2;
var parent = child.Parent; // Still points to Parent ID #1.
context.SaveChanges();
parent = child.Parent; // Now it depends.
What child.Parent refers to after SaveChanges will depend on whether EF happens to be tracking the parent with the new ID. If it isn't tracking parent ID #2, then child.Parent will now be null. If it was tracking parent ID #2, then child.Parent will be referencing that entity after SaveChanges.
vs.
var newParent = context.Parents.Single(x => x.ParentId == 2);
child.Parent = newParent;
var parentId = child.ParentId; // Still set to 1.
context.SaveChanges();
parentId = child.ParentId; // Updated to 2.
This behaviour is a bit more consistent. Setting the parent doesn't automatically update the FK until SaveChanges is called.
Classic Parent/Child relationships don't typically see "parents" change, but Many-to-One relationships such as Order to OrderStatus are cases where an Order could see it's Status change. By setting FKs for OrderStatus where there is a navigation property available, your behaviour could change subtly depending if the context had happened to previously work with an order with the new status or not. (Whether that updated Status might be tracked already or not.)
Overall to avoid the risk of inconsistent behaviour and the bugs that that sometimes crop up when dealing with navigation properties vs. their FKs, my advice is to only use one or the other. For general use entities where having navigation properties available is beneficial, then use navigation properties along with shadow properties for the FKs. For situations where the navigation property isn't required and we want raw performance, use FKs alone. (Bounded contexts can help manage separating entity definitions for general use /w navigation properties vs. cases where you want raw read/update performance)
The additional benefit of fetching related entities is that it can provide a more meaningful validation. For instance with an Order / OrderStatus update scenario:
var order = context.Orders.Single(x => x.OrderId == dto.OrderId);
// update various order details...
order.OrderStatusId = dto.OrderStatusId;
context.SaveChanges(); // Throws possible exception.
vs.
var orderStatus = context.OrderStatuses.Single(x => x.OrderStatusId == dto.OrderStatusId); // Throws exception if status ID is not valid.
var order = context.Orders.Single(x => x.OrderId == dto.OrderId);
// update various order details...
order.OrderStatus = orderStatus;
context.SaveChanges(); // Throws possible exception for other violations.
In the first example, any invalid/illegal data combination including FK violations will occur on SaveChanges which isn't much of a hint what went wrong. Where-as in the second example, if the OrderStatus ID provided wasn't valid, the exception details would be on the line that attempted to load that ID. Debugging issues when resolving the references is a lot easier to see exactly what/where the issue lies.
Fetching entities can "feel" expensive, but it really isn't. EF will return references it is tracking from cache, or it will go to the DB if necessary. Fetching a row by ID is about as efficient as a DB operation can get. In cases where you might be dealing with a number of references (I.e. updating a set of data) you can consolidate the read operations ahead of time. (Get the relevant IDs from your ViewModels/DTOs, then pre-load those related rows in one read call, then set the references from that set.)
From what you are saying, this looks like a Primary Key - Foreign Key relationship.
Add the annotation inside Simple class -> Test property to specify FK relationship as below:
public class Simple
{
public int SimpleId { get; set; }
public int TestId { get; set; }
[ForeignKey("TestId")]
public virtual Test Test { get; set; }
}
public class Test
{
public int TestId { get; set; }
public string Name { get; set; }
}
This explicitly tells EF that you have a relationship and helps filling in the navigational property.
You can also use fluent APIs to specify the above relationship.

Audit.net data models example

Does any one have a working example of how to added audit models to an existing project, for Audit.Net.
It is one fantastic component to use, and up until now, my team and I have gotten by with the standard JSON files, however, we'd like to migrate our current solution to our Xamarin application, and would like to store the auditing in the local SQLite database on the device.
However, the documentation for this project is somewhat lacking and there is no concise examples of how to get custom auditing working with Entity Framework.
We have worked through the MD files on the github repo, but we still cannot get auditing to work.
Another question, similar to this has been asked HERE, but there is no definitive example of what the Audit_{entity} table should look like, what fields it MUST contain, and how to set up relationships for it.
We tried to reverse engineer the JSON files into a relational structure, but at the time of asking this question, we have not gotten any auditing to write to the SQLite database.
Sorry about the documentation not helping too much, hope I (or anybody) can provide better documentation in the future.
I am assuming you are using EntityFramework to map your entities
to a SQLite database, and you want to use the EF data
provider
to store the audits events in the same database, in Audit_{entity} tables.
There is no constraint on the schema you want to use for your Audit_{entity} tables, as long as you have a one-to-one relation between your {entity} table and its Audit_{entity} table. Then the mapping can be configured on several ways.
The recommendation for the Audit_{entity} tables is to have the same columns as the audited {entity} table, with any common additional column needed, like a User and a Date defined on an Interface.
So, if all your Audit_{entity} tables has the same columns/properties as its {entity}, and you added some common columns (defined on an interface), the configuration can be set like this:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Audit_User : IAudit
{
public int Id { get; set; }
public string Name { get; set; }
// IAudit members:
public string AuditUser { get; set; }
public datetime AuditDate { get; set; }
public string Action { get; set } // "Insert", "Update" or "Delete"
}
Audit.Core.Configuration.Setup()
.UseEntityFramework(x => x
.AuditTypeNameMapper(typeName => "Audit_" + typeName)
.AuditEntityAction<IAudit>((ev, ent, auditEntity) =>
{
auditEntity.AuditDate = DateTime.UtcNow;
auditEntity.AuditUser = evt.Environment.UserName;
auditEntity.AuditAction = ent.Action;
});
Note the interface is not mandatory, but using it makes the configuration cleaner. Also note you can make your Audit_{entity} inherit from your {entity} if you wanted to.
Update
Maybe my assumption at the beginning is incorrect and you are not auditing EF entities, but any other type of audit. If that's the case, what you are looking for is a Data Provider that stores the audit events into your SQLite database.
At the time being, there is no built-in data provider that stores to SQLite, and if there was one, it would store just the JSON representation of the event in one column (like the SQL/MySql providers). But it looks like you want to have a custom schema, so you will need to implement your own data provider.
Check the documentation here.
Here is a sample skeleton of a data provider:
public class SQLiteDataProvider : AuditDataProvider
{
public override object InsertEvent(AuditEvent auditEvent)
{
// Insert the event into SQLite and return its ID
}
public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
{
// Replace the event given its ID (only used for CreationPolicies InsertOnStartReplaceOnEnd and Manual)
}
// async implementation:
public override async Task<object> InsertEventAsync(AuditEvent auditEvent)
{
// Asynchronously insert the event into SQLite and return its ID
}
public override async Task ReplaceEventAsync(object eventId, AuditEvent auditEvent)
{
// Asynchronously replace the event given its ID
}
}
Then you just set it up with:
Audit.Core.Configuration.Setup()
.UseCustomProvider(new SQLiteDataProvider());

Cannot Update Entity Using EF 6 - ObjectStateManager Error

I'm trying to update an entity using Entity Framework version 6.
I'm selecting the entity from the database like so...
public T Find<T>(object id) where T : class
{
return this._dbContext.Set<T>().Find(id);
}
And updating the entity like so..
public T Update<T>(T entity) where T : class
{
// get the primary key of the entity
object id = this.GetPrimaryKeyValue(entity);
// get the original entry
T original = this._dbContext.Set<T>().Find(id);
if (original != null)
{
// do some automatic stuff here (taken out for example)
// overwrite original property values with new values
this._dbContext.Entry(original).CurrentValues.SetValues(entity);
this._dbContext.Entry(original).State = EntityState.Modified;
// commit changes to database
this.Save();
// return entity with new property values
return entity;
}
return default(T);
}
The GetPrimaryKeyValue function is as so...
private object GetPrimaryKeyValue<T>(T entity) where T : class
{
var objectStateEntry = ((IObjectContextAdapter)this._dbContext).ObjectContext
.ObjectStateManager
.GetObjectStateEntry(entity);
return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
}
Just for clarity. I'm selecting the original entry out as I need to perform some concurrency logic (that Ive taken out). I'm not posting that data with the entity and need to select it manually out of the DB again to perform the checks.
I know the GetPrimaryKeyValue function is not ideal if there's more than one primary key on the entity. I just want it to work for now.
When updating, entity framework coughs up the error below when trying to execute the GetPrimaryKeyValue function.
The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'NAME_OF_ENTITY_IT_CANNOT_FIND'
I've written many repositories before and I've never had this issue, I cannot seem to find why its not working (hence the post).
Any help would be much appreciated.
Thanks guys!
Steve
It seems like you are having issues getting the PK from the entity being passed in. Instead of trying to go through EF to get this data you could either use their Key attribute or create your own and just use reflection to collect what the key names are. This will also allow you to retrieve multiple keys if it is needed. Below is an example I created inside of LinqPad, you should be able to set it to "Program" mode and paste this in and see it work. Hack the code up and use what you may. I implemented an IEntity but it is not required, and you can change the attribute to anything really.
Here are the results:
Keys found:
CustomIdentifier
LookASecondKey
Here is the code:
// this is just a usage demo
void Main()
{
// create your object from wherever
var car = new Car(){ CustomIdentifier= 1, LookASecondKey="SecretKey", Doors=4, Make="Nissan", Model="Altima" };
// pass the object in
var keys = GetPrimaryKeys<Car>(car);
// you have the list of keys now so work with them however
Console.WriteLine("Keys found: ");
foreach(var k in keys)
Console.WriteLine(k);
}
// you probably want to use this method, add whatever custom logic or checking you want, maybe put
private IEnumerable<string> GetPrimaryKeys<T>(T entity) where T : class, IEntity
{
// place to store keys
var keys = new List<string>();
// loop through each propery on the entity
foreach(var prop in typeof(T).GetProperties())
{
// check for the custom attribute you created, replace "EntityKey" with your own
if(prop.CustomAttributes.Any(p => p.AttributeType.Equals(typeof(EntityKey))))
keys.Add(prop.Name);
}
// check for key and throw if not found (up to you)
if(!keys.Any())
throw new Exception("No EntityKey attribute was found, please make sure the entity includes this attribute on at least on property.");
// return all the keys
return keys;
}
// example of the custom attribute you could use
[AttributeUsage(AttributeTargets.Property)]
public class EntityKey : Attribute
{
}
// this interface is not NEEDED but I like to restrict dal to interface
public interface IEntity { }
// example of your model
public class Car : IEntity
{
[EntityKey] // add the attribure to property
public int CustomIdentifier {get;set;}
[EntityKey] // i am demonstrating multiple keys but you can have just one
public string LookASecondKey {get;set;}
public int Doors {get;set;}
public string Make {get;set;}
public string Model {get;set;}
}

Entity Framework CTP5, code-first. Many to many with cascade delete

I have two entities (Customer and CustomerRole) and would like to declare many-to-many relationship between them. I can do using the following code:
modelBuilder.Entity<CustomerRole>()
.HasMany(cr => cr.Customers)
.WithMany(c => c.CustomerRoles)
.Map(m => m.ToTable("Customer_CustomerRole_Mapping"));
But it creates the relationship (and the third mapping table) with cascade delete switched off by default. How can I tell EF to create the relationship with cascade delete switched on when using many-to-many?
As of CTP5, there seems to be no way to directly turn on cascade deletes on Many to Many associations by Fluent API.
That said, if your intention is to make sure that you can delete the principal (e.g. a Customer record) without having to worry about the dependent record in the join table (i.e. Customer_CustomerRole_Mapping) then you don't need to turn on cascades on the database since EF Code First will take care of the cascade deletes on the client side when it comes to Many to Many associations.
For example, when you delete a Customer object, EF is smart enough to first send a delete statement to get rid of the dependent record in the join table and after that it will send another delete statement to delete the Customer record.
Update:
Due to a bug in CTP5, you need to explicitly eager/Lazy load the navigation property and have it loaded on the context when you remove the dependent. For example, consider this model:
public class User
{
public int UserId { get; set; }
public virtual ICollection Addresses { get; set; }
}
public class Address
{
public int AddressID { get; set; }
public virtual ICollection Users { get; set; }
}
Assuming that we have a User with an address in the database, this code will throw:
using (EntityMappingContext context = new EntityMappingContext())
{
User user = context.Users.Find(1);
context.Users.Remove(user);
context.SaveChanges();
}
However, this one will perfectly work with removing the link table's record first:
using (EntityMappingContext context = new EntityMappingContext())
{
User user = context.Users.Find(1);
((IObjectContextAdapter)context).ObjectContext
.LoadProperty(user, u => u.Addresses);
context.Users.Remove(user);
context.SaveChanges();
}
Please note that this is just a workaround and we will be able to (hopefully) remove a principal without loading its navigation property.

Resources