I'm really struggling with this scenario :
I have two entities: Person and Company
both share common properties so I have a base abstract class Contact
Now I have another entity called Supplier this can be either a Company or a Person
And I just don't know how to set up this scenario.
What I have so far is :
public abstract class Contact {
public abstract string DisplayName { get; }
...
}
public class Person : Contact {
public override string DisplayName { ... }
public string SomePersonProperty { get; set; }
...
}
public class Company : Contact {
public override string DisplayName {...}
public string SomeCompanyProperty { get; set; }
...
}
public class Supplier {
public ICollection<Product> Products { get; set; }
...
// how can I make this being either a Person or a Company ?
}
The use for this will be:
Get Suppliers that are Persons
Get Suppliers that are Companies
Get all Suppliers
With the object Supplier ideally I want to access the particular properties either of Company or Person.
Is that even possible ? Where's the flaw in the design (pretty sure there's at least one).
From my thinking:
Person is a Contact
Company is a Contact
Supplier is either a Person or a Company
Person can be a Supplier
Company can be a Supplier
Supplier cannot be a Person and a Company
I'm trying to implement this in C# Entity Framework Code First but I think the question is not close to those technologies.
UPDATE : I came up with this but I'm not sure if ok or messy. Comments are welcome!
public abstract class Supplier {
public int SupplierId { get; set; }
public ICollection<Product> Products { get; set; }
...
}
public abstract class Supplier<T> : Supplier where T : Contact {
public T Contact { get; set; }
}
public class SupplierPerson : Supplier<Person> {}
public class SupplierCompany : Supplier<Company> {}
not sure what type of Database Schema this will generate...
Make your Person and Company implementing Supplier interface. Supplier extends the Contact interface. Now, you can include the Persons and Companies into your lists of suppliers
class Institution {
Supplier supplier; // refers either Person or Company
This is natural implementation and, therefore, has an advantage that you do not need to allocate extra space per object to store the category of referred supplier. I suppose that this solution is not acceptable OOP is considered evil ourdays.
Related
I have a contact class
[Table("Contacts")]
public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
}
I have two classes inheriting from Contact:
[Table("Employees")]
public class Employee : Contact
{
public decimal Salary { get; set; }
}
[Table("Suppliers")]
public class Supplier : Contact
{
public string TIN { get; set; }
}
I am using Entity Framework 6.3 as my ORM.
Can I model something where the same contact can be both Employee and
Supplier with the same unique Id.
Employee emp = new Employee();
emp.Id = "C1";
emp.Name = "Employees";
emp.Salary = 10000;
emp.TrackingState = TrackingState.Added;
Supplier sup = new Supplier();
sup.Id = "C1";
sup.Name = "Employees";
sup.TIN = "ABC";
sup.TrackingState = TrackingState.Added;
When I do:
context.Employee.Add(emp);
context.Supplier.Add(sup);
context.Save();
Obviously, it will not allow me to add the record.
I get an error:
"Violation of PRIMARY KEY constraint 'PK_dbo.Contacts'. Cannot insert duplicate key in object 'dbo.Contacts'. The duplicate key value is (C1).\r\nThe statement has been terminated."
Can I make the Supplier also share the same Id as that of employee and
enable insert / update of employee and supplier?
Thanks
I assume you are not doing any specific inheritance mapping which by default will map this as Table per Hierarchy (TPH) inheritance. With this mapping you get a single Db table that contains the aggregate of all the fields in the base type and the dependent types with a discriminator field to know which type the row should be materialized. This allows for doing a query where you ask for a type.
With TPH each row can only be a single type, so you can not have the same row be both an Employee and Supplier.
There is another type of mapping you can do called Table Per Type (TPT) which will create a table for each type, so in your case 3, one for the shared fields, and one for each dependent type. This should allow for what you are asking about. (YMMV)
However, it seems to be that Employee and Supplier would be used in much different domains so I would suggest that you create your Contact table and relate it to both your employee and supplier.
[Table("Contacts")]
public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
}
[Table("Employees")]
public class Employee
{
public string Id { get; set; }
public string ContactId { get; set; }
public decimal Salary { get; set; }
public Contact Contact { get; set; }
}
[Table("Suppliers")]
public class Supplier
{
public string Id { get; set; }
public string ContactId { get; set; }
public string TIN { get; set; }
public Contact Contact { get; set; }
}
Now you can query for an employee:
db.Employees.Include(e => e.Contact).First();
Or for a Supplier:
db.Employees.Include(e => e.Contact).First();
Which might be cleaner than the inheritence query you would need:
db.Contacts.OfType<Employee>().First();
In both the Has A modeling I show above and the Is A Modeling with TPT you are getting three tables. You just have the FK in what I show rather than the same ID in 3 tables with TPT.
There is also Table Per Class you can look at, which is similar to TPT but you don't get a table for the abstract/parent class, rather you get table for each dependent type with all the fields in it. I don't think this is what you want because it would have duplicate data, however, it does mean less joins.
I am currently using OData V4 and wish to join 2 tables Account and Product:
The tables are as follows:
Account: Id, Name, Address, ColorCode,
Product: Id, AccountId
AccountId in the Product table is a foreign key mapped to the Id field in the Account table
In my builder I have :
var ProductType= builder.EntityType<Product>();
When I build the Product entity I wish to get the "ColorCode" values from the Account entity.
How can i establish this relationship in the model builder?
I would like the product class to look like this:
public class Product
{
public string Id { get; set; }
public string AccountId { get; set; }
public string ColorCode { get; set; }
}
OData enables you to define relationships between entities. It seems that you're using Web API 2.2 for OData V4 to write your service. In this case you can build the relationship between Products and Accounts like this:
Firstly, in the definition of your POCO classes for Products, you need to add a navigation property for its account(s):
public class Product{
public int Id {get;set;}
public string AccountId {get;set;}
public Account Account {get;set;} // define "Account" as a navigation property of Product
public class Account{
public int Id {get;set;}
public string Name {get;set;}
public Address Address {get;set;} // Address may be a complex type
public int ColorCode {get;set;}
}
Then in the class that inherit from the DbContext, add information about both entities in:
public virtual DbSet<Product> Products { get; set; }
public virtual DbSet<Account> Accounts { get; set; }
Finally in WebApiConfig.cs, you define the model using ODataConventionModelBuilder according to your need. The model builder will automatically recognize the relationship from the POCO classes and
generate the model for you.
After the service is built, on the client side, a client send such request to get the Product and the ColorCode of its Account:
GET http://host/service/Products?$expand=Account($select=ColorCode)
An example can be viewed here: http://services.odata.org/v4/(S(xrtmlkz1igiqidututicmb2t))/TripPinServiceRW/People?$expand=Trips($select=TripId)
I've got a programm logic problem..
Ok, let's start.
I have 2 classes in a model.
namespace Korbball.Models
{
public class Clubs
{
public int id { get; set; }
public string name { get; set; }
public string state { get; set; }
public string description { get; set; }
}
public class ClubDetails : Clubs
{
public string website { get; set; }
public string banner { get; set; }
}
}
In a first step, i will call an overview of all Clubs with the entries of the Club Class.
If i click on a Club, there is a link to -> Website/Clubs/Details/Id
Now, i will show on this view all the data from Clubs and ClubDetails.
The Data for this is stored on a database.
private static List<Clubs> clubs = new List<Clubs>();
KorbballEntities db = new KorbballEntities();
public KorbballRepository()
{
var clubsdata = db.Korbball_Clubs.ToArray();
foreach (var c in clubsdata)
{
clubs.Add(new Clubs { id = c.ClubId, name = c.Name, state = c.State });
}
}
So i get the Data for ONLY the club information.
For Details, should i generate a new LIST with all the informations?
Hope anybody know what i mean...can't declare it much better..
thanks!
Update:
Ok, i think the problem is, that i cant find someone who can teach me the mvc logic =D
I have many clubs. This clubs has basic informations (name,id,state)
This clubs have also detailed informations like description,banner,website etc.
I made a ModelClass
ClubModel.cs
public class Clubs
{
public int id { get; set; }
public string name { get; set; }
public string state { get; set; }
public string description { get; set; }
}
public class ClubDetails : Clubs
{
public string website { get; set; }
public string banner { get; set; }
}
I will show a list of all Clubs with the basic informations with site/Clubs
If i go to the detailed view, Clubs/Details/Id i will show all the information(Basic + detailed)
Your solution seems to be cool. I cant find any ideas on the internet how to build the "best practice" method on such a situation.
Is the relationship between Club and Details one-to-one? It sounds like it based on how you described everything but it doesn't make as much sense because like someone mentioned you inherited from Club, so you could just load Club_Details and you'll automatically get Club columns.
If your relationship were one-to-many the easiest way is to build the association into your model (foreign key relationship on the database) and then use the "Include" to tell Entity Framework that you want associated entities. I put this together quickly so syntax may not be perfect the basic idea is to look into the "Include".
This logic is flawed because it is loading many clubs and then loading club details, you'd want to fix that.
private static List<Clubs> clubs = new List<Clubs>();
private List<Club_Details> clubDetails = new List<Club_Details>();
// make a view model class that contains club and club details (one to many)
private ClubData clubdata = new ClubData();
public KorbballRepository()
{
var clubsdata = db.Clubs.Include("Club_Details").ToArray();
foreach (var c in clubsdata)
{
clubs.Add(new Clubs { id = c.ClubId, name = c.Name, state = c.State });
/* sample of how to reference the Included entities use the association name
of Club_Details if it were a one to many relationship, ideally you
would create a view model or something with the two classes defined
and one club and list of club details, then define a list of the view
model for all Clubs */
clubDetails = new List<Club_Details>();
foreach (var d in c.Club_Details)
{
clubDetails.Add(new Club_Details {
website = d.website.ToString(),
banner = d.banner.ToString()
});;
}
/* load the data to the view model class and loop again */
}
}
I have a legacy table I need to connect my app to. I am using a code-first, POCO model. I have the following classes:
public class Equipment
{
[Key]
public string EquipmentId { get; set; }
public string OriginatorId { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
[Key]
[Column("employee_id")]
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
[ForeignKey("OriginatorEmployeeId")]
public virtual Equipment Equipment { get; set; }
}
I need to map EmployeeId in the Employee class to to OriginatorEmployeeId in the Equipment class.
Also, the legacy table is represented by the Employee class. The table is actually named employee (lower case) and the EmployeeId column is named "employee_id". I want to keep naming of my classes and properties consistent with the rest of the app, hence Employee and EmployeeId.
Here is what I have tried using fluent API:
modelBuilder.Entity<Employee>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("employee");
});
modelBuilder.Entity<Equipment>()
.HasOptional<Employee>(u => u.Employee)
.WithOptionalDependent(c => c.Equipment).Map(p => p.MapKey("OriginatorEmployeeId"));
I am probably mixing things I don't need to. The error I am getting right now is:
Multiplicity is not valid in Role 'Equipment_Employee_Source' in relationship 'Equipment_Employee'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Any help is appreciated.
Can an employee record be associated to more than one equipment record? If they can then your Employee POCO should contain a collection property representing a one-to-many relationship between Employee and Equipment.
public virtual ICollection<Equipment> Equipments {get;set;}
You configuration should then be adjusted accordingly to show this relationship:
modelBuilder.Entity<Employee>()
.HasMany<Equipment>(u => u.Equipments)
.WithRequired(c => c.Employee).HasForeignKey(p => p.OriginatorId);
It also looks like you will need to setup a configuration for your column name mappings as well. Therefore, I would recommend that you create a separate configuration file for each of your POCOs to make it easier to manage the configurations, then just add those configurations to the modelbuilder.Configurations collection in your OnModelCreating event of your DBContext
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelbuilder.Configurations.Add(new EmployeeConfiguration());
}
My simplified domain model looks something like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
and I've defined a viewmodel:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
public string Name { get; set; }
public string Company { get; set; }
public List<SelectListItem> Companies { get; set; }
}
to manage my contacts in a view.
Since I want the user to be able to choose from a list of companies I've added a list of SelectedListItem which will be rendered in my view like this:
<%=Html.ListBoxFor(m => m.Company, (List<System.Web.Mvc.SelectListItem>)Model.Companies)%>
Now, when the user submits my form I remap my viewmodel with my model before I save it.
I populate my Contact and use the id of the ContactViewModel.Company to create an object of type Company to associate with the property of the Contact class.
Since I don't want to fetch the whole company from the database I just fill the id.
When I persist my contact, though, I get an exception: "not-null property references a null or transient Domain.Contact.Company".
What is the best solution to manage lookups and persistence with MVC + Nhibernate?
Do you have any suggestions from your experience?
Unfortunately with NHibernate and lookups you can't just assign the ID property to a new instance of the Company object and then assign that Company object to the Contact.
Generally what I would do is in my repository, assuming that you can't change the Company information when saving a contact is something like this:
public Contact Save(Contact contact)
{
if(contact.Company.Id > 0)
contact.Company = Session.Load<Company>(contact.Company.Id);
Session.SaveOrUpdate(contact);
}
I generally find this allows you to encapsulate the logic of loading the Company and also allows you to keep it all wrapped up nicely in a single session.
Using Session.Load in this manner avoids hitting the database as described here
If you don't do this, what you're essentially saying to NHibernate is that you have a company object which you have assigned an ID and now want to save it with all the properties set to Null or empty string values or whatever and that is not what you want.
Alternatively you could create a Save specific Domain Object that looks like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class SavableContact : Entity
{
public virtual string Name { get; set; }
public virtual IdK CompanyId { get; set; }
}
Which maps directly to the Contact table in your database so that when you Save this entity you can literally just map back the CompanyId from your view model and NHibernate will only save that value back and not care at all about the company objects.
It's a case of working out what works best for you. I personally prefer the first option as the extra bit of logic helps simplify the domain model, however if you're creating and exposing a public API then the second method might make more sense.