EF Code First: One-to-One One-way Relationship - ef-code-first

I regularly have the following structure:
MyClass
public virtual ICollection<Version> Versions { get; set; }
public virtual Version CurrentVersion { get; set; }
That is, there is a list of stuff, and some class both points to that list, and one specific item in that list - either the current version of many versions, the next upcoming event in a list of events, etc.
In my schema what I'd like to end up with is a Foreign Key pointing from Version to MyClass - that much works out properly. But then I'd like a Foreign Key pointing from MyClass to Version representing the CurrentVersion property, with no Foreign Key pointing back - I don't want the extra storage or bother of telling a Version what MyClass it's the CurrentVersion for, if any. Put another way, I'd like this second relationship to be one-way from MyClass to Version, even though it's one-to-one.
What EF Code First gives me instead is the normal one-to-many on the first property, with the FK from Version to MyClass, but then a full one-to-one relationship on the second property with an FK pointing in both directions - so the underlying schema for Version ends up with MyClass_Id and MyClass_Id1.
So, is there a way to get a one-way relationship in EF Code First without resorting to the Fluent API? It looked like maybe System.ComponentModel.DataAnnotations.Schema.InverseProperty had a shot at it, but it didn't seem to offer a way to say "Don't generate one."

The key is to specify the InverseProperty on the property that points back, so that EF realizes it's to the Many-to-Many, not to the One-to-One.
public class MyClass
{
public int Id { get; set; }
public Version CurrentVersion { get; set; }
public ICollection<Version> Versions { get; set; }
}
public class Version
{
public int Id { get; set; }
[InverseProperty("Versions")]
public Versioned Versioned { get; set; }
}

Related

Xamarin Forms Add List to SQL database

