Objectify How to Assign value to Parent Key - objectify

I'm getting my feet wet with persistence and Objectify. I'd like some guidance on assigning a Parent key. My specific questions are in all caps. Thanks.
(The sample model below contains an AppUser and a Video. The idea is like YouTube; a user creates videos that belong to him/her.)
#Entity
class Video{
// QUESTION 1: SHOULD THIS CLASS HAVE ONLY 1 KEY FIELD IF I WANT A
PARENT RELATIONSHIP WITH AppUser, AND TYPE IS Key<AppUser> ?
#Parent Key<AppUser> owner;
#Id private Long id;
protected Video(){}
protected Video(User u){ // GAE User object
AppUser au = ofy().load().type(AppUser.class).filter("userId",u.getUserId()).first().get();
// QUESTION 2: WHICH WAY IS RIGHT (TO ASSIGN PARENT KEY)?
this.owner = Key.create(au.getKey(),AppUser.class,au.getId());
// or:
// owner = au.getKey();
// or:
// owner = au;
}
}
#Entity
public class AppUser {
#Id private String userId;
// QUESTION 3: DO ALL CLASSES REQUIRE A KEY FIELD?
private Key<AppUser> key;
protected AppUser(){}
protected AppUser(User u){// GAE User object
this.userId = u.getUserId();
}
public String getId(){
return userId;
}
public Key<AppUser> getKey(){
// QUESTION 4: IS THIS THE CORRECT WAY TO RETURN THE KEY?
// WOULD THAT IMPLY I NEED TO EXPLICITLY ASSIGN A VALUE TO FIELD key?
return this.key;
// THE ALTERNATIVE WOULD BE TO CREATE A KEY
AND RETURN IT RIGHT? (THEN I CAN EXCLUDE FIELD key?)
// return Key.create(AppUser.class, userId);
}
}

Answering my own question based on further knowledge:
Normally if a parent relationship is desired, one Key is fine. I can't see why another Key field would be required.
I don't think there's 1 right way to assign a value to the #Parent Key. Using this seems to work:
this.parent = Key.create(instanceOfParent);
All classes do not REQUIRE a key field. Use when needed.
There's no one right way to return a Key, both examples could work.

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.

An exception of type 'Microsoft.EntityFrameworkCore.DbUpdateException' occurred in Microsoft.EntityFrameworkCore.dll

