What the best way of translating created user name with ef core 3.1 - ef-core-3.1

public class Entity
{
public string Id { get; set; }
public string CreatedBy { get; set; }
public string LastUpdatedBy { get; set; }
[NotMapped]
public string Creator { get; set; }
[NotMapped]
public string Updater { get; set; }
}
public class User
{
public string Name { get; set; }
public string Id { get; set; }
}
I am going to search Entities, sort on Creator/Updater properties(and return UserInfo.Name) with ef core query, any idea?

After hours of researches, refers
How do you perform a left outer join using linq extension methods
Entity Framework Join 3 Tables
There 3 ways in oder(presonal perfer the first than second):
class Program
{
static void Main()
{
using TenantDBContext dbContext = new TenantDBContext("PORT=5432;DATABASE=linqtosql;HOST=xxx.com;PASSWORD=xxx;USER ID=postgres;Pooling=true;Minimum Pool Size=10;Application Name=xxx");
var result = (
from entity in dbContext.Entities
join user in dbContext.Users on entity.CreatedBy equals user.Id into temp1
from ce in temp1.DefaultIfEmpty()
join user1 in dbContext.Users on entity.UpdatedBy equals user1.Id into temp2
from cu in temp2.DefaultIfEmpty()
select new Entity() { Id = entity.Id, CreatedBy = entity.CreatedBy, UpdatedBy = entity.UpdatedBy, Creator = ce.Name, Updater = ce.Name }
).ToList();
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
var result2 = dbContext.Entities
.GroupJoin(dbContext.Users, e => e.CreatedBy, u => u.Id, (e, u) => new { Entity = e, User = u })
.SelectMany(eUser => eUser.User.DefaultIfEmpty(), (e, u) => new Entity() { Id = e.Entity.Id, CreatedBy = e.Entity.CreatedBy, UpdatedBy = e.Entity.UpdatedBy, Creator = u.Name })
.GroupJoin(dbContext.Users, e => e.UpdatedBy, u => u.Id, (e, u) => new { Entity = e, User = u })
.SelectMany(eUser => eUser.User.DefaultIfEmpty(), (e, u) => new Entity() { Id = e.Entity.Id, CreatedBy = e.Entity.CreatedBy, UpdatedBy = e.Entity.UpdatedBy, Creator = e.Entity.Creator, Updater = u.Name }
).ToList();
Console.WriteLine(JsonConvert.SerializeObject(result2, Formatting.Indented));
var result3 = dbContext.Entities
.SelectMany(entity => dbContext.Users.Where(user => entity.CreatedBy == user.Id).DefaultIfEmpty(), (entity, user) => new { Entity = entity, User = user })
.SelectMany(entity => dbContext.Users.Where(user => entity.Entity.UpdatedBy == user.Id).DefaultIfEmpty(), (entity, user) => new Entity { Id = entity.Entity.Id, CreatedBy = entity.Entity.CreatedBy, UpdatedBy = entity.Entity.UpdatedBy, Creator = entity.User.Name, Updater = user.Name })
.ToList();
Console.WriteLine(JsonConvert.SerializeObject(result2, Formatting.Indented));
}
}

Related

EF Core 2.1 evaluates locally when Sum complex and Grouping

