EntityFreamwork full entity and lite entity - c#

i have table users
user table :
Id, Name , firstName , password , email , address , dateofBrith
i want to create two entity for user table one lite and other full
[Table("user")]
public class LiteUser
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
second entity
public class fullUser : LiteUser
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
but not I get error about no column discriminator
is possible to do somthing like my entity are same but one have more filed then the other entity
thank you in advance for help

Unfortunately, no. You can only define one entity to one table. Instead, you'd have to do a manual .Select off of the full entity to return a custom "Lite" entry because EF needs to know all the columns that tie to a specific table from the start.
Edit: The only way around this would be to create a view and map to that instead.

You can do something like this
[Table("user")]
public class LiteUser
{
public string Name {get;set;}
public int firstName{get;set;}
}
public class fullUser : LiteUser
{
public int ID {get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Use primary key public int ID {get;set;} value in the derived class

As Daniel points out, a table can be associated to a single entity definition, outside of Table Per Hierarchy inheritance, which isn't what you are looking for.
This was an old trick I used with NHibernate which isn't supported in EF.
With EF you can utilize Linq and ViewModels to avoid the need of Lite vs. Full models.
Given:
//Entity
public class User
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
// View Models...
public class LiteUserViewModel
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
public class FullUserViewModel : LiteUserViewModel
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Querying..
//Give me a list of lite data..
var viewModels = context.Users
.Where(x => x.DateOfBirth < startDate)
.Select(x => new LiteUserViewModel
{
UserId = x.UserId,
Name = x.Name,
FirstName = x.FirstName
}).ToList();
// Give me a full user.
var viewModel = context.Users
.Where(x => x.UserId = userId)
.Select(x => new FullUserViewModel
{
UserId = x.UserId,
// ... etc ...
}).SingleOrDefault();
You can leverage libraries like AutoMapper to handle mapping entity to view model. In cases where you just want to inspect data you don't need to define a view model / DTO, just use an anonymous type. The end result is the same in that EF will execute an optimized query to just return back the data you want rather than entire entities. You can optimize view models to flatten down hierarchical data using this technique. You do need to ensure that any methods or transformations in the .Select() are pure and EF compatible because EF will attempt to translate and pass those to SQL. More complex transformations should be done in the view model itself, or utilize an anonymous type select of the raw data, followed by a ToList/Single/etc. then .Select() into the view model with appropriate transformations via Linq2Object.

One option is to use table splitting which is when you map a single table to two or more entities. The difference with your requested solution is that the "additional" properties in the "full" configuration will be represented by another entity type. Example (for EF Core; EF6 will be very similar):
public class SplitTablePrincipal
{
[Key]
public int Id { get; set; }
public string PrincipalProperty { get; set; }
// principal entity has a nav property to the dependent entity
public virtual SplitTableDependent Dependent { get; set; }
}
public class SplitTableDependent
{
[Key]
public int Id { get; set; }
public string DependentProperty { get; set; }
}
public class SplitTablePricipalConfiguration : IEntityTypeConfiguration<SplitTablePrincipal>
{
public void Configure( EntityTypeBuilder<SplitTablePrincipal> builder )
{
//builder.HasKey( pe => pe.Id );
// establish 1:? relationship w/ shared primary key
builder.HasOne( pe => pe.Dependent )
.WithOne()
.HasForeignKey<SplitTableDependent>( de => de.Id ); // FK is PK
builder.ToTable( "YourTableName" );
}
}
public class SplitTableDependentConfiguration : IEntityTypeConfiguration<SplitTableDependent>
{
public void Configure( EntityTypeBuilder<SplitTableDependent> builder )
{
//builder.HasKey( de => de.Id );
// map dependent entity to same table as principal
builder.ToTable( "YourTableName" ); // same table name
}
}
You only need to include a DbSet for the SplitTablePrincipal entity type in your DbContext. When querying, the Dependent property will not be populated by default (your "lite" configuration); you would need to eager load the property for the "full" data configuration via .Include( stp => stp.Dependent ). You could also lazy load or explicitly load the Dependent property further down the line should you so choose. For example:
dbContext.Entry( principalEntity ).Reference( p => p.Dependent ).Load();

Related

EF - cast int(short) to string with copability Linq expression

Default DTO model class:
public class Test
{
public short Id { get; set; }
public string Name { get; set; }
}
Legacy DB with name type as Number(5).
CREATE TABLE SCHEME.TABLE_NAME
(
ID NUMBER(5) NOT NULL,
NAME NUMBER(5) NOT NULL
)
Example:
public dynamic GetResult(string searchString)
{
return DbContext.Set<Test>()
.Where(x => x.Name.Contains(searchString))
.FirstOfDefault();
}
How to create mapping for field Test for available query Entity Framework? Because without normal mapping EF mean string type as normally but cannot create normally query SQL to get same data.

Many-to-many relationship in EF user instance is NULL [duplicate]

This question already has answers here:
EF Core returns null relations until direct access
(2 answers)
Closed 5 years ago.
I'm using .net core 2 mvc, I tried to build many-to-many relationship between Users and Steps.
the relationship is doen but when I query for the record I get user = null.
Hier is my code:
(applicationUser model):
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public List<StepsUsers> StepUser { get; set; }
}
(Steps model):
public class Steps
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<StepsUsers> StepUser { get; set; }
}
StepsUsers model:
public class StepsUsers : IAuditable
{
public int StepId { get; set; }
public Steps Step { get; set; }
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
In DbContext I did this :
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<StepsUsers>()
.HasKey(s => new { s.StepId, s.UserId });
builder.Entity<StepsUsers>()
.HasOne(su => su.Step)
.WithMany(s => s.StepUser)
.HasForeignKey(su => su.StepId);
builder.Entity<StepsUsers>()
.HasOne(su => su.User)
.WithMany(s => s.StepUser)
.HasForeignKey(su => su.UserId);
}
public DbSet<MyApp.Models.StepsUsers> StepsUsers { get; set; }
Now, when I query for an instance of StepsUsers with specific StepId I get all de fields correct except the User field is null
var stepUsers = await _context.StepsUsers.Where(s => s.StepId == id).ToListAsync();
I did the same code for another two tables and it works fine, I don't know why it is like this, any suggestion 1?
The cause of your problems is that your forgot to declare your To-many relations as virtual. Another improvement would be to declare them as virtual ICollection instead of List. After all, what would ApplicationUser.StepUser[4] mean?
If you configure a many-to-many relationship according to the entity framework conventions for many-to-many, you don't need to mention the junction table (StepsUsers). Entity framework will recognize the many-to-many and will create the junction table for you. If you stick to the code first conventions you won't even need the fluent API to configure the many-to-many.
In your design every ApplicationUser has zero or more Steps and every Step is done by zero or more ApplicationUsers.
class ApplicationUser
{
public int Id {get; set;}
// every ApplicationUser has zero or more Steps:
public virtual ICollection<Step> Steps {get; set;}
public string Name {get; set;}
...
}
class Step
{
public int Id {get; set;}
// every Step is performed by zero or more ApplicationUsers:
public virtual ICollection<ApplicationUser> ApplicationUsers {get; set;}
public string Name {get; set;}
...
}
public MyDbContext : DbContext
{
public DbSet<ApplicationUser ApplictionUsers {get; set;}
public DbSet<Step> Steps {get; set;}
}
This is all entity framework needs to know to recognize that you configured a many-to-many relationship. Entity framework will create the junction table for you and the foreign keys to the junction table. You don't need to declare the junction table.
But how am I suppose to do a join if I don't have the junction table?
The answer is: Don't do the join. Use the collections instead.
If you want all ApplicationUsers that ... with all their Steps that ... you would normally do an inner join with the junction table, and do some group by to get the Application users. Ever tried method syntax to join three tables? They look hideous, difficult to understand, error prone and difficult to maintain.
Using the collections in entity framework your query would be much simpler:
var result = myDbContext.ApplicationUsers
.Where(applicationUser => applicationUser.Name == ...)
.Select(applicationUser => new
{
// select only the properties you plan to use:
Name = applicationUser.Name,
Steps = applicationUser.Steps
.Where(step => step.Name == ...)
.Select(step => new
{
// again fetch only Step properties you plan to use
Name = step.Name,
...
})
.ToList(),
});
Entity framework will recognize that joins with the junction table is needed and perform them for you.
If you want Steps that ... with their ApplicationUsers who ... you'll do something similar:
var result = myDbContext.Steps
.Where(step => ...)
.Select(step => new
{
Name = step.Name,
... // other properties
ApplicationUsers = step.ApplicationUsers
.Where(applicationUser => ...)
.Select(applicationUser => new
{
...
})
.ToList(),
});
In my experience, whenever I think of performing a query with a of DbSets using entity framework, whether it is in a many-to-many, a one-to-many or a one-to-one relation, the query can almost always be created using the collections instead of a join. They look simpler, they are better to understand and thus better to maintain.

