I have a view model which consists of data that has been flatten in order to populate a Pivot Grid.
This works fine however now I need a way to write values back to the database entity.
The entity has 3 composite properties, which make each row unique, JobLevelId, GenderId, RaceId.
The data hydrated into the view model is grouped by the JobLevel.
I need to find a way to deconstruct the view model and not sure the best way to do this e.g. if MaleAfrican has a value of 30, then this would need to be saved in the database as
new EmploymentValue{ JobLevelId = 1, GenderId = 1, Race = 1, Value = 30 }
Lookup Tables:
Job Levels
| ID | Description |
| -- | ----------- |
| 1 | Senior |
| 2 | Mid |
| 3 | Junior |
Gender
| ID | Description |
| -- | ----------- |
| 1 | Male |
| 2 | Female |
| 3 | Other |
Race
| ID | Description |
| -- | ----------- |
| 1 | Blue |
| 2 | Red |
| 3 | Green |
View Model:
public class EmploymentValueViewModel
{
public Guid Id => Guid.NewGuid();
public string JobLevel { get; set; }
public int MaleBlue { get; set; }
public int MaleRed { get; set; }
public int MaleGreen { get; set; }
public int FemaleBlue { get; set; }
public int FemaleRed { get; set; }
public int FemaleGreen { get; set; }
public int OtherBlue { get; set; }
public int OtherRed { get; set; }
public int OtherGreen { get; set; }
}
Database Entity:
[Table("EmploymentValues")]
public class EmploymentValue
{
[Required]
[Column("fkJobLevelID", Order = 1)]
public int? JobLevelId { get; set; }
public virtual JobLevel OccupationalLevel { get; set; }
[Required]
[Column("fkRaceID", Order = 2)]
public int RaceId { get; set; }
public virtual Race Race { get; set; }
[Required]
[Column("fkGenderID", Order = 3)]
public int GenderId { get; set; }
public virtual Gender Gender { get; set; }
[Required]
public int? Value { get; set; }
}
Related
Database in Oracle - View:
COLUMN_NAME | DATE_TYPE
___________________________________________________
UMOWA | VARCHAR2(30)
RODZ_LEAS | VARCHAR2(1)
KTH | NUMBER(10)
SKRÓT | VARCHAR2(50)
NAZWA | VARCHAR2(240)
AMORTYZ | NUMBER(10,6)
WALUTA | VARCHAR2(3)
WARTOSC_UMW | NUMBER
LICZBA_RAT | NUMBER
WK | NUMBER
WK% | NUMBER
Rodzaj stopy baz.| VARCHAR2(2000)
Wart.stopy baz. | NUMBER
REFI | NUMBER
RA_NR_RATY | NUMBER(10)
RA_TYP | VARCHAR2(10)
RA_DATA_SPLATY | DATE
RA_KWOTA_NETTO | NUMBER(12,2)
RA_KAPITAL_POZOSTALY | NUMBER(12,2)
Procedure in SQL Server - it works - I get the data from Oracle.
CREATE PROCEDURE [egeria].[ContractData_P] (#ContractNumberPar varchar(20))
AS
EXECUTE(
'Select
Umowa ContractNumber
, Rodz_leas TypeOfLeasing
, Kth CodeClient
, skrót Short
, Nazwa NameOfClient
, amortyz Amortization
, Waluta Currency
, Wartosc_Umw ContractValue
, Liczba_rat NumberOfInstallments
, WK FinalValue
, "WK%" FinalValuePercent
, "Rodzaj stopy baz." TypeOfBaseRate
, "Wart.stopy baz." ValueOfBaseRate
, REFI
, ra_nr_raty InstallmentNumber
, ra_typ Type
, ra_data_splaty MaturityDate
, ra_kwota_netto NetAmount
, ra_kapital_pozostaly CapitalRemaining
from RAPADM.XLS_SKROCENIA_UMOW_V
where UMOWA = ?'
, #ContractNumberPar) AT EF_EG5PROD
GO
Model
public class ContractData
{
public string ContractNumber { get; }
public string TypeOfLeasing { get; }
public int CodeClient { get; }
public string Short { get; }
public string NameOfClient { get; }
public decimal Amortization { get; }
public string Currency { get; }
public decimal ContractValue { get; }
public decimal NumberOfInstallments { get; }
public decimal FinalValue { get; }
public decimal FinalValuePercent { get; }
public string TypeOfBaseRate { get; }
public decimal ValueOfBaseRate { get; }
public decimal REFI { get; }
public decimal InstallmentNumber { get; }
public string Type { get; }
public DateTime MaturityDate { get; }
public decimal NetAmount { get; }
public decimal CapitalRemaining { get; }
}
Context
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
public virtual DbSet<ContractData> ContractData { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ContractData>().HasNoKey();
}
}
ContractDataService -- HERE I HAVE ERROR
private readonly AppDbContext _context;
public ContractDataService(AppDbContext context)
{
_context = context;
}
public async Task<IEnumerable<ContractData>> ExecAsync(string contractNumberPar)
{
var data = _context.ContractData.FromSqlRaw("EXECUTE [egeria].[ContractData_P] #ContractNumberPar", contractNumberPar).AsEnumerable();
return data;
}
Error:
system.InvalidOperationException: Sequence contains no elements at
System.Linq.ThrowHelper.ThrowNoElementsException() at
System.Linq.Enumerable.Max(IEnumerable`1 source) at
Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitBinary(BinaryExpression
binaryExpression) at
System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at
System.Dynamic.Utils.ExpressionVisitorUtils.VisitBlockExpressions(ExpressionVisitor
visitor, BlockExpression block) at
System.Linq.Expressions.ExpressionVisitor.VisitBlock(BlockExpression
node) at
System.Linq.Expressions.BlockExpression.Accept(ExpressionVisitor
visitor) at System.Linq.Expressions.ExpressionVisitor.Visit(Expression
node) at
Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression
extensionExpression) at
System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) at
Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression
shaperExpression, RelationalCommandCache& relationalCommandCache,
LambdaExpression& relatedDataLoaders, Int32& collectionId) at
Does anyone have an idea how to do this? Should I go another way?
Please check if what I am doing has any sense, or is it not better to go a better way?
EF Core cannot create projection expression because all properties of ContractNumber are readonly. Add setters to solve your issue:
public class ContractData
{
public string ContractNumber { get; set; }
public string TypeOfLeasing { get; set; }
public int CodeClient { get; set; }
public string Short { get; set; }
public string NameOfClient { get; set; }
public decimal Amortization { get; set; }
public string Currency { get; set; }
public decimal ContractValue { get; set; }
public decimal NumberOfInstallments { get; set; }
public decimal FinalValue { get; set; }
public decimal FinalValuePercent { get; set; }
public string TypeOfBaseRate { get; set; }
public decimal ValueOfBaseRate { get; set; }
public decimal REFI { get; set; }
public decimal InstallmentNumber { get; set; }
public string Type { get; set; }
public DateTime MaturityDate { get; set; }
public decimal NetAmount { get; set; }
public decimal CapitalRemaining { get; set; }
}
I have configured my connection to DB as follows:
Web.config
<add name="MyContext" providerName="System.Data.SqlClient" connectionString="Data Source=MyServer;Initial Catalog=MyDB;User id=MyId;Password=MyPassword; MultipleActiveResultSets=true" />
Then I have my class to create context in order to access Database
public class MyContext: DbContext
{
public MyContext() : base("MyContext")
{
Database.SetInitializer<Models.MyContext>(null);
}
public DbSet<MyModel> MyTable { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
Then I have my DB with table 'MyTable', which is a history table, so every day at 6:00 I am inserting data from another table, finally I will have results like this
Id | Date
-----------------------------
1 | 2018-05-30 16:01:00.332
1 | 2018-05-31 14:21:03.456
1 | 2018-06-01 11:45:01.316
2 | 2018-05-30 21:44:00.544
2 | 2018-05-31 22:45:00.987
2 | 2018-06-01 23:46:00.769
So now in MyController.cs
public IQueryable<MyModel> GetData(string Id)
{
var result = MyContext.MyTable.AsQueryable();
return result.Where(w => w.Id == Id).toList();
}
I am trying to return all rows for Id = 1, on debugging when I set a breakpoint in the return statement, it returns the same row, for every record.
Id | Date
-----------------------------
1 | 2018-05-30 16:01:00.332
1 | 2018-05-30 16:01:00.332
1 | 2018-05-30 16:01:00.332
Edit: I add MyModel class as you requested
public class MyModel
{
[Index(IsUnique = false)]
public int Id { get; set; }
public DateTime Date{ get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public string Field5 { get; set; }
public string Field6 { get; set; }
public string Field7 { get; set; }
public DateTime? Field8 { get; set; }
public string Field9 { get; set; }
public string Field10 { get; set; }
public string Field11 { get; set; }
public string Field12 { get; set; }
public string Field13 { get; set; }
public string Field14 { get; set; }
public string Field15 { get; set; }
public string Field16 { get; set; }
public string Field17 { get; set; }
public string Field18 { get; set; }
public string Field19 { get; set; }
public int? Field20 { get; set; }
public string Field21 { get; set; }
public int? Field22 { get; set; }
public int? Field23 { get; set; }
public int? Field24 { get; set; }
public string Field25 { get; set; }
public string Field26 { get; set; }
public string Field27 { get; set; }
public int Field28 { get; set; }
public string Field29 { get; set; }
public string Field30 { get; set; }
public string Field31 { get; set; }
public string Field32 { get; set; }
public string Field33 { get; set; }
public DateTime? Field34 { get; set; }
}
Can you help me to figure out what's going on?
Thanks.
[Index(IsUnique = false)]
public int Id { get; set; }
This does not prevent Id from being the Primary Key. If it did you would have seen another error, EF demands a PK on your classes.
You have only added an extra Index on the Id column. Not removed the PK constraint.
So our first suspicions appear to be right, you load a bunch of records with duplicate PK values in memory. When you try SaveChanges() you will get an error or lose data.
A simple fix would look like
public class MyModel
{
[Key]
public int ModelId { get; set; } // auto filled, ignore where you don't need it
//[Index(IsUnique = false)] -- not needed
public int Id { get; set; }
public DateTime Date{ get; set; }
...
Table [Note]
Id int
Description nvarchar(255)
ClassName nvarchar(50)
ClassId int
Table [User]
Id int
FullName nvarchar(255)
Enable bit
Address nvarchar(255)
Email nvarchar(255)
Table [Client]
Id int
CompanyName nvarchar(255)
PhoneNumber nvarchar(255)
Address nvarchar(255)
Email nvarchar(255)
The following classes:
public class User{
public int Id { get; set; }
public string FullName { get; set; }
public bool Enable { get; set; }
public string Address { get; set; }
public string Email { get; set; }
}
public class Client{
public int Id { get; set; }
public string CompanyName { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
public string Email { get; set; }
}
public class Note{
public int Id { get; set; }
public string Description { get; set; }
public string ClassName { get; set; }
public int ClassId { get; set; }
public virtual User { get; set; } <-- Populate depending on the ClassName and ClassId fields
public virtual Client { get; set; } <-- Populate depending on the ClassName and ClassId fields
}
Table NOTE example
╔════╦═════════════╦═══════════╦═════════╦
║ id ║ Description ║ ClassName ║ ClassId ║
╠════╬═════════════╬═══════════╬═════════╬
║ 1 ║Test Note 1 ║ Client ║ 2544 ║
╠════╬═════════════╬═══════════╬═════════╬
║ 2 ║Test Note 2 ║ User ║ 25 ║
╠════╬═════════════╬═══════════╬═════════╬
║ 3 ║Test Note 3 ║ Client ║ 2578 ║
╚════╩═════════════╩═══════════╩═════════╩
Note that in the note class there are two objects
public virtual User { get; set; }
public virtual Client { get; set; }
What I need to do is to map in the table Note one of the two tables User or Client, depending on the ClassName field in the database. So, the entity should know in which table to find the information.
If in the ClassName field is "Client" it will populate the Client object if it is "User" it should populate the User object.
Is it possible to do this using Entity Framework?
Edited
I am trying to set up a many-to-one relationship on two tables I have got but I am unable to manage to make it work. I tried different options but I end up retrieving records of only one table.
Below is my table structure:
Customer
[Table("Customer")]
public class CustomerList
{
[Key]
public int CustomerID { get; set; }
public int Number { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public DateTime? DateOfBirth { get; set; }
public int? MembershipTypeID { get; set; }
public MembershipTypeList MembershipType { get; set; }
public string Name;
public string EmailAddress { get; set; }
}
MembershipType
[Table("MembershipType")]
public class MembershipTypeList
{
[Key]
public int? MembershipTypeID { get; set; }
public string Name { get; set; }
//public virtual CustomerList Customer { get; set; }
}
Result
CustomerID 56
Number 52
Surname Antonelli
Forename Renny
DateOfBirth 1945-02-19
MembershipTypeID 1
EmailAddress Mr#test.com
Name NULL
Expected
CustomerID 56
Number 52
Surname Antonelli
Forename Renny
DateOfBirth 1945-02-19
MembershipTypeID 1
EmailAddress Mr#test.com
Name Blue ---> from membershiptype table
I have a table that looks like this :-
CategoryId | QuestionId
-----------------------
1 | 2
1 | 3
3 | 2
4 | 3
I need to pull out all of the Questions that are not in use by a specific category. so for eg, CategoryID = 1.
The result should be that there are no questions to display.
Anyone know the best way to do this? so far i've not got anywhere with it.
EDIT **
public partial class FAQ
{
public FAQ()
{
this.FAQCategoriesFAQs = new HashSet<FAQCategoriesFAQ>();
}
public int Id { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
public bool IsVisible { get; set; }
public Nullable<System.DateTime> DateLastUpdated { get; set; }
public System.DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
public virtual ICollection<FAQCategoriesFAQ> FAQCategoriesFAQs { get; set; }
}
public partial class FAQCategory
{
public FAQCategory()
{
this.FAQCategoriesFAQs = new HashSet<FAQCategoriesFAQ>();
}
public int Id { get; set; }
public string Name { get; set; }
public int DomainId { get; set; }
public Nullable<System.DateTime> DateLastUpdated { get; set; }
public System.DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
public int SortOrder { get; set; }
public virtual Domain Domain { get; set; }
public virtual ICollection<FAQCategoriesFAQ> FAQCategoriesFAQs { get; set; }
}
public partial class FAQCategoriesFAQ
{
public int FAQCategoryId { get; set; }
public int FAQId { get; set; }
public int SortOrder { get; set; }
public virtual FAQCategory FAQCategory { get; set; }
public virtual FAQ FAQ { get; set; }
}
If you have navigation property for categories in Question entity:
db.Questions.Where(q => !q.Categories.Any(c => c.Id == id))
UPDATE Thus your junction table differs a little bit from your original question :)
db.FAQs.Where(q => !q.FAQCategoriesFAQs.Any(qc => qc.FAQCategoryId == id))