I'm using EF Core 2.1 and query don't evaluates on SQL server side.
Model using in this query is:
public class V_TurnoverByDivision
{
public long Id { get; set; }
public decimal LineAmount { get; set; }
public DateTime? PostingDate { get; set; }
public decimal Quantity { get; set; }
public decimal UnitCostLcy { get; set; }
public string DivisionCode { get; set; }
public string DivisionDescription { get; set; }
public string TopDivisionCode { get; set; }
public string TopDivisionDescription { get; set; }
public decimal RUCAmount { get; set; }
}
This LINQ statement is run completely in SQL Server:
return query
.GroupBy(g => new { g.DivisionCode, g.DivisionDescription, g.TopDivisionCode, g.TopDivisionDescription, g.PostingDate })
.Select(s =>
new V_TurnoverByDivision
{
DivisionCode = s.Key.DivisionCode,
DivisionDescription = s.Key.DivisionDescription,
TopDivisionCode = s.Key.TopDivisionCode,
TopDivisionDescription = s.Key.TopDivisionDescription,
PostingDate = s.Key.PostingDate,
LineAmount = s.Sum(ss => ss.LineAmount),
RUCAmount = s.Sum(ss => ss.LineAmount - (ss.Quantity * ss.UnitCostLcy))
});
and generates the following SQL
SELECT
[v].[BIInvCNLinesID]
,[v].[DivisionCode]
,[v].[DivisionDescription]
,[v].[LineAmount]
,[v].[PostingDate]
,[v].[Quantity]
,[v].[TopDivisionCode]
,[v].[TopDivisionDescription]
,[v].[UnitCostLcy]
FROM [V_TurnoverByDivision] AS [v]
WHERE [v].[PostingDate] >= #__firstDayOfcurrentMonth_0
ORDER BY [v].[DivisionCode], [v].[DivisionDescription], [v].[TopDivisionCode], [v].[TopDivisionDescription], [v].[PostingDate]
This LINQ statement works but performs the GroupBy in memory
and I get warrnings in the Output windows
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'Sum()' could not be translated and will be evaluated locally.
BUT WHEN I use this query
return query
.GroupBy(g => new { g.DivisionCode, g.DivisionDescription, g.TopDivisionCode, g.TopDivisionDescription, g.PostingDate })
.Select(s =>
new V_TurnoverByDivision
{
DivisionCode = s.Key.DivisionCode,
DivisionDescription = s.Key.DivisionDescription,
TopDivisionCode = s.Key.TopDivisionCode,
TopDivisionDescription = s.Key.TopDivisionDescription,
PostingDate = s.Key.PostingDate,
LineAmount = s.Sum(ss => ss.LineAmount)
});
};
and SQL generates query that should be:
SELECT
[v].[DivisionCode]
,[v].[DivisionDescription]
,[v].[TopDivisionCode]
,[v].[TopDivisionDescription]
,[v].[PostingDate]
,SUM([v].[LineAmount]) AS [LineAmount]
FROM [V_TurnoverByDivision] AS [v]
WHERE [v].[PostingDate] >= #__firstDayOfcurrentMonth_0
GROUP BY [v].[DivisionCode]
,[v].[DivisionDescription]
,[v].[TopDivisionCode]
,[v].[TopDivisionDescription]
,[v].[PostingDate]
How to solve problem with:
RUCAmount = s.Sum(ss => ss.LineAmount - (ss.Quantity * ss.UnitCostLcy))
This is EF Core GroupBy translation limitation (probably will be resolved in some future version). In order to be translatable to SQL, the aggregate method expression should be simple property accessor.
That's why
s.Sum(ss => ss.LineAmount)
translates, but
s.Sum(ss => ss.LineAmount - (ss.Quantity * ss.UnitCostLcy))
doesn't.
Hence the solution is to pre-select the expression(s) needed for aggregates. One way to do that is to use the GroupBy overload with element selector:
return query
.GroupBy(e => new // Key
{
e.DivisionCode,
e.DivisionDescription,
e.TopDivisionCode,
e.TopDivisionDescription,
e.PostingDate
},
e => new // Element
{
e.LineAmount,
RUCAmount = e.LineAmount - (e.Quantity * e.UnitCostLcy) // <--
})
.Select(g => new V_TurnoverByDivision
{
DivisionCode = g.Key.DivisionCode,
DivisionDescription = g.Key.DivisionDescription,
TopDivisionCode = g.Key.TopDivisionCode,
TopDivisionDescription = g.Key.TopDivisionDescription,
PostingDate = g.Key.PostingDate,
LineAmount = g.Sum(e => e.LineAmount),
RUCAmount = g.Sum(e => e.RUCAmount) // <--
});

How to fix: I am getting a "No coercion operator" error only after a .Join with .Include