EF won't update tables based on my model

At first I created a Person model which only contained the basic properties:
[Table("SGDB_Persons")]
public class Person {
public int Id { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
[Required]
public Department Department { get; set; }
[Required]
public SourceType SourceType { get; set; }
After I noticed I'm missing something I've added a new PersonData Property:
[Required]
public PersonData PersonData { get; set; }
Unfortunately EF won't update the Database at all - PersonData which at first contained an object of type Person got updated so there is no Person property anymore. On the other hand, EF does not create a new Column for PersonData_Id.
Additionally the ID column is not auto-incrementing (all other table's Id Column do). What's confusing me is the following Constraing which gets created inside my Person table:
CONSTRAINT [FK_dbo.SGDB_Persons_dbo.SGDB_PersonData_Id] FOREIGN KEY ([Id]) REFERENCES [dbo].[SGDB_PersonData] ([Id])
I tried everything (at least I think so). I dropped all tables / the whole database manually, reinstalled EF, executed manual migrations but nothing seems to work.
I think it's this problem which causes me to not be able to seed my database with the following code:
protected override void Seed(PersonContext context) {
base.Seed(context);
var dep = new DepartmentContext().Departments.First();
var status = new Status("Test");
var persondata = new PersonData(status);
context.Status.Add(status);
context.PersonData.Add(persondata);
for (int i = 0; i < 10; i++) {
var person = new Person {
Firstname = $"TestPersonFirstname{i}",
Lastname = $"TestPersonLastname{i}",
SourceType = COM.SourceType.Manual,
Department = dep,
PersonData = persondata
};
context.Persons.Add(person);
}
context.SaveChanges();
}
Everytime this code get's executed I'm getting an Exception:
The member with identity 'SGDB.DAL.Contexts.Person_Department' does not exist in the metadata collection. Parameter name: identity.
I don't know if both problems are related to the same problem but both need to be resolved :)
Thanks in advance!
Update 1
My Solution is divided into a few different Projects:
BLL, DAL, COM, UI
DataContexts are located inside the DAL project, Models inside the COM project.
Department Model:
[Table("SGDB_Departments")]
public class Department {
public int Id { get; set; }
[Required]
public string Costcenter { get; set; }
[Required]
public string Abbreviation { get; set; }
public string Description { get; set; }
public string FullDepartmentName {
get {
return $#"{Division.Abbreviation}\{Abbreviation}";
}
}
[Required]
public virtual Division Division { get; set; }
}
PersonData Model:
[Table("SGDB_PersonData")]
public class PersonData {
public PersonData(Status status) {
Status = status;
}
public int Id { get; set; }
public DateTime Limit { get; set; }
public Person Responsible { get; set; }
[Required]
public Status Status { get; set; }
}
The Person table (as you can see) has got a Department_Id column (EF inserted automatically).
Clarification
A Person object contains a PersonData object as additional Information for this Person. A Person may / may not has a Responsible Person (so PersonData.Responsible is not a Navigation Property to the Parent Person).
Additionaly if possible I don't want to have a foreign Key inside the PersonData table.
As I figured out I'd have to modify
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal(e => e.Responsible)
.WillCascadeOnDelete(true);
to
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithMany()
.WillCascadeOnDelete(true);
I'll try this and report in if it solved my problem.
Update 2
The member with identity 'SGDB.DAL.Contexts.Person_Department' does not exist in the metadata collection.
Your model defines one-to-one relationship between Person and PersonData with later being required and the former - optional. EF always uses the required side of the one-to-one relationship as principal and optional part as dependent. Hence it thinks PersonaData is the principal and Person - dependent and reflects that in database table design.
You need the opposite and also both sides being required. When both sides are required or optional, EF cannot automatically derive the principal/dependent side and there is no way to specify that via data annotations (attributes), so you need a fluent API setup.
Override your DbContext OnModelCreating and add something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal(e => e.Responsible)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
What it does is to tell EF that both sides of the Person->PersonData relationship are required and Person is the principal. This should make again your Person.Id column auto-increment and should resolve the person-data part of the problem.
The other thing I've noticed is this line:
var dep = new DepartmentContext().Departments.First();
while all other parts of the same procedure are using a variable called context. This might/might not be a problem, just check it out.
UPDATE: From the clarification in the updated question it turns out you have two relationships between Person and PersonData, so you need separate configuration for each of them like this:
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal()
.WillCascadeOnDelete(true);
modelBuilder.Entity<PersonData>()
.HasOptional(e => e.Responsible)
.WithOptionalDependent() // or WithMany()
.WillCascadeOnDelete(false);
Please note that there is no way to not introduce additional FK column in the PersonData table. It's needed to represent the Responsible relation, so you'll end up with a table column called Responsible_Id.

Entity Framework - set model property value dynamically from a query

I have the following models:
public class A_DTO
{
[Key]
public string Id { get; set; }
**public virtual B_DTO B { get; set; }**
public virtual List<B_DTO> Bs { get; set; }
}
public class B_DTO
{
[Key]
public string Id { get; set; }
public string AId { get; set; }
public string UserId {get; set; }
[ForeignKey("AId"]
public virtual A_DTO A { get; set; }
[ForeignKey("UserId"]
public virtual User User { get; set; }
}
I am trying to get a list of object A_DTO but also including property B:
using AutoMapper.QueryableExtensions;
public IQueryable<A_DTO> GetAllA_DTO()
{
string userId = "8b6e9332-7c40-432e-ae95-0ac052904752";
return context.A_DTO
.Include("Bs")
.Include("B")
.Project().To<A_DTO>()
.Where(a => a.Bs.Any(b => b.UserId == userId));
}
How do I dynamically set this property according to set UserId and A_DTO.Id?
Here is a bag of observations in which you may be lucky enough to find your solution:
The B property in a code first model will result in there being a foreign key in the database table for A_DTOs that contains a reference to the B_DTOs table. Entity Framework will expect to own the responsibility for filling the B navigation property with an object populated with the data from the referenced row in the B_DTOs table, hence you would not be able to change it dynamically.
There is no need to use the Automapper Project method if your source type and destination type are the same. In your example they would both appear to be A_DTO. Are you sure you don't actually intend to have an entity "A" that is included in the context and "A_DTO" that is mapped from "A" via Automapper? If that is what you really want then you could have code in a .Select call mapping A.Bs.FirstOrDefault(b => b.UserId == userId) to A_DTO.B. However, you would not be able to apply filtering on the basis of the userId in an Automapper map.
Without seeing any of the Automapper Map setup code, it is difficult to get an idea of intent here.
As an aside, when using .Include it is better, in my opinion, to use the overload that takes an expression. In your case the includes would be rewritten:
.Include(a => a.B)
.Include(a => a.Bs)
Using this overload ensures that you will get compile time errors if you rename a property but fail to update the string in the .Include statement.

EF6: Code First Complex Type

I'm having trouble getting entity framework to flatten my domain entity classes with Value Objects (complex type) fields to one table.
Everything works if I tell my model builder to ignore my value objects/complex type, but that results in all the attributes of the value object being missed in my tables. As soon as I remove the ignore statement i get "A value shared across entities is created in more than one location". If I look in the resulting CE SQL file I see an additional table named after my Domain class appended with a 1 and containing only the Value Object parameters.
Some Code:
My domain Classes:
public User {
private User(){}
public long Id {get; private set;} // dont ask, inherited legacy database
public string UserId { get; private set; }
public string Domain { get; private set; }
public AuditIformation AuditDetails {get ; private set;}
//..domain logic etc
}
public AuditInformation : IValueObject {
public long CreatedByUserId { get; private set; }
public DateTime CreatedDate { get; private set; }
}
My repository project (going code first) has got this:
public partial class myContext : DbContext {
protected override void OnModelCreating(DbModelBuilder mb) {
mb.Conventions.Remove<PluralizingTableNameConvention>();
mb.ComplexType<Domain.Model.AuditInformation>();
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedDate).HasColumnName("Created_On");
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedByUserId).HasColumnName("Created_By");
//This line lets everything work but doesn't include my
//AuditInformation attributes in my User Table.
mb.Ignore<Domain.Model.AuditInformation>(); // <== I think I need to remove this
//..
mb.Entity<User>().Map(a => {
a.Property(x => x.Id).HasColumnName("Id");
a.Property(x => x.UserId).HasColumnName("User_Id");
a.Property(x => x.Domain).HasColumnName("User_Dmain");
})
.HasKey(x => x.Id)
.ToTable("Tbl_User"); //<==Again, dont ask
}
}
What I want to get is a table looking like:
[TBL_USER]
ID AS BIGINT,
USER_ID as VARCHAR(MAX),
USER_DMAIN AS VARCHAR(MAX),
CREATED_ON as DATE,
CREATED_BY as BIGINT
But what im getting is only:
[TBL_USER]
ID AS BIGINT,
USER_ID as VARCHAR(MAX),
USER_DMAIN AS VARCHAR(MAX),
and if I remove the ignore line i get this bonus freak table
[USER1] <<==Note, named after the domain class, not the destination table..
ID AS BIGINT,
CREATED_ON as DATE,
CREATED_BY as BIGINT
and a whole bunch of error when I try to use my repository:
----> System.Data.Entity.Infrastructure.DbUpdateException : A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
----> System.Data.Entity.Core.UpdateException : A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
----> System.ArgumentException : An item with the same key has already been added.
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.
Ive done a lot of searching but I just cant find any concrete examples of persisting my value object attributes into the tables created for my domain objects. Can someone show me where I'm going wrong?
Try this:
public class AuditInformation
{
public long CreatedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
}
public abstract class AuditInfo
{
public AuditInformation AuditDetails { get; set; }
public AuditInfo()
{
this.AuditDetails = new AuditInformation();
this.AuditDetails.CreatedByUserId = 0;
this.AuditDetails.CreatedDate = DateTime.Now;
}
}
public User : AuditInfo
{
private User(){}
public long Id {get; private set;} // dont ask, inherited legacy database
public string UserId { get; private set; }
public string Domain { get; private set; }
//..domain logic etc
}
public partial class myContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Conventions.Remove<PluralizingTableNameConvention>();
mb.ComplexType<Domain.Model.AuditInformation>();
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedDate).HasColumnName("Created_On");
mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedByUserId).HasColumnName("Created_By");
mb.Entity<Cricketer>().Map(a =>
{
a.Property(x => x.Id).HasColumnName("Id");
a.Property(x => x.UserId).HasColumnName("User_Id");
a.Property(x => x.Domain).HasColumnName("User_Dmain");
a.Property(x => x.AuditDetails.CreatedByUserId).HasColumnName("CreatedByUserId");
a.Property(x => x.AuditDetails.CreatedDate).HasColumnName("CreatedDate");
})
.HasKey(x => x.ID)
.ToTable("Tbl_User"); //<==Again, dont ask
}
}

Categories