I am having trouble at very long time. Lets imagine this example:
public class Coordinate {
public int id {get;set;}
public int x {get;set;}
public int y {get;set;}
}
public class Planet {
public int id {get;set;}
public string name {get;set;}
public Coordinate coordinate {get;set;}
}
I have created two models, and the model Planet has the model Coordinate as attribute.
Now imagine somewhere in the code I create one coordinate and it is stored in database.
Imagine this is the coordinate:
Coordinate c = new Coordinate();
c.x = 1;
c.y = 2;
Then I add it to my database and it is saved.
But when I create a planet and I do:
planet.coordinate = c;
And then I try to add it to database I have the following error:
An exception of type 'Microsoft.EntityFrameworkCore.DbUpdateException'
occurred in Microsoft.EntityFrameworkCore.dll but was not handled in
user code
Additional information: An error occurred while updating the entries.
See the inner exception for details.
I know I can change the attribute public Coordinate coordinate to public int coordinate_id but I want to do this with the Coordinate model instead.
I am using ASP NET CORE 1.0
Cumps
Your problem is that at this point, c already has an Id.
With planet.Add, the planet and all coordinates attached to it will be set to Added in your DbSet's and upon calling SaveChanges, insert statements will be created. (Here I assume an autoincrement on your column and your Id property)
When SaveChanges is completed, EF will see that the planet is in the database, but the Id of the just added coordinate is different (it was altered by DBMS, so now the coordinate is twice in your database, with two different Id's), so it will expect something went wrong and throw this exception.
When you don't have problems with duplicate entries, set the Id to null or 0. Otherwise, there are two solutions:
-Set only the FK property, not the navigation property
or
-Call SaveChanges only once (for example, just add the planet, however with added coordinates relationship fixup should lead to the same result)
Having a look at the inner exception will give you a more detailed look at what is going wrong.
To do this, in debug mode, when the exception shows. Click view detail and follow the tree until you find inner exception.
There may be duplicate rows, primary key issues or structure issues.
I was having the same problem and I realized that I was creating more than only one instance to access the database.
So the solution I took was creating a class that made only one access.
class SingletonContext {
private static Context _context;
public static Context GetInstance() {
if (_context == null) {
_context = new Context();
}
return _context;
}
}
And in every access to the database layer I call the GetInstance(), like this:
private static Context _context = SingletonContext.GetInstance();
I also got the similar error, while creating the post method but I resolved this by doing the following
Make sure that you have included the following code in
Constructor
In this import the interface file of which the foreign is key belongs to
HttpPost method
[HttpPost]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
//IMPORTANT TO INCLUDE [FromQuery]
public IActionResult CreateBatch([FromQuery] int trackId,
[FromBody] BatchDto batchCreate)
{
if(batchCreate == null)
return BadRequest(ModelState);
var batch = _batchInterface.GetBAtches()
.Where(c => c.batchName.Trim().ToLower() ==
batchCreate.batchName
.TrimEnd().ToLower());
if (batch == null)
return BadRequest(ModelState);
var batchMap = _mapper.Map<Batch>(batchCreate);
batchMap.track = _trackInterface.GetTrack(trackId); //Important
if (!ModelState.IsValid)
return BadRequest(ModelState);
if(!_batchInterface.CreateBatch(batchMap))
{
ModelState.AddModelError("", "Something went in the
creation of batch");
return StatusCode(500, ModelState);
}
return Ok("Successfully created Batch");
}

How to design a table in dynamodb for Carts

I have a use case where in a person can have no. of carts. This is possible because 1) user can complete an order with a cart and after that cart is considered closed 2) user can leave a cart for 2 months and it is considered expired. If user adds new item on 2 months old cart, old cart is marked expired and new cart is generated.
I tried designing following table:
Table Name: Cart
Primary Hash Key: cartId (String)
Primary Range Key: updated (String)
I am using updated as a range column so that when I query it I can get all the carts sorted on when user updated those and I can pick the first (or last) one without sorting myself to have the most recent cart. However this is messing up my use cases.
If a user adds another item, I update the item in the cart and update the updated column as well. However this creates another cart for me (with same cart id but with new updated column). After re-reading the docs, I understand that primary key is composite of cartId and updated so probably I should remove it. However I believe my use case is genuine enough and it is bad that in my case I have to do sorting in application. Another way around would be to use an auto increment as range but that is non intuitive way of putting columns. If there is a work around pls let me know. I am using DynamoDBMapper and posting my classes (with only few fields).
import java.util.Set;
import com.amazonaws.services.dynamodbv2.datamodeling.*;
#DynamoDBTable(tableName="Cart")
public class Cart {
private String cartId;
private String email;
private Set<String> cartItemsJson;
private String status;
private String created;
private String updated;
#DynamoDBHashKey(attributeName="cartId")
public String getCartId() {
return cartId;
}
public void setCartId(String cartId) {
this.cartId = cartId;
}
#DynamoDBAttribute(attributeName="email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#DynamoDBAttribute(attributeName="cartItems")
public Set<String> getCartItemsJson() {
return cartItemsJson;
}
public void setCartItemsJson(Set<String> cartItemsJson) {
this.cartItemsJson = cartItemsJson;
}
#DynamoDBAttribute(attributeName="status")
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#DynamoDBAttribute(attributeName="created")
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
#DynamoDBRangeKey(attributeName="updated")
#DynamoDBVersionAttribute(attributeName="updated")
public String getUpdated() {
return updated;
}
public void setUpdated(String updated) {
this.updated = updated;
}
}
This the persistence layer code. I have tried various combinations of Save behaviour but still same results.
protected static DynamoDBMapper mapper = new DynamoDBMapper(dynamoDbClient);
mapper.save(cart,new DynamoDBMapperConfig(DynamoDBMapperConfig.SaveBehavior.UPDATE));
In DynamoDB you can not update the Hash or Range key. Updating them means deleting and create a new entry.
I know that you can create a Secondary index. Maybe this will help.
Also I think you can overcome the updated as range key. You can create the table as follows:
HashKey = userId
RangeKey = cardId ( but cardId needs to be sortable for each user )
normal column = updated
normal column = etc..
When you need the last cardId of a specific user, you can get top 1 rows for a hashkey=your user and reverse sorted so you get the last one first.
When you need to add an item to the card, you don't need to update the hash/range keys.
Hope it helps.

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;}
}

Resources