I have a problem. I created the following class:
public class KnownDevice
{
public int Id { get; set; }
public string Name { get; set; }
public string IP { get; set; }
public string MAC { get; set; }
public string Type { get; set; }
public List<TriangleRegistryObject> triangles { get; set; }
public List<HexagonRegistryObject> hexagons { get; set; }
}
Now, I want to create a Database on the mobile phone itself, so I use the following code to create the table:
database = DependencyService.Get<ISQLite>().GetConnection();
database.CreateTable<KnownDevice>();
But the code crashes on the second line with the error:
System.NotSupportedException: 'Don't know about
System.Collections.Generic.List`1
Now on the internet I found that it is not allowed to add a List to a database, but I need the data in that list, so I have no idea how I can fix this problem. The list can contain arround 25 rows!
Any idea how I can solve this problem?
List<TriangleRegistryObject> is not a valid type for a SQLite database value. Your type of List<TriangleRegistryObject> does not match any of the clrType == typeof(XXXX) statements, so you get that exception. You will need to rethink the class structure a little to be able to use SQLite-net like that.
For more details about the SQLite database, you could download the source file from the link for reference.
https://learn.microsoft.com/zh-cn/samples/xamarin/xamarin-forms-samples/todo/
If you want to use ou could use List, you could use SQLite-Net Extensions instead of SQLite.
You could refer to the link. The SQLite-Net Extensions library direct to specific relationships in database.
How can you store lists of objects in SQLite.net?

Is it possible to set up this relationship via the Fluent API?

I have two code first entities, Package and PackageEntry that I am having trouble setting up in EF Core.
I am trying to achieve the following with the code first entities and the Fluent API:
A Package can contain any number of PackageEntries
Each PackageEntry has a reference to a single Package entity (a different instance of a package, unrelated to the parent Package reference that contains the collection of PackageEntries)
The two entities:
public class Package{
public Package()
{
_packageEntries = new List<PackageEntry>();
}
//trimmed other properties
private readonly List<PackageEntry> _packageEntries;
[NotMapped]
public IReadOnlyCollection<PackageEntry> PackageEntries => _packageEntries.ToList().AsReadOnly();
}
and
public class PackageEntry
{
public int DisplayOrder { get; set; }
public int PackageID { get; set; }
public Package Package { get; set; }
public int Quantity { get; set; }
public Package ParentPackage { get; set; }
public int ParentPackageID { get; set; }
}
What I currently have using the Fluent API, which is not working is:
modelBuilder.Entity<Package>().HasMany(x => x.PackageEntries).WithOne();
modelBuilder.Entity<PackageEntry>().HasOne(x => x.Package).WithOne().HasForeignKey(typeof(PackageEntry), "PackageID");
It isn't throwing errors, but what I am seeing is that when a PackageEntry is added to a package, it is not getting saved when calling SaveChanges on the context.
Am I doing something wrong with the Fluent API or something else?
EDIT
I had missed adding the top level package to the context, once that was done the package entry that gets added to it is being saved. I would still appreciate comments on the Fluent API setup and any best practices.
From the PackageEntry entity, I need to know both the Parent Package and the contained Package which will be separate references to the same type. I can't seem to set this up with the Fluent API, when the Parent Package is loaded via EF it doesn't contain any PackageEntry objects, even if their ParentPackageID is set correctly.
Upon some offline advice from an EF expert, I have worked around this issue by removing the navigation property for PackageEntry.Package and simply manually handle the foreign key for that package entity.
Once I did that, now when the Parent Package entity is loaded, it properly loads the children PackageEntries.
So, the PackageEntry class now looks like this:
public class PackageEntry
{
public int DisplayOrder { get; set; }
public int PackageID { get; set; }
//public Package Package { get; set; } //Handle manually
public int Quantity { get; set; }
public Package ParentPackage { get; set; }
public int ParentPackageID { get; set; }
}
And the Fluent API code:
navigation = builder.Metadata.FindNavigation(nameof(Package.PackageEntries));
//EF access the PackageEntries collection property through its backing field
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
modelBuilder.Entity<Package>().HasMany(x => x.PackageEntries)
.WithOne("ParentPackage")
.HasForeignKey(nameof(PackageEntry.ParentPackageID))
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
Your Package.PackageEntries collection is marked [NotMapped], and it does not have a setter. No matter what, EntityFramework is not going to pick that up.
I've never tried using an IReadonlyCollection<T> with EntityFramework, but I would imagine that EF won't like that either.
Your first try should be to remove the attribute and arrange the property like this:
public virtual IReadOnlyCollection<PackageEntry> PackageEntries {
get {
return _packageEntries.ToList().AsReadonly();
}
protected internal set {
_packageEntries = value;
}
}
Granted, that would require you to remove the readonly from the private member variable.
That being said, I'm not sure if EF has an internal list that it eventually assigns to the property, but I would imagine that it would just call the Add() method on the collection (which is why your properties must be ICollection<T> instead of IEnumerable<T>.
Therefore, if that is all still not working, you should make _packageEntries protected internal and use that as your EF collection. Then you can only publicly expose your PackageEntries as you are doing now.

SQLite-Net Extensions | Foreign Key Reference to same entity

I am facing an issue in using SQLite-Net Extensions to save data in local DB in scenario where the foreign key is referencing the same entity (self-join).
Example – Employee and Manager. Every employee has a manager and a manager is also an employee. I am facing issues in saving data in such cases. It will be really helpful if you can provide some insights. Does this extension support this kind of relationship?
Yes, relationships between objects of the same class are supported, but the foreign keys and inverse properties must be explicitly specified in the relationship property attribute because the discovery system will get it wrong as there are be two relationships with the same type.
This example is extracted from the project readme:
public class TwitterUser {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "LeaderId", "Followers",
CascadeOperations = CascadeOperation.CascadeRead)]
public List<TwitterUser> FollowingUsers { get; set; }
// ReadOnly is required because we're not specifying the followers manually, but want to obtain them from database
[ManyToMany(typeof(FollowerLeaderRelationshipTable), "FollowerId", "FollowingUsers",
CascadeOperations = CascadeOperation.CascadeRead, ReadOnly = true)]
public List<TwitterUser> Followers { get; set; }
}
// Intermediate class, not used directly anywhere in the code, only in ManyToMany attributes and table creation
public class FollowerLeaderRelationshipTable {
public int LeaderId { get; set; }
public int FollowerId { get; set; }
}
As you can see here we have a many-to-many between Twitter users. In your case it will be a one-to-many, so you won't need the intermediate table and you'll need the foreign key (ManagerId for example) in your Person class.

ef two relations

I have the following Entity Framework 5 code first classes
public class Airplane
{
public int Id { get; set; }
public int LeftWingId { get; set; }
public virtual Wing LeftWing { get; set; }
public int RightWingId { get; set; }
public virtual Wing RightWing { get; set; }
}
public class Wing
{
public int Id { get; set; }
}
Airplane has one left and one right wing (both are required). Wing may belong to a 0..1 airplane (as a left or right wing) or to some other "flying device".
Deleting an airplane should cascade-delete it's wings.
How can this be configured in code-first fluent API?
Is it possible to have two 0..1 --- 1 associations in EF with cascade delete on both?
Unfortunately, you cannot have two associations pointing from same source table to same target table with cascade delete on both.
However, that is not due to limitation in EF, but in SQL server.
Quote from answer on another question you might want to check:
SQL Server does simple counting of cascade paths and, rather than
trying to work out whether any cycles actually exist, it assumes the
worst and refuses to create the referential actions (CASCADE): you can
and should still create the constraints without the referential
actions. If you can't alter your design (or doing so would compromise
things) then you should consider using triggers as a last resort.

Entity Framework 4.1 Code First: Advice on persisting data from external source?

Part of my project is to persist data from another source. In this case we have an SAP data source that we will need to pull data from. I need to take the data from SAP and map it to entities I have in my application. Here is an example of an entity I have in my application:
public class Project : BaseEntity
{
public string Name { get; set; }
public string ProjectNumber { get; set; }
public string Description { get; set; }
public string CreatedBy { get; set; }
public string ModifiedBy { get; set; }
public string Currency { get; set; }
#region Navigation Properties
public virtual Address Address { get; set; }
public virtual CompanyCode CompanyCode { get; set; }
public virtual ICollection<Contact> TeamMembers { get; set; }
#endregion
}
As you can see, I have child objects that I map from SAP as well. I need some advice on the best way to insert and update my entities. I am struggling with knowing when to add (insert) entities to my context and when to attach (update) them, because SAP doesn't have knowledge of what my application may or may not have. I need to guard against duplicates, too. For example, should I perform a lookup of each child entity in my parent entity to see if they exist before I apply them to the parent? Then, add / attach the entire parent object to the context or handle each entity separately while still maintaing their relationships?
Yes you must manually test everything to make correct decision what must be inserted, updated or deleted. Depending on the application you can use some more complex queries to reduce number of round trips to the database - for example you can use single query with Contains to load all TeamMembers needed for processed Project or you can load Project with including all related data if you also need to test if project exists.
I did large synchronization application before and I end up with pre-loading all entities at the beginning with few queries and working completely in memory.
Don't forget to use DbSet's Local property or Find method to take advantage of already loaded entities.
You can also use some custom stored procedures to improve performance of this operation.

Resources