I have a model with some inherits and it is using nhibernate to persisti on a Database. The nhibernate mapping with fluent nhibernate is working fine, but I have a scenario where I need to save a child for a existent parent. My model looks like this:
public class Item
{
public long Id { get; set; }
public string Name { get; set; }
// other properties
}
public class ItemCommercial : Item
{
public decimal Value { get; set; }
// other properties
}
In my Database, the respective tables are related by Id <-> Id (one per one).
I would like to know, how to Save just a ItemCommercial instance for a existent Item on database. I have the Id of the Item, but I do not know howt to say to nhibernate to say just the Child, instead creating a new Item, for sample:
session.Save(itemCommercialObj); // will create a Item and ItemCommercial with the same Id
Thank you.
As I also answered here
No, it is not possible to "upgrade" an already persisted object to its subclass. Nhibernate simply doesn't support this.
If you safe the subclass with the same ID as the base class, Nhibernate simply creates a copy with a new ID of the object instead of creating the reference to Member...
So basically you could do either
Copy the data of Customer into Member, delete customer and save Member
Use a different object structure without subclasses where Member is a different table with it's own ID and a reference to Customer
Use native sql to insert the row into Member...
you can not the runtimetype of an object like that hence NH does not support it. Change the design to
public class Item
{
public long Id { get; set; }
public string Name { get; set; }
public CommercialValue CommercialValue { get; set; }
// other properties
}
public class CommercialValue
{
public Item Item { get; set; }
public decimal Value { get; set; }
// other properties
}
and a one-to-one mapping. Then it is as simple as setting the CommercialValue property
Related
I have an entity called Asset, similar to below:
public class Asset
{
public int Id { get; set; }
public int TypeId { get; set; }
public int AddedById { get; set; }
public DateTime DateTimeAdded { get; set; }
public virtual AssetType Type { get; set; }
public virtual ITUser AddedBy { get; set; }
}
I want to be able to have a navigation property that is linked to a single table, but that table is dependent on what type of Asset it is. For instance, if the Asset is of the type "Printer" then I want the navigation property to link to the PrinterDetail entity. My initial way of going about this was to have unused columns in the Asset entity, but I figured that was wasteful or bad practice. Is there something that I am overlooking or is this just something that cannot be done?
Thanks for any advice given.
if you want navigate printerDetail by type you can use entityfraemwork inheritance strategy:
Table per Hierarchy (TPH)
Table per Type (TPT)
Table per Concrete class (TPC)
you have to create Model per each type and use TPT strategy for that.
and then you can use fluent api for config mapping for that.
parent Model (Asset) must define as abstract class and AssesTypes Must be Drive from the Parent.
more information
I have a Person Class and Inventory can be two types: Sales and CustomerService.
Sales and CustomerService have their unique properties and Peron holds the common properties.
I want to be able to query
So, when creating all three classes how do i create EF relation between them? OR is there a better way to think about the division of classes?
I don't want to have Person as Abstract class because most of the time i would want to query for the common properties.
There are 3 possible approaches you can take here:
1. Store all types in a single table (Table per Heirarchy)
You would have a single Person class that contains all possible properties that would be needed between the three classes. In addition, you would add a PersonType enum to specify different types for each entry.
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
// ...
public PersonType Type { get; set; }
}
public enum PersonType
{
Sales,
CustomerService
}
This is generally the simplest and best performing approach. The biggest issue is with specialized fields. Since every type is in this one table, this table will need to contain all of the fields that any type may need. This also means all specialized fields need to be nullable, which makes it difficult to enforce specific types having specific fields.
2. Store each type in a separate table (Table per Concrete Class)
Instead of having a Person table at all, you could instead just have Sales and CustomerService tables that simply repeat the properties that would have been contained in the Person table.
public class Sales
{
public int SalesId { get; set; }
public string Name { get; set; }
// ...
}
public class CustomerService
{
public int CustomerServiceId { get; set; }
public string Name { get set; }
// ...
}
Of course, you can still take advantage of the Person abstraction in code if you want. Using code-first, you can make use of inheritance:
public class Person
{
public string Name { get; set; }
}
public class Sales : Person
{
public int SalesId { get; set; }
// ...
}
public class CustomerService : Person
{
public int CustomerServiceId { get; set; }
// ...
}
Just make sure that you only define entities for Sales and CustomerService in your DbContext subclass:
public class MyContext : DbContext
{
// Do not include a DbSet for Person.
public DbSet<Sales> Sales { get; set; }
public DbSet<CustomerService> CustomerService { get; set; }
// ...
}
The advantage of this approach is that your types are separated into clear, distinct sets. The downside is that there is no easy way to do a universal search through every single "person" since that abstraction doesn't exist as far as the database is concerned. For example, if you wanted to find someone with a specific name, you'll have to do separate searches through the Sales table and the CustomerService table manually, which may not be ideal. Also, if you end up with a person who serves a role in both sales and customer service, you'll be creating redundancy since you need to enter their information for both entries.
3. Store each type and the base type in their own tables (Table per Type)
On top of your Person class, you'll also create Sales and CustomerService classes that each specify their specialized properties and contain a reference to the Person class. This is a common principle known as composition over inheritance; since we can't effectively model inheritance in a database, we can use composition instead.
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
// ...
}
public class Sales
{
public int SalesId { get; set; }
public int PersonId { get; set; }
public virtual Person { get; set; }
// ...
}
public class CustomerService
{
public int CustomerServiceId { get; set; }
public int PersonId { get; set; }
public virtual Person { get; set; }
// ...
}
This will allow you to add the specialized properties for each type while still maintaining a universal Person table that you can search through. This will also allow you to reuse a person's information if they serve multiple roles. The downside is that creating a new Sales and CustomerService record is a little more tedious, since you'll also need to also either find an existing Person record or create a new one. This also may not be the best on performance since queries may end up requiring joins.
The approach you should take depends on your needs. If you want to go more in depth with these 3 strategies, check out this tutorial for implementing inheritance in Entity code-first:
http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx
With Entity Framework Core you can use inheritance in your database:
public class PeopleContext : DbContext {
public DbSet<Person> Persons { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<CustomerService>().HasBaseType<Person>();
modelBuilder.Entity<Sales>().HasBaseType<Person>();
}
}
This will create one table with the properties of all derived types. Also, it will create a Discriminator-Column so that if you query your database EF Core instantiates the correct derived types:
context.Users.Add(new Sales() {
Id = 1
});
context.SaveChanges();
// This will actually be of type "Sales"
var salesPerson = context.Persons.Single(u => u.Id == 1);
For more information look here and here.
I prefer to use System.ComponentModel.DataAnnotations to apply attributes to my models such as the database table name to the class, the key(s), the foreign keys, and the inverse properties for navigation. EF will use this to auto-magically create the database table(s) with the inheritance. EF can create one table with all the properties from the derived types, or separate tables for each type including the base class (which can be abstract or not). You can search against the base type and it will return the proper implementation of the derived types. So you can get back a list containing both Sales and CustomerService objects.
I have a large application that has many drop down fields in the system that have just a small set of values. It's not really worth it to have a table for each of these values and a separate page to handle crud for each of the drop down types. WHat I did is make a DropDowns table that has a discriminator column to discern which type of drop down it is.
I define each type in the code by just creating a class that derives from the drop down class.
My CRUD manager is able to use reflection to allow the user to add a value to any of the drop downs.
So far so good.
Now I want to be able to EXTEND a particular drop down, so that it can have properties that the other drop downs don't have.
For instance, the list of payment terms. I'd like to add a field for the deposit percentage, so that I can use that to calculate the deposit.
My goal is to have all of the values stored in a single table, similar to the way drop down values are.
In SQL, I would just have a table called DropDownExtendedProperties with fields: DropDownID, PropertyName, PropertyValue with the DropDownID linking the Extended Property to the dropdown value that it accompanies.
In C# / EF I was able to get the correct database structure using 1 to 0 or 1, BUT once I make subtypes of DropDownExtendedProperty, the dropdown type makes a direct relationship to that table for the subtype.
I tried fooling with a one to many but I don't see a way out of this other than just pulling the types that need extended properties out of the DropDown table and making their own table & CRUD. I understand that at this point that is actually the simpler solution, but I think what I'm trying to do should be possible and I just can't figure out how.
I've included some code below, but I have tried the relationship a few different ways, and I am posting the most recent attempt.
public class DropDownExtendedProperty
{
[Key]
public int PropertyID { get; set; }
[ForeignKey("DropDown")]
public int DropDownID { get; set; }
public virtual DropDown DropDown { get; set; }
public int? IntValue { get; set; }
public string StringValue { get; set; }
public FieldType FieldType { get; set; }
public DateTime? DateValue { get; set; }
public float? MoneyValue { get; set; }
}
public class DropDown
{
[Key]
public int ID { get; set; }
[Required]
public string Value { get; set; }
public virtual List<DropDownExtendedProperty> ExtendedProperties { get; set; }
}
public class PaymentTerms : DropDown
{
public virtual DaysToPay DaysToPay { get; set; }
}
public class DaysToPay : DropDownExtendedProperty
{
}
public enum FieldType
{
Date,
String,
Int,
Money
}
modelBuilder.Entity<DropDownExtendedProperty>()
.HasRequired(a => a.DropDown)
.WithMany();
My goal with this is to be able to define SINGLE value for each DropDown that has a specific type THROUGH the more abstract DropDownExtendedProperty. So in our case, the PaymentTerms dropdown value of "Net30" can have an accompanying int value in the DaysToPay field which I can use in the code for calculations.
I have a collection on a model:
[Table("Templates")]
public class Template
{
[Key]
public Guid ID { get; set; }
public virtual IList<TemplateSection> Sections { get; set; }
}
[Table("TemplateSections")]
public class TemplateSection
{
internal TemplateSection() { Fields = new List<TemplateField>(); }
[Key]
public Guid ID { get; set; }
[Required]
public Guid TemplateID { get; set; }
public virtual Template Template { get; set; }
}
This appears to create the correct relationships in the database.
When I update my database any sections I have added are not saved to the database. Here's how I'm updating the dabatase:
Template existingtemplate = db.Templates.Find(sltemplate.ID);
db.Entry(existingtemplate).CurrentValues.SetValues(template);
db.SaveChanges();
If I update inspect the existingtemplate before db.SaveChanges(); the Sections collection is null. Any other changes will have been updated onto existingtemplate.
If I add:
existingtemplate.Sections = template.Sections;
before db.SaveChanges(); then the sections will be saved to the database but aren't reloaded if I load the template again.
Obviously I have missed something in declaring the one-to-many relationship but having read dozens of articles I can't see what it might be.
The construct CurrentValues.SetValues only sets scalar properties, in other words, the non-navigation properties.
I'm a bit surprised that existingtemplate.Sections = template.Sections; apparently saves the TemplateSection objects, but doesn't establish the associations. I would have expected it to do neither or both.
One way to make sure that everything is saved correctly is to first load existingtemplate.Sections and Add() each TemplateSection object to it. This requires a database roundtrip though.
It's more efficient to attach the TemplateSection objects to the context and set their TemplateID property.
How do you go about protecting the primary key property on a POCO object that is used by EF?
If I have a POCO like so:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
....
}
How do I prevent someone form changing the value of the Id?
EF uses reflection to set property values. Thus, even private setter will work:
public Id { get; private set; }
You could mark the setter as internal, that way only the containing assembly classes can modify it.
public class Customer
{
public int Id { get; internal set; }
public string FirstName { get; set; }
....
}
I'm not really sure what purpose you are using EntityFramework for, but keep in mind that if that field is getting serialized either for a web app or a service, users will still be able to modify that field. So when you are saving any object to your database, you should always verify that a user has the authority to edit that object.