How can I stop Fluent NHibernate from creating foreign keys - database

I got a polymorphic relationship like the following example:
public class A
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
Class B & C contining a List of A's:
public class B/C
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<A> As { get; set; }
public virtual SomeParent Parent { get; set; }
}
My goal are queries like
session.Linq<B>().Where(x => x.Parent == someParent && x.As.Contains(someA));
Currently I configured a Many-To-Many relation between B => A and C => A using a shared link table because I want to have all my links in one table.
In this examle NH shema export creates 4 tables, A, B, C and ChildToA.
HasManyToMany(x => x.As)
.AsBag()
.Table("XX_ChildToA")
.ParentKeyColumn("Child_ID")
.ChildKeyColumn("A_ID")
.Cascade.All()
This works fine as long as you use only one the child types because shema export generates a foreign key restricting the "Child_ID" to IDs of whatever table it hits first while exporting (B in this case).
var b = session.Get<B>(id);
b.As.Add(someA);
tx.Commit(); // works fine
var c = session.Get<C>(id);
c.As.Add(someA);
tx.Commit(); // crashes because of FK constraint
Can I stop FluentNHibernate from creating this FK? While I searched google for this problem I noticed HBM samples with foreign-key="no" attributes in many-to-one relationships. So NHibernate should be able to solve this, right? However I would like to keep my fluent mappings because I can create a generic base mapping class for all my child types this way and currently all our mappings are FNH mappings.

This should do it:
HasManyToMany(x => x.As)
.ForeignKeyConstraintNames("no", "no");

I'm not entirely familiar with FluentNHibernate, but I'm assuming you could set this custom attribute on your mapping for the collection using something like:
.SetAttribute("foreign-key", "no")

Related

How to store multi values Ex (skills) in Asp.net core mvc

I want to store multiple values from a dropdown using .NET Core MVC and Entity Framework. I have no idea how to do that. This is my model code.
public class Project
{
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string? Description { get; set; }
public List<int> SkillsID { get; set; }
[ValidateNever]
public List<Skill> skills { get; set; }
}
Start here (https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key ) to understand relationships in EF. In your case you will likely want to set up a Many-to-Many relationship between Projects and Skills. This assumes you will have a list of Skills where by each Project associates itself with 0, 1, or many of those skills. In this case you would have classes like:
public class Project
{
// ... project properties.
public virtual ICollection<Skill> Skills { get; protected set; } = new List<Skill>();
}
public class Skill
{
// ... skill properties.
}
When mapped, you tell EF that Project .HasMany(x => x.Skills).WithMany() as we likely don't need a Projects collection on each individual Skill entity. The last step is telling EF how to associate these two entities. In Many-to-Many relationships this involves a joining table such as a ProjectSkills table:
[tbl:ProjectSkills]
- PK,FK - ProjectId
- PK,FK - SkillId
You don't track lists of FKs in the entity. Think of it from a database perspective. When relating tables together with FKs, you don't have an array or such of FKs within a single row. You use a joining table.
The PK for the table is a composite between the Project ID and Skill ID, where each of those is a FK back to the corresponding table. EF can create this table by convention or you can configure it manually if you want to fine-tune the naming.
If you want to track more detail about the relationship such as tracking a CreatedDate etc. then you will need to map the relationship as an entity which would look like:
public class Project
{
// ... project properties.
public virtual ICollection<ProjectSkill> ProjectSkills { get; protected set; } = new List<ProjectSkill>();
}
public class Skill
{
// ... skill properties.
}
public class ProjectSkill
{
[Key, Column(Order = 0)]
public int ProjectId { get; set; }
[Key, Column(Order = 1)]
public int SkillId { get; set; }
public DateTime CreatedDateTime { get; set; }
// .. Other details about the relationship.
[ForeignKey("ProjectId")]
public virtual Project Project { get; set; }
[ForeignKey("SkillId")]
public virtual Skill Skill { get; set; }
}
You may come across examples for EF core using these joining entities as this was required in earlier versions of EF Core (2, 3.1) for all Many-to-Many relationships.
For a One-to-Many where skills specifically belong to a Project, then a ProjectID would be put into the Skill table. The Project entity would have the same collection of Skills, but the mapping would be a: .HasMany(x => x.Skills).WithOne() Where the Skill table would contain a ProjectID to associate itself to a given project. EF can represent this relationship as a one-way where Project has the collection and Skill doesn't expose a reference to Project, or bi-directional where you can add a Project reference into Skill. (.HasMany(x => x.Skills).WithOne(x => x.Project))

Another efcore violation of primary key constraint reference table problem

