Entity Framework 6: Two unrelated entities mapped to same table - c#

I want to map one table to two unrelated entities: EntityBasic and EntityAdvanced.
EntityAdvanced has extra business logic that I don't need for this one feature, and I would like to make a new Entity that only has fields from the table.
MyTable:
MyTableId : Guid
ParentId : Guid
Name : string
Description : string
Type : int
EntityBasic:
[Table("MyTable")]
public class EntityBasic
{
[Key]
public Guid MyTableId { get; set; }
public Guid ParentId { get; set }
public string Name { get; set; }
public string Description { get; set; }
public int Type { get; set; }
[ForeignKey("ParentId")]
public virtual List<EntityBasic> Entities{ get; set; }
}
EntityAdvanced:
[Table("MyTable")]
public class EntityAdvanced
{
private List<EntityAdvanced> _entities;
private List<Filter> _filters;
[Key]
public Guid MyTableId { get; set; }
public Guid ParentId { get; set }
public string Name { get; set; }
public string Description { get; set; }
public int Type { get; set; }
[ForeignKey("ParentId")]
public virtual List<EntityAdvanced> Entities
{
get { //Some complicated getter }
set { //Some complicated setter }
}
[NotMapped]
public string ImageUrl
{
get { //Some complicated getter }
set { //Some complicated setter }
}
public void SetFilters(//Some parameters)
{
//Some logic
}
}
When I do this i get this error:
The entity types 'EntityAdvanced' and 'EntityBasic' cannot share table 'MyTable' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.
Is there a way to do what I want?

As a base start, your EntityAdvanced should inherit EntityBasic since they share the same base set of properties. You don't need to rewrite them. Note the extends EntityBasic.
[Table("MyTable")]
public class EntityBasic
{
[Key]
public Guid MyTableId { get; set; }
public Guid ParentId { get; set }
public string Name { get; set; }
public string Description { get; set; }
public int Type { get; set; }
[ForeignKey("ParentId")]
public virtual List<EntityBasic> Entities{ get; set; }
}
[NotMapped]
public class EntityAdvanced : EntityBasic
{
//[NotMapped]
public string ImageUrl
{
get { //Some complicated getter }
set { //Some complicated setter }
}
public void SetFilters(//Some parameters)
{
//Some logic
}
}
Using inheritence, List<EntityBasic> Entities could reference EntityAdvanced objects so you don't need anymore to declare:
[ForeignKey("ParentId")]
public virtual List<EntityAdvanced> Entities
{
get { //Some complicated getter }
set { //Some complicated setter }
}
You can get usefull information about implementing inheritence with Entity Framework here.
Happy coding!

I think you can use the ability "Table Splitting" of Entity Framework 6,
Have a look at the example here: https://www.c-sharpcorner.com/UploadFile/ff2f08/table-splitting-in-entity-framework-6-code-first-approach/

Related

Automapper, Entity Framework Core and multiple nested collections

My database has two tables - RuleGroups and Rules. My Entity Framework classes are the following:
public class RuleGroup
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Rule> Rules { get; set; }
}
public class Rule
{
[Key]
public Guid Id { get; set; }
public Guid RuleGroupId { get; set; }
public string Name { get; set; }
public ICollection<Condition> Conditions { get; set; }
[ForeignKey("RuleGroupId")]
public virtual RuleGroup RuleGroup { get; set; }
}
[NotMapped]
public class Condition
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Class Condition is not mapped because it is being serialized and stored as JSON in Rule Table (using this example)
My DTOS are the following:
public class UpdateRuleGroupDto
{
public string Name { get; set; }
public ICollection<UpdateRuleDto> Rules { get; set; }
}
public class UpdateRuleDto
{
public string Name { get; set; }
public ICollection<UpdateConditionDto> Conditions { get; set; }
}
public class UpdateConditionDto
{
public string Name { get; set; }
}
In my Startup.cs I initialize Automapper :
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<UpdateRuleGroupDto, RuleGroup>();
cfg.CreateMap<UpdateRuleDto, Rule>();
cfg.CreateMap<UpdateConditionDto, Condition>();
}
I have an API controller endpoint that accepts JSON PATCH document to make changes to data stored in database.
public IActionResult Patch(Guid ruleGroupId, [FromBody]JsonPatchDocument<UpdateRuleGroupDto> body)
{
RuleGroup ruleGroupFromRepo = _deviceRules.GetRuleGroup(ruleGroupId);
UpdateRuleGroupDto ruleGroupToPatch = Mapper.Map<UpdateRuleGroupDto>(ruleGroupFromRepo);
// Patching logic here
Mapper.Map(ruleGroupToPatch, ruleGroupFromRepo);
context.SaveChanges();
return NoContent();
}
The problem:
When changes are made/saved, Rules in Rule table change their/get new GUID.
Example, say we have this data in 2 Tables.
RuleGroup Table
[Id][Name]
[ddad5cac-e5a1-4db7-8167-66a6de3b8a0c][Test]
Rule Table
[Id][RuleGroupId][Name][Condition]
[17c38ee8-4158-4ecc-b893-97786fa76e13][ddad5cac-e5a1-4db7-8167-66a6de3b8a0c][Test][[{"Name":"Test"}]]
If I change field [Name] to a new value, Rules Table will look like this.
Rule Table
[Id][RuleGroupId][Name][Condition]
[ba106de8-bcbc-4170-ba56-80fe619cd757][ddad5cac-e5a1-4db7-8167-66a6de3b8a0c][Test2][[{"Name":"Test"}]]
Note that [Id] field has now a new GUID.
EDIT
#Gert Arnold made me realize that I'm not attaching entities.
I ran the following code:
foreach (var item in ruleGroupFromRepo.rules)
{
var x = _context.Entry(item).State;
}
and all the states were Added and not modified. Now I just have to figure out how to do it properly.

Creating ORM-independent POCO classes

My goal is to create the poco classes without knowing which ORM is going to be used.
Now observe the following code...
public class NeuralModel
{
public NeuralModel()
{
Configurations = new HashSet<NeuralModelConfiguration>();
}
public int Id { get; set; }
public string Name { get; set; }
public DateTime? LastTrained { get; set; }
//Navigation properties
public ICollection<NeuralModelConfiguration> Configurations { get; set; }
//NotMapped properties
[NotMapped]
public NeuralModelConfiguration DefaultConfiguration { get { return Configurations.SingleOrDefault(config => config.IsDefault); } }
[NotMapped]
public bool IsTrained { get { return LastTrained.HasValue; } }
}
public class NeuralModelConfiguration
{
public NeuralModelConfiguration()
{
KeyValues = new HashSet<KeyValuePair<string, string>>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsDefault { get; set; }
public ICollection<KeyValuePair<string, string>> KeyValues
public int ModelId { get; set; }
//Navigation properties
public NeuralModel Model { get; set; }
}
Now suppose if I were to use Entity Framework Core, I get into the issue of mapping complex types (in my example it would be ICollection<KeyValuePair<string, string>>).
According to my research I came across two possible solutions:
Serialization
Another Entity
Now my question is that is there a third solution that do not require me to change my poco classes but rather do some sort of hidden processing at the dbcontext level?
Should there not be a third solution then out of the 2 available solutions which one would be considered better in terms of performance?

How to model with EntityFramework extensible fields for the entities

I have the following requirement, on my app the Entities will come with some fields, however the user needs to be able to add additional fields to the entity and then values for those fields.
I was thinking something like this but I am not sure if it would be a good approach or not.
The base class is an entity (Not sure which fields I need to add here)
public class Entidad
{
}
Then the Company Class will inherit from Entity
public class Empresa : Entidad
{
[Key]
public int Id { get; set; }
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
As you can see there is an ICollection of additional fields. that class would have the fieldname, type and id
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public Tiposcampo TipoCampo { get; set; }
}
and then the field value would be something like this:
public class ValorCampo
{
public Entidad Entidad { get; set; }
public CampoAdicional Campo { get; set; }
public string ValorTexto { get;set ; }
public int ValorNumerico { get; set; }
}
However I am not sure if this is the correct model classes for my scenario and whether it would create the tables correctly.
EF works with lazy load so at least there are several "virtual" missings.
In all properties that does not use primitive types and in collections.
Can you extend more than one entity with additional fields? If so you need that ValorCampo contains the entity (Entidad) but the entity should have the Id so you need to move the Id from Empresa to Entidad. Otherwise you need ValorCampo should refer to Empresa not to Entidad

Entity framework creating a non existent column in the query

This has been puzzling me for quite some time but I keep getting an invalid identifier error when my entity framework tries to execute an oracle query. The classes in question are the following:
public class Projectship : ModelTrackingBase
{
[Column("PROJECTID")]
public long ProjectId { get; set; }
[Column("VISITID")]
public long VisitId { get; set; }
public virtual Bpid Bpid { get; set; } //new
public virtual Visit Visit { get; set; }
}
and
public class Bpid : EntityIdBase
{
[Column("BUDPRJID")]
[MaxLength(38)]
public string BPId { get; set; }
[Column("DESCRIPTION")]
[MaxLength(255)]
public string Description { get; set; }
[Column("CSTOBJ")]
[MaxLength(255)]
public string Custobj { get; set; }
[Column("PVID")]
[MaxLength(255)]
public string Pvid { get; set; }
public virtual ICollection<Projectship> Projectships { get; set; }
public IEnumerable<Visit> Visits
{
get { return Projectships.Select(p => p.Visit); }
}
[NotMapped]
public string DisplayName
{
get { return string.Format("{0}: {1}", BPId , Description); }
}
}
Now the EntityIdBase has the following:
public class EntityIdBase : EntityBase
{
[Column("ID")]
public long Id { get; set; }
}
It tries to keep on looking for a column Bpid_Id in the query. Does someone have any idea?
Bpid_id is created by EF because it can't automatically determine the relationship. Try adding the annotation:
[ForeignKey("ID")]
public virtual Bpid Bpid { get; set; } //new
You have specified a navigation property in the Projectship class
public virtual Bpid Bpid { get; set; }
You have not specified a foreign key to go with the navigation property so Entity Framework has chosen one, and it has chosen the name Bpid_Id. And it should be in the database. It should not be "non-existent".
You will probably find it easier to use Entity Framework if you add a foreign key like this:
public int BpidId { get; set; }
References:
Why does Entity Framework Reinsert Existing Objects into My Database?
Making Do with Absent Foreign Keys

Entity framework, issue saving data in many-to-many relationship

I have issue saving data in many-to-may relationship between two tables that breaks by introducing another table in between, containing primary keys of both. I have code first existing database approach along with repository pattern and unit of work in MVC application
and here is my model classes
Navigation_Functions
public class Navigation_Functions
{
public Navigation_Functions()
{
}
[Key]
public int Function_ID { get; set; }
[StringLength(250)]
[Required(ErrorMessage = "Required Title")]
[Display(Name = "Function Title")]
public string FunctionName { get; set; }
[Required(ErrorMessage = "Required Hierarchy Level")]
[Display(Name = "Hierarchy Level")]
public int Hierarchy_Level { get; set; }
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
}
Navigation_FunctionController Model
public class Navigation_FunctionController
{
public Navigation_FunctionController()
{
}
[Key]
public int ControllerID { get; set; }
[StringLength(250)]
[Required]
public string ControllerName { get; set; }
public ICollection <Navigation_Functions> Navigation_Functions { get; set; }
}
Junction Model
[Table("Navigation_FunctionInController")]
public class Navigation_FunctionInController
{
public Navigation_FunctionInController()
{
}
[Key]
public int FunctionInController_ID { get; set; }
[Key]
[ForeignKey("Navigation_Functions")]
public int Function_ID { get; set; }
[Key]
[ForeignKey("Navigation_FunctionController")]
public int ControllerID { get; set; }
public Navigation_FunctionController Navigation_FunctionController { get; set; }
public Navigation_Functions Navigation_Functions { get; set; }
}
I have generic repository for CRUD operation
public void InsertEntity(TEntity obj)
{
_DbSet.Add(obj);
}
My ViewModel
public class FunctionsNavigation_ViewModel
{
public Navigation_Functions _Navigation_Functions { get; set; }
public Navigation_FunctionController _Navigation_FunctionController { get; set; }
}
public void CreateFunctionNavigation(FunctionsNavigation_ViewModel _obj)
{
using (var _uow = new FunctionsNavigation_UnitOfWork())
{
try
{
var _navigationFunction = _obj._Navigation_Functions;
_navigationFunction.Navigation_FunctionController = new List<Navigation_FunctionController>();
_navigationFunction.Navigation_FunctionController.Add(_obj._Navigation_FunctionController);
_uow.Navigation_Functions_Repository.InsertEntity(_navigationFunction);
_uow.Save();
}
catch
{
}
}
}
if I remove following line from above code then it save new Navigation_Functions
_navigationFunction.Navigation_FunctionController.Add(_obj._Navigation_FunctionController);
following is screen shot from debug code.
I am wondering if my ViewModel are correct? secondly How Entity Framework knows that it need to put primary keys of two tables in Navigation_FunctionInController?
When your model looks like this...
public class Navigation_Functions
{
...
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
public class Navigation_FunctionController
{
...
public ICollection <Navigation_Functions> Navigation_Functions { get; set; }
}
...so without a Navigation_FunctionInController class, the junction table in the database (Navigation_FunctionInController) is not represented in the class model. If you have this model code first, EF will create a junction table itself. If you work database first, EF won't create a junction class and in the diagram you will see a pure many to many association, like this: *--*. But this only happens if the junction table only contains the two foreign keys, both of which comprise a compound primary key.
In your model you have an explicit junction class (probably because the table has a primary key field besides the two foreign keys). This means that the many to many association turns into a 1:n:1 association. The class model should essentially look like this...
public class NavigationFunction
{
...
public ICollection<NavigationFunctionInController> NavigationFunctionInControllers { get; set; }
}
public class NavigationFunctionController
{
...
public ICollection <NavigationFunctionInController> NavigationFunctionInControllers { get; set; }
}
public class NavigationFunctionInController
{
public int FunctionInControllerID { get; set; }
[ForeignKey("NavigationFunction")]
public int FunctionID { get; set; }
[ForeignKey("NavigationFunctionController")]
public int ControllerID { get; set; }
public NavigationFunctionController NavigationFunctionController { get; set; }
public NavigationFunction NavigationFunction { get; set; }
}
Note that the foreign key fields don't have to be part of the primary key (also not that I prefer the singular name Navigation_Function and plural names for collections, and no underscores in names).
So what happens in your code is that you have a 1:n:1 association, but you try to manage it like a m:n association by adding items directly to _navigationFunction.Navigation_FunctionController(s) . But EF doesn't track that collection, and the items are not saved.
Instead you have to create a junction class instance...
var nfic = new NavigationFunctionInController
{
NavigationFunction = obj.NavigationFunction,
NavigationFunctionController = obj.Navigation_FunctionController
};
...and save it through your repositories and UoW.

Categories

Resources