I am using EF core 2.2, I am working on a search page, and I need to get only the newest(timestamp) form a custID. So the data can look like this.
AltId, CustID, ReceiveDate, name,...
1, 2, Null, bob...
1, 2, 2/9/2018, bob...
1, 2, 2/5/2018, bob...
1, 2, 1/10/2017, bob...
2, 5, 1/6/2018, Matt...
3, 7, 1/3/2018, Kelly...
4, 9, 1/5/2018, Sam...
The results I need are.. you can see I needed to filter out null and get the max date.
AltId, CustID, ReceiveDate, name,...
1, 2, 2/9/2018, bob...
2, 5, 1/6/2018, Matt...
3, 7, 1/3/2018, Kelly...
4, 9, 1/5/2018, Sam...
I am less worried about the details of the error. I don't want talk about the int and string. BUT rather why the .join is braking or changing the type of something.
Also, any advice on how to debug this low-level EF error would help.
if I remove the .join, I get all the record and my array of custTransations is populated. (as I would suspect)
if I remove the .include I get the correct filtered data but custTransations null (as I would suspect)
var query = Set.GroupBy(e => e.CustId)
.Select(e => new { CustId= e.Key, ReceiveDate = e.Max(m => m.ReceiveDate ) })
.AsQueryable();
data = await Set.Where(
e => e.IssueDate >= criteria.StartDate
&& e.IssueDate <= criteria.EndDate)
.Where(e => e.CustTransactions.Any(
cat =>cat.Cust.Member.AltId == criteria.AltId
&& cat.Cust.IsActive == true
&& cat.IsActive == true)
.Join(query,
outer => new { outer.CustId, outer.ReceiveDate },
inner => new { inner.CustId, inner.ReceiveDate },
(outer, inner) => outer
)
.Include(e => e.CustTransactions)
.AsNoTracking()
.ToListAsync();
```
No coercion operator is defined between types 'System.Int32' and 'System.String'.
Looks like a query generation bug/limitation in EF Core. There are still many query shapes that EF Core can't translate to SQL, but most of them should fail with an intelligible error message. This one just fails. Below is a repro and a workaround. Someone should see if this repros on EF Core 3, and open an issue on GitHub.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SomeUI
{
public class Product
{
public int Id { get; set; }
public string CategoryName { get; set; }
public DateTime UpdatedAt { get; set; }
public DateTime CreatedAt { get; set; }
public virtual ICollection<Transactions> Transactions { get; } = new HashSet<Transactions>();
}
public class Transactions
{
public int Id { get; set; }
public Product Product { get; set; }
public int Quantity { get; set; }
public DateTime TransDate { get; set; }
}
public class Db : DbContext
{
private string connectionString;
public Db() : this("Server=.;Database=EfCoreTest;Integrated Security=true")
{ }
public Db(string connectionString)
{
this.connectionString = connectionString;
}
public DbSet<Product> Products{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString, b => b.UseRelationalNulls())
.ConfigureWarnings(warnings => warnings.Throw(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.QueryClientEvaluationWarning));
base.OnConfiguring(optionsBuilder);
}
}
public class Program
{
static void Main(string[] args)
{
using (var db = new Db())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var p = new Product() { CategoryName = "A" };
var tran = new Transactions() { Product = p, Quantity = 2, TransDate = DateTime.Now };
db.Products.Add(p);
db.SaveChanges();
}
using (var db = new Db())
{
var q = db.Products.GroupBy(p => p.CategoryName).Select(g => new { CategoryName = g.Key, UpdatedAt = g.Max(p => p.UpdatedAt) });
var q2 =
from p in db.Products.Include(p => p.Transactions)
join r in q
on new { p.CategoryName, p.UpdatedAt } equals new { r.CategoryName, r.UpdatedAt }
select p;
var q3 =
from p in db.Products.Include(p => p.Transactions)
where db.Products.Where(np => np.CategoryName == p.CategoryName).OrderByDescending(np => np.UpdatedAt).Take(1).Select(np => np.UpdatedAt).Contains(p.UpdatedAt)
select p;
var q4 =
db.Products
.Include(p => p.Transactions)
.Where(p => db.Products
.Where(np => np.CategoryName == p.CategoryName)
.OrderByDescending(np => np.UpdatedAt)
.Take(1)
.Select(np => np.UpdatedAt)
.Contains(p.UpdatedAt));
//var results = q2.ToList(); //fails
/*
System.InvalidOperationException
HResult=0x80131509
Message=No coercion operator is defined between types 'System.String' and 'System.Nullable`1[System.Int32]'.
Source=System.Linq.Expressions
StackTrace:
at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType)
*/
var results = q3.ToList(); //works
db.SaveChanges();
}
}
}
}

Get nested data and sharp into DTO with nested DTO

I'm newbie to EF, Linq and C# in general, I'm stuck with developing following.
I cannot map data into structure like this:
Id,
Actions [
Action1,
Action2,
Action3
]
I have 2 DTO classes like this:
public class TestDTO
{
public int TestId { get; set; }
public TestDTO2[] Actions { get; set; }
}
and
public class TestDTO2
{
public int TestActionId { get; set; }
public DateTime? StartDate { get; set; }
...
}
I've separated calls to DB into file called BusinessLogic, I'm doing it like this:
public IQueryable<TestDTO> GetNested(Filter filter)
{
var query =
from a in db.Table1.AsQueryable()
select new TestDTO
{
TestId = a.Id,
Actions = (
from b in db.Table2.AsQueryable()
where a.Id == b.TestId
select new TestDTO2
{
TestActionId = b.TestActionId,
StartDate = b.StartDate
}
).ToArray()
};
return query;
}
I'm getting following error:
LINQ to Entities does not recognize the method 'Project.Core.Models.TestDTO2[] ToArrayTestDTO2' method, and this method cannot be translated into a store expression.
You can't perform exactly this query, it is better to make two simple queries and then process their results on client side:
var main = db.Table1.Select(x => new { x.Id, x.Title }).ToList();
var mainIds = main.Select(x => x.Id).ToList();
var actions = db.Table2.Where(x => mainIds.Contains(x.TestId)).Select(x => new
{
x.TestId,
x.TestActionId,
x.StartDate
}).ToList();
var result = main.Select(x => {
var actions = actions.Where(y => y.TestId == x.Id).Select(y => new TestDTO2
{
TestActionId = y.TestActionId,
StartDate = y.StartDate
}).ToArray();
return new TestDTO
{
TestId = x.Id,
Title = x.Title,
Actions = actions.Length == 0 ? null : actions
};
}).ToList();
yes, you can't use any c# method that can't translate a sql in EF.
actually, you need get a list,then covert it to your DTO
db.Table1
.Join(db.Table2,
a => a.Id,
b => b.TestId,
(a, b) => new
{
a.Id,
b
})
.GroupBy(k => k.Id, v => v).ToList()
.Select(a=>new TestDTO
{
TestId = a.Id,
Actions = a.Select(b=>
new TestDTO2
{
TestActionId = b.TestActionId,
StartDate = b.StartDate
}.ToArray()
}).ToList()

ASP.NET Core cannot sort grouped items

I have the following model in my ASP.NET Core application:
public class LocationTypeGroup {
public string Name { get; set; }
public IEnumerable<LocationType> LocationTypes { get; set; }
}
public class LocationType
{
[Key]
public int LocationTypeID { get; set; }
public string Name { get; set; }
public string IntExt { get; set; }
}
I am trying to run a query that groups them by IntExt, and sorts by Name within each group.
The following works, but doesn't sort:
public async Task<List<LocationTypeGroup>> GetGroupedLocationTypes()
{
return await _context.LocationTypes
.GroupBy(p => p.IntExt)
.Select(g => new LocationTypeGroup
{
Name = g.Key,
LocationTypes = g.Select(x => x)
})
.OrderBy(x=>x.Name)
.ToListAsync();
}
If I change to this:
LocationTypes = g.Select(x => x).OrderBy(x => x)
Then I still do not get a sorted result.
What am I doing wrong?
It's possible that EF can't build SQL query.
So you need simplify it manually. and split to 2 queries:
var groups = await context.LocationTypes
.GroupBy(p => p.IntExt)
.ToListAsync();
return groups.Select(g => new LocationTypeGroup
{
Name = g.Key,
LocationTypes = g.Select(x => x)
})
.OrderBy(x=>x.Name);
The first query loads simply groups, and the second sorts them and converts to LocationTypeGroup.
May be it caused by too old version of Entity Framework Core. Try this approach, moreover it will be less expensive:
//data is loaded into memory
var data = await _context.LocationTypes.ToListAsync();
//data's transform
var answer = data.GroupBy(x => x.IntExt)
.Select(x => new LocationTypeGroup
{
Name = x.Key,
LocationTypes = x.AsEnumerable()
}).OrderBy(x => x.Name).ToList();

How To Insert Data In FluentAPI Mapping Table

I have a A Table, B Table and AB (Mapping Table)
A
public class A
{
public int AID{ get; set; }
[JsonIgnore]
public virtual ICollection<B> Bs { get; set; }
}
B
public class B
{
public int BID { get; set; }
[JsonIgnore]
public virtual ICollection<A> As { get; set; }
}
ApplicationDbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<B>()
.HasMany(s => s.As)
.WithMany(c => c.Bs)
.Map(cs =>
{
cs.MapLeftKey("AID");
cs.MapRightKey("BID");
cs.ToTable("AB");
});
}
Now things are perfectly fine, but how do I insert in this AB Mapping table?
If I try to create AB as like below, it generates two tables, AB and AB1 with same column name and all.
public class AB
{
public int ABID { get; set; }
public string AID { get; set; }
public int BID { get; set; }
}
So is there any way to do CRUD in FluentAPI Mapping Table?
If not, then can I force FluentAPI to map from Existing table? In this case I'll manually manage Employee and will change the mapping code to use existing table.
I'm unable to find any of the solution.
Edit: Since the question was changed, I'm writing up a more thorough answer. The answer to your question remains the same, however:
Now things are perfectly fine, but how do I insert in this AB Mapping
table?
You don't!
This is exactly the kind of thing that EF is good at. Instead of managing a link table yourself, now you just end up with the actual object you want. So, if you want to add a link between an A and B, all you do is add a B to the Bs collection on that A. You don't ever insert directly into the AB table, because who cares about that? That table is there so we can have relationships between different As and Bs, that's it. So, Entity Framework will create the table for it's own use, but not present it to you, because that's not how EF works: you work with your objects and let EF handle the database.
That's why when you try to define the table yourself, it creates two: it's already making a table called AB, but you're asking for another one. It can't have exactly the same name so it appends a '1' to the end of it. Since you've already used FluentAPI to define the apping, let EF worry about how to implement the mapping: all you need to care about is that you've now got a way to have an A with a set of Bs, or vice versa.
Since this still sounds confusing with names 'A' and 'B', below is the Program class for a console app that will illustrate this; all you need to do is start a fresh console app, replace the Program class with this one, install the entity framework package, and run enable-migrations -enableautomaticmigrations -force. I recommend you use this to add some objects and relate them, and then go have a look at your database: you will see the 'AB' table, with records that were added. This might help explain it better.
class Program
{
static bool quit = false;
static void Main(string[] args)
{
string s = "Please select an option:" +
"\n1: Insert an A" +
"\n2: Insert a B" +
"\n3: Add a B to an A" +
"\n4: Add an A to a B" +
"\n5: Print all As" +
"\n6: Print all Bs" +
"\n7: Print AB Table" +
"\nx: Quit.";
while (!quit)
{
Console.WriteLine();
Console.WriteLine(s);
var k = Console.ReadKey();
DoStuff(k);
}
}
private static void DoStuff(ConsoleKeyInfo i)
{
switch (i.Key)
{
case ConsoleKey.D1:
//add an A
AddA(GetName());
break;
case ConsoleKey.D2:
//add a B
AddB(GetName());
break;
case ConsoleKey.D3:
// link a B to an A
LinkB(GetBtoLink(),GetAtoLink());
break;
case ConsoleKey.D4:
//link an A to an B
LinkA(GetAtoLink(), GetBtoLink());
break;
case ConsoleKey.D5:
// print As
WriteA();
break;
case ConsoleKey.D6:
//print Bs
WriteB();
break;
case ConsoleKey.D7:
// print AB
WriteAB();
break;
case ConsoleKey.X:
quit = true;
break;
}
}
private static int GetAtoLink()
{
string x;
int z;
do
{
Console.Clear();
Console.WriteLine("Please enter the ID of the A you want to use and then press enter.");
WriteA();
x = Console.ReadLine();
} while (!int.TryParse(x, out z));
return z;
}
private static int GetBtoLink()
{
string x;
int z;
do
{
Console.Clear();
Console.WriteLine("Please enter the ID of the B you want to use and then press enter.");
WriteB();
x = Console.ReadLine();
} while (!int.TryParse(x, out z));
return z;
}
private static void WriteB()
{
Console.WriteLine("{0,10}{1,15}", "ID", "Name");
using (var db = new Context())
{
foreach (var a in db.Bs)
{
Console.WriteLine("{0,10}{1,15}", a.BID, a.Name);
}
}
}
private static void WriteA()
{
Console.WriteLine("{0,10}{1,15}", "ID", "Name");
using (var db = new Context())
{
foreach (var a in db.As)
{
Console.WriteLine("{0,10}{1,15}", a.AID, a.Name);
}
}
}
private static void WriteAB()
{
Console.WriteLine("{0,10}{1,10}", "AID", "BID");
using (var db = new Context())
{
// this is the only way we need to do this, because it's many to many,
// if an A is linked to a B, then that B is by definition linked to that A as well.
foreach (var a in db.As)
{
foreach (var b in a.Bs)
{
Console.WriteLine("{0,10}{1,10}", a.AID, b.BID);
}
}
}
}
private static void LinkB(int bToUse, int aToUse)
{
using (var db = new Context())
{
var a = db.As.First(x => x.AID == aToUse);
var b = db.Bs.First(y => y.BID == bToUse);
a.Bs.Add(b);
db.SaveChanges();
}
}
private static void LinkA(int aToUse, int bToUse)
{
using (var db = new Context())
{
var a = db.As.First(x => x.AID == aToUse);
var b = db.Bs.First(y => y.BID == bToUse);
b.As.Add(a);
db.SaveChanges();
}
}
private static string GetName()
{
Console.WriteLine("Please enter a name");
return Console.ReadLine();
}
private static void AddA(string input)
{
using (var db = new Context())
{
db.As.Add(new A {Name = input});
db.SaveChanges();
}
}
private static void AddB(string input)
{
using (var db = new Context())
{
db.Bs.Add(new B { Name = input });
db.SaveChanges();
}
}
}
public class A
{
public int AID { get; set; }
public string Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public int BID { get; set; }
public string Name { get; set; }
public virtual ICollection<A> As { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<B>()
.HasMany(s => s.As)
.WithMany(c => c.Bs)
.Map(cs =>
{
cs.MapLeftKey("AID");
cs.MapRightKey("BID");
cs.ToTable("AB");
});
}
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
}
Old Answer: You've defined an ICollection<ApplicationUser> called Employees in Company, and mapped to it with FluentAPI. This creates a table called 'Employees' as expected. You don't have to create another class called Employees; as far as Entity Framework is concerned, you've already told it to create a table called Employees. This is why
I think the step you're missing is defining your DbSet<>.
Using your code, and running Add-Migration, this is the definition I get for the Employees table:
CreateTable(
"dbo.Employees",
c => new
{
UserID = c.Int(nullable: false),
CompanyID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.UserID, t.CompanyID })
.ForeignKey("dbo.ApplicationUsers", t => t.UserID, cascadeDelete: true)
.ForeignKey("dbo.Companies", t => t.CompanyID, cascadeDelete: true)
.Index(t => t.UserID)
.Index(t => t.CompanyID);
Which seems to correlate with what you wanted.
To finish it off, add (if you haven't already) this to your ApplicationDbContext file:
public DbSet<ApplicationUser> Employees;
public DbSet<Company> Companies;
Then to add an employee, you create a new ApplicationUser and add it like
ApplicationUser user = new ApplicationUser();
// do whatever here to give it the right data
ApplicationDbContext ctx = new ApplicationDbContext();
ctx.Employees.Add(user);
The Employees table itself you shouldn't ever have to interact with.
EF will manage that you don't need to insert into the mapping table directly, have a look at this sample that I have in my project:
public class Organization : Entity<int>
{
public string Name { get; set; }
public string Address { get; set; }
public string MainContact { get; set; }
public string Phone { get; set; }
public string Website { get; set; }
//navigation property
public virtual ICollection<DevelopmentalGoal> DevelopmentalGoals { get; set; }
public virtual ICollection<ServiceActivity> ServiceActivities { get; set; }
}
public class DevelopmentalGoal : Entity<int>
{
public string Name { get; set; }
public string Icon { get; set; }
//navigation property
public virtual ICollection<Organization> Organizations { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Organization>().ToTable("Organization", "ServiceLearning")
.HasKey(t => t.ID);
modelBuilder.Entity<DevelopmentalGoal>().ToTable("DevelopmentalGoal", "ServiceLearning")
.HasKey(t => t.ID);
modelBuilder.Entity<Organization>()
.HasMany(t => t.DevelopmentalGoals)
.WithMany(t=> t.Organizations)
.Map(m =>
{
m.ToTable("OrganizationDevelopmentalGoal", "ServiceLearning");
m.MapLeftKey("OrganizationID");
m.MapRightKey("DevelopmentalGoalID");
});
}
public int SaveOrganization(OrganizationViewModel viewModel, IUserContext currentUser)
{
Organization organization;
{
if (viewModel.ID == 0)
{
organization = ObjectMapper.MapTo<Organization>(viewModel);
_context.Set<Organization>().Add(organization);
}
else
{
organization = _context.Set<Organization>()
.SingleOrDefault(t =>
t.ID == viewModel.ID
);
organization.Name = viewModel.Name;
organization.Address = viewModel.Address;
organization.MainContact = viewModel.MainContact;
organization.Phone = viewModel.Phone;
organization.Website = viewModel.Website;
UpdateOrganizationDevelopmentalGoals(organization, viewModel);
}
try
{
CommitChanges();
}
catch (DbUpdateException ex)
{
if (ex.IsDuplicateException())
throw new KeystoneDuplicateException("A Organization with the same name already exists.");
throw ex;
}
}
return organization.ID;
}
private void UpdateOrganizationDevelopmentalGoals(Organization organization, OrganizationViewModel viewModel)
{
var originalIdList = organization.DevelopmentalGoals.Select(d => d.ID).Distinct().ToList();
var modifiedIdList = viewModel.DevelopmentalGoal.Where(d => d.Selected == true).Select(d => d.ID).Distinct().ToList();
//Remove deleted Developmetal Goals.
foreach (var id in originalIdList.Except(modifiedIdList))
organization.DevelopmentalGoals.Remove(organization.DevelopmentalGoals.Single(d => d.ID == id));
//Add new Developmetal Goals.
foreach (var id in modifiedIdList.Except(originalIdList))
{
//Add director relationship without having to load entity.
var d = new DevelopmentalGoal { ID = id };
_context.Set<DevelopmentalGoal>().Attach(d);
organization.DevelopmentalGoals.Add(d);
}
}
As you can see in the UpdateOrganizationDevelopmentalGoals method I do not insert or delete data from the mapping table directly, I insert and delete from the organization.DevelopmentalGoals and as I've already defined the mapping table in fluent API on "OnModelCreating" then EF knows how to manage the relations.