Assume I've read and googled, and I still don't know what I'm doing incorrectly. Whenever I try to execute
_dbContext.Set<T>().Add(aMediaObjectWithAssociatedProvider);
_dbContext.SaveChanges();
I get the dreaded efcore violation of primary key constraint reference table
I have a class as such:
public class Media : BaseModel
{
public virtual string Title { get; set; }
public virtual string? Description { get; set; }
public virtual string Source { get; set; }
public virtual Guid? MediaTypeId { get; set; }
public virtual Guid? ProviderId { get; set; }
public virtual DateTime? StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual Provider? Provider { get; set; }
}
The BaseModel class is
public abstract class BaseModel : IBaseModel
{
public virtual Guid Id { get; set; }
}
The Provider class is as such:
public class Provider : BaseModel
{
public virtual string Name { get; set; }
public virtual string? ApiUsername { get; set; }
public virtual string? ConfigurationSection{ get; set; }
}
My DBContext has the following:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Media>().HasKey(x => x.Id);
mb.Entity<Media>().HasOne(p => p.Provider).WithOne().HasForeignKey<Media>(x => x.ProviderId);
}
The code for inserting a new object is as follows:
public T Insert(T oneObject)
{
try
{
// Ensure the entity has an ID
if (oneObject.Id == Guid.Empty)
{
oneObject.Id = Guid.NewGuid();
}
_dbContext.Set<T>().Add(oneObject);
_dbContext.SaveChanges();
}
catch (Exception error)
{
_logger.LogError(error.Message, error);
}
return oneObject;
}
Assume that providers are static, in a sense that they already exist in their table, and I don't want to add new providers when I save media... Media just needs to have a provider.
I know exactly what is happening (the model, after travelling through json back through the api to the server is losing context, but I'm also trying to build a repository type of system where I don't have to build complex save logic for every object. (hence why i'm hand wringing over adding code that loads existing providers).
This problem specifically began rearing its head when I was saving new Media objects into the database with existing Providers. I am still mulling over how to look up children dynamically, but i'm not quite there yet.
I've been at this for so long, i'm about ready to give up on efcore relations and just rebuild the models as single objects, and handle all of the manipulation in javascript. And I don't like this idea.
I know for a fact that there will be questions for more code, but please let me know what. Again, I'm just stepping into .net core / ef core so this code-first is a little confusing for me. Thanks
You may have 2 options to try. Do backup your whole project and database beforehand. Clone your database to another database name. Try these either one option using new cloned database for testing.
No.1
Set "newid()" without quotes in your ID's default value in sql server. So you don't need to use Guid.NewGuid() in code every insert. newid() will auto generate GUID.
No. 2
How about removing primary key from ID (GUID) and then creating new column "UID" (running number) and set UID (running number) as primary key and enable its identity? You need to change all other tables too. And re-link UID each other if you use relationship. This way, your UID will not have existing number when insert.

How do I use a composite key for a one to many relationship in Code First EF

I am using EF Code First.
I need two tables, LedgerCategories and LedgerSubCategories with a one-to-many relationship (Categories -> SubCategories), with the keys in each being codes (strings) - i.e. LedgerCategoryCode and LedgerSubCategoryCode respectively. However, I need to allow the SubCategoryCode values to be the same for different Categories.
E.g. CategoryCode = REHAB, SubCategoryCodes = MATL, CONTR, and FEES; and CategoryCode = MAINT, SubCategoryCodes = MATL, CONTR, and FEES.
I'm thinking I need to use a composite key and include both the CategoryCode and SubCategoryCode fields in the LedgerSubCategories table. Currently I have:
public class LedgerCategory
{
[Key]
public string LedgerCategoryCode { get; set; }
public string Description { get; set; }
public List<LedgerSubCategory> LedgerSubCategories { get; set; }
}
public class LedgerSubCategory
{
[Key, Column(Order = 0)]
public string LedgerCategoryCode { get; set; }
[Key, Column(Order = 1)]
public string LedgerSubCategoryCode { get; set; }
public string Description { get; set; }
}
I am seeding these tables using only instances of the LedgerCategory class, having each contain a List of appropriately instantiated LedgerSubCategory classes. This appears to both set up the DB schema correctly (in my perception), and populate both tables appropriately.
But, when I reinstantiate a simple List of LedgerCategory, i.e.
using (var db = new BusinessLedgerDBContext())
{
var LedgerCategories = db.LedgerCategories.ToList();
}
The LedgerCategory instances don't contain their respective List of associated LedgerSubCategory instances.
I am trying to avoid, what seems like a kludge, to introduce a unique number or Guid ID field in LedgerSubCategories as a PK and just index off the other Code fields. I haven't tried this, but I'm not sure it would cause any different results for reinstantiating the LedgerCategories and getting associated LedgerSubCategories.
Any advice on how to do this appropriately and get proper results is appreciated.
To, I suppose, answer my own question, I have found that overriding OnModelCreating() in the respective DbContext with Fluent API to establish the one to many relationship and foreign key when the Code First framework establishes the desired DB Schema. There appears no other way to do this, such as with Attributes. By many accounts of others, including MSDN, Fluent API appears to be what is needed. However, that has led me to a new issue, or set of issues, which I've posed as a question here.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configures the one-many relationship between Categories and
// SubCategories, and established the Foreign Key in SubCategories
modelBuilder.Entity<Category>()
.HasMany<SubCategory>(c => c.SubCategories)
.WithRequired(s => s.Category)
.HasForeignKey<string>(s => s.CategoryCode);
}

optional many to one or zero issue in Entity Framework

I am trying to create a quick demo shop, and I am failing with an optional many to one or zero relationship.
The relevant classes are:
Item
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public int SubCategoryID { get; set; }
public virtual SubCategory Category { get; set; }
public double Price { get; set; }
}
Order
public class Order
{
public int ID { get; set; }
public DateTime DateOfOrder { get; set; }
public virtual ICollection<Item> Items { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
However, I am getting confused because viewing the model shows:
but, the database itself shows (for items):
Which indicates to me that each item can only belong to a single order.
Do I have to create a separate class that is many to many orders/items?
I seem to remember EF doing this automatically, but, I haven't touched it for a few years and I just can't remember what I used to do.
I had to add:
public virtual ICollection<order> Orders { get; set; }
to the Item... I'm never going to call it this way, but it looks like that is required for EF to build this relationship.
I am sure it used to be easier, so, leaving this question open so someone can give a better answer!
If you add a collection of Orders to the Item entity, EF will create for you implicitly the junction table on your DB. In this page you can find more info about how to configure a many to many relationship.
The junction table generally is mapped when you need to add an additional column (that excludes both keys of the tables you are joining). In that case you need to create two one-to-many relationships between the entity that represent the junction table and Order and Item respectively. Some people recommend always map the junction table because that way you have all the tables represented as entities and you can write queries starting by the junction table. You can find interesting info about this subject in this link. But, in my experience, in most cases you can work perfectly without map explicitly the junction table.

dreaded "Ids can not be autogenerated for entities with multipart keys"

Ok I’m at a loss, being new to breeze I’m still learning the ropes. My project uses the hot towel template for AngularJs and breeze from John Papa.
He's what I’m trying to achieve: I have a master\slave tables in my database. An "Agency" has many people it can "Notify". Here are the EF classes for the server side:
public class Agency {
public Agency() {
this.Notifies = new HashSet<Notify>();
}
public long Id { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
<<removed unneeded details>>
public bool Active { get; set; }
public virtual ICollection<Notify> Notifies { get; set; }
}
public class Notify
{
public long Id { get; set; }
public long? AgencyId { get; set; }
public string Name { get; set; }
<<removed unneeded details>>
public virtual Agency Agency { get; set; }
}
Now the Maps:
public class AgencyMaps : EntityTypeConfiguration<Agency>
{
internal AgencyMaps()
{
HasKey(x => x.Id);
}
}
public class NotifyMap : EntityTypeConfiguration<Notify>
{
internal NotifyMap()
{
HasKey(x => x.Id);
HasOptional(x => x.Agency)
.WithMany(p => p.Notifies)
.HasForeignKey(i => i.AgencyId);
}
}
Now on the client side I use breeze to create new entities like this:
// create a new entity
function create() {
return manager.createEntity(entityName);
}
// create a new notify entity
function createNotify(){
return manager.createEntity(entityNameNotify);
}
Then there are two scenarios I need to achieve:
- First is where I retrieve an existing agency and add additional
people to notify
- Second is where I create a new agency and add people
to notify
Both fail in the same place.
Note: I’m using SQL server and my Id fields are bigint (long) at this point in time.
I’m retrieving the “Agency” entity and placing it in a variable called “vm.agency”. “vm.agency” has a navigation called “notifies” with an entity type of “Notify”. So when I want to create and add a new person I’m calling this function:
function addNotifyRec(){
if (vm.agency !== undefined){
var notifyRec = datacontext.agency.createNotify(); //<<< fails here
notifyRec.agencyId = vm.agency.id;
notifyRec.name = vm.notify.name;
<<removed unneeded details>>
vm.agency.notifies.push(notifyRec);
logSuccess(“New person to notify added”);
}
else
{ logError(“Agency is undefined”); }
}
As soon as the createNotify() is called I get the “Ids can not be autogenerated for entities with multipart keys” error.
So I’m stuck. It seems to me this is a pretty common scenario. I am obviously not understanding the breeze framework well enough to implement this. If you can point me in the right directions I’d appreciate your help.
UPDATE 4/9/2014
I'm thinking i could eliminate this issue altogether if i switch over to guid id and generate them client side. Is this correct thinking?
What's interesting here is that Breeze thinks that Notify.Id and Notify.AgencyId are multi part primary keys but they are actually not. Id is a PK and AgencyId is an FK. The only thing that I can think of is try removing the EntityTypeConfiguration for both Agency and Notify, specifically the part where it specifies HasKey and HasForeignKey. This Fluent API configuration shouldn't be required as EF will match your configuration by convention instead.
I took a different approach on working around my issue. Since i have the luxury to change out the id types, i swapped out the bigint ids to Uuid types and removed the auto generation of the ids in sql. Now i'm just creating my own ids using breeze.core.getUuid() when a new record is created. Not sure this is the most efficient way to work around the issue, but it seems to be working fine.

Resources