How to insert old record to new entity? - c#

I am trying to check record , if exists declare to data with old one . But if doesnt then create a new one. Here is my code ;
var teamCheck= FootballerDBContext.Teams.Any(r => r.Name == teamName.Text.Trim());
if (teamCheck)
{
team = FootballerDBContext.Teams.FirstOrDefault(c => c.Name == teamName.Text);
}
FootballerDBContext.Teams.Add(team);
FootballerDBContext.SaveChanges(); // throwing exception right there . I did it exactly same to other entity everything was fine.
Here is other entity , i did the same but Team throws exception. No errors here , doing what i want. It doesnt create new entity with new ID , just declaring old one.
( sponsor has many-to-many relationship , team has one-to-many )
var sponsorCheck = FootballerDBContext.Sponsors.Any(x => x.Name == Sponsor.Text.Trim());
if (sponsorCheck)
{
sponsor = FootballerDBContext.Sponsors.FirstOrDefault(z => z.Name == Sponsor.Text);
}
FootballerDBContext.FootballerSponsor.Add(fbsp);
FootballerDBContext.SaveChanges();

Don't do it like you're doing; it queries the database twice
Do a pattern like this instead:
var team = FootballerDBContext.Teams.FirstOrDefault(c => c.Name == teamName.Text.Trim());
if(team == default) //or null
{
team = new Team(){ Name = teamName.Text };
FootballerDBContext.Teams.Add(team);
}
team.Location = teamLocation.Text;
...
FootballerDBContext.SaveChanges();
Query using Find or FirstOrDefault
If the result is null make a new one and assign it to the variable that is null
Add the team to the context if you just made a new one
Now you definitely have a team, either because you made it new or because it already exists and is looked up
You can set other properties
Call save changes at the end and an update or insert command will be run as appropriate

These LINQ queries are not the same:
teamName.Text.Trim() vs teamName.Text
.Trim()
try to check if variable is null or not like this:
string teamName = teamName.Text.Trim()
if (!String.IsNullOrEmpty(teamName))
first: **team** default value is (teamName)
second: validate linq query... r => r.Name == teamName
if (second == false)
save first.

Related

Why does Entity Framework show that item is already tracked, despite of fact that it is not tracked?

I have 3 tests, and between every test, a new DbContext is created. I checked it simply run 3 test cases, all added the same object to db and there was no error. But when I run this tests:
[TestCase("test2#test.com", "test2#test.com", "123", "321", UserSaveResultStatus.UserAlreadyExists, Description = "Duplicated emails")]
[TestCase("test3#test.com", "test4#test.com", "456", "456", UserSaveResultStatus.UserWithGivenEmployeeIdAlreadyExists, Description = "Duplicated Employee Ids")]
[TestCase("test5#test.com", "test6#test.com", "", "", UserSaveResultStatus.Success, Description = "Empty Employee Ids. Should add two users")]
public void ImportUsers_Should_Add_User_Only_Once_When_Email_Or_EmployeeId_Doubled_On_The_List(string email1, string email2, string employeeId1, string employeeId2, UserSaveResultStatus expectedStatus)
{
var emails = new string[] { email1, email2 };
var employeeIds = new string[] { employeeId1, employeeId2 };
var dto = GetUserImportDto(emails, employeeIds);
var checker = dbContext.ChangeTracker
.Entries()
.Where(t => t.State == EntityState.Detached
|| t.State == EntityState.Unchanged
|| t.State == EntityState.Modified
|| t.State == EntityState.Detached
|| t.State == EntityState.Deleted
|| t.State == EntityState.Added);
.
.
.
Asserts
I get this error:
The instance of entity type 'PowerPlantUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values
I create breakpoint in place when error is thrown:
foreach (var powerPlantId in newPowerPlantsIds)
{
var personToAdd = new PowerPlantUser() { UserId = userId, PowerPlantId = powerPlantId };
var state1 = commandsContext.Entry(personToAdd).State;
var state2 = commandsContext.Entry(personToAdd).State; //STATE BEFORE ERROR IS DETACHED
commandsContext.PowerPlantUsers.Add(personToAdd); //HERE IS AN ERROR
As we can see above also in test I create checker which shows me tracked entries. New PowerPlantUser has id = 0 and there is no this user in checker.
When I run tests separately, there is no error and all tests passed. Can someone tell me where the problem is?
The error implies that a different instance of a PowerPlantUser with that same ID is already tracked by the DbContext. It is also referring to an "Id" column so while you are providing a UserId and PowerPlantId I'd check to see if you are implementing some base class or such defining a common "Id" column that isn't being set up correctly as an Identity resulting in each record possibly going into the DB with a PK of "0".
Check your PowerPlantUser definition for an Id column and whether it is correctly set up as an Identity column. (Can also depend on which Database you are using for testing.)
Check the local tracking cache for a matching entity. If the PK is definitely the PowerPlantId + UserId, then add the following code to confirm if the Context is tracking an existing entity:
..
var existingItem = commandsContext.PowerPlantUsers.Local
.SingleOrDefault(x => x.PowerPlantId == powerPlantId && x.UserId == userId);
That checks the tracking cache for the DbContext. If that returns an entity then something else is inserting and leaving a tracked instance in the DbContext.

Linq to entities - SQL Query - Where list contains object with 2 properties (or more)

Having the following example:
var myIds = db.Table1.Where(x=>x.Prop2 == myFilter).Select(x=>x.Id).ToList();
var results = db.Table2.Where(x=> myIds.Contains(x.T1)).ToList();
This part is straight forward.
However, now I am facing a "slight" change where my "filter list" has 2 properties instead of only one:
// NOTE: for stackoverflow simplification I use a basic query to
// get my "myCombinationObject".
// In reality this is a much more complex case,
// but the end result is a LIST of objects with two properties.
var myCombinationObject = db.Table3.Where(x=>x.Prop3 == myFilter)
.Select(x=> new {
Id1 = x.T1,
Id2 = x.T2
}).ToList();
var myCombinationObjectId1s = myCombinationObject.Select(x=>xId1).ToList();
var myCombinationObjectId2s = myCombinationObject.Select(x=>xId2).ToList();
// step#1 - DB SQL part
var resultsRaw = db.Tables.Where( x=>
myCombinationObjectId1s.Contains(x.Prop1)
|| myCombinationObjectId2s.Contains(x.Prop2))
.ToList();
// step#2 - Now in memory side - where I make the final combination filter.
var resultsFiltered = resultsRaw.Where( x=>
myCombinationObject.Contains(
new {Id1 = x.Prop1, Id2 = x.Prop2 }
).ToList();
My question: is it even possible to merge the step#2 in the step#1 (query in linq to entities) ?
I've managed once to do what you want, however it is pretty hard and requires changing entity model a bit. You need an entity to map type
new {Id1 = x.Prop1, Id2 = x.Prop2 }
So you need enity having 2 properties - Id1 and Id2. If you have one - great, if not then add such entity to your model:
public class CombinationObjectTable
{
public virtual Guid Id1 { get; set; }
public virtual Guid Id2 { get; set; }
}
Add it to your model:
public DbSet<CombinationObjectTable> CombinationObjectTable { get; set; }
Create new migration and apply it database (database will have now additional table CombinationObjectTable). After that you start to build a query:
DbSet<CombinationObjectTable> combinationObjectTable = context.Set<CombinationObjectTable>();
StringBuilder wholeQuery = new StringBuilder("DELETE * FROM CombinationObjectTable");
foreach(var obj in myCombinationObject)
{
wholeQuery.Append(string.Format("INSERT INTO CombinationObjectTable(Id1, Id2) VALUES('{0}', '{1}')", obj.Id1, obj.Id2);
}
wholeQuery.Append(
db.Tables
.Where( x=>
myCombinationObjectId1s.Contains(x.Prop1)
|| myCombinationObjectId2s.Contains(x.Prop2))
.Where( x=>
combinationObjectTable.Any(ct => ct.Id1 == x.Id1 && ct.Id2 == x.Id2)
).ToString();
);
var filteredResults = context.Tables.ExecuteQuery(wholeQuery.ToString());
Thanks to this your main query stays written in linq. If you do not want to add new table to your db this is as well achievable. Add new class CombinationObjectTable to model, generate new migration to add it and afterwards remove code creating that table from migration code. After that apply migration. This way the db schema won't be changed but EF will think that there is CombinationObjectTable in database. Instead of it you will need to create a temporary table to hold data:
StringBuilder wholeQuery = new StringBuilder("CREATE TABLE #TempCombinationObjectTable(Id1 uniqueidentifies, Id2 uniqueidentifier);");
And when you invoke ToString method on your linq query change CombinationObjectTable to #TempCombinationObjectTable:
...
.ToString()
.Replace("CombinationObjectTable", "#TempCombinationObjectTable")
Other thing worth considering would be using query parameters to pass values in INSERT statements instead of just including them in query yourself - this is of course achievable with EF as well. This solution is not fully ready to apply, rather some hint in which direction you may go for the solution.
Can you do something like this:
var result=
db.Tables
.Where(t=>
db.Table3
.Where(x=>x.Prop3 == myFilter)
.Any(a=>a.T1==t.Prop1 || a.T2==t.Prop2)
).ToList();
If you simply want to avoid the intermediate result (and also creating a second intermediary list) you can do the following
var resultsFiltered = db.Tables.Where( x=>
myCombinationObjectId1s.Contains(x.Prop1)
|| myCombinationObjectId2s.Contains(x.Prop2))
.AsEnumerable() // everything past that is done in memory but isn't materialized immediately, keeping the streamed logic of linq
.Where( x=>
myCombinationObject
.Contains(new {Id1 = x.Prop1, Id2 = x.Prop2 })
.ToList();

Nested query in entity framework

I am getting the following exception:
The nested query is not supported. Operation1='Case' Operation2='Collect'
with this query
var Games = context.Games.Select(a => new GameModel
{
Members = (a.Type == 1 ? (a.UsersInGames.Where(b => b.GameID == a.ID && b.StatusID == 1).Select(c => new Member
{
ID = c.UserID,
email = c.UserInfo.EmailAddress,
screenName = c.UserInfo.ScreenName
})) :
(a.Teams.Where(b => b.GameID == a.ID).SelectMany(b => b.UsersInTeams.Where(c => c.StatusID == 1)).Select(d => new Member
{
ID = d.UserID,
email = d.UserInfo.EmailAddress,
screenName = d.UserInfo.ScreenName
)))
})
when I don't include the condition in selecting Members, the query works fine. Is there a way I can do the conditional inside the query?
You're overestimating the power of LINQ translation to SQL. Not everything is translatable and there is no compiler warning for that due to the way LINQ works.
Nested collections are usually either a) not supported or b) end up in horrible SELECT N+1 queries. What you ask EF to do is to return an object tree. SQL does not support tree like results so you run into the object-relational impedance mismatch and it hurts.
I advise you to fetch the nested collection data as a second, completely separate query. That allows you more control and is guaranteed to work.
As a non-essential side-note, you will probably not be able to convince EF to use the ?: operator over sequences. That is very hard to translate. Think how you would write this as SQL - very hard and convoluted.
It looks like Linq to EF doesn't support the following
context.Games.Select(g => new
{
Field = g.IsX? queryable1 : queryable2
});
But, here's a hack you can use to get it to work:
context.Games.Select(g => new
{
Field = queryable1.Where(q => g.IsX)
.Concat(queryable2.Where(q => !g.IsX))
});
I faced the same problem. The solution was to load both results and determine what to use after the query (I know it has performance downside), but at least you can do it temporarily if deadline attacks you:
At the LINQ side
var Games = context.Games.Select(a => new GameModel
{
// carries type1 results
Members = a.UsersInGames.Where(b => b.GameID == a.ID && b.StatusID == 1).Select(c => new Member
{
ID = c.UserID,
email = c.UserInfo.EmailAddress,
screenName = c.UserInfo.ScreenName
})),
//You need to create this temporary carrier to carry type 2 results
MembersOfType2 = a.Teams.Where(b => b.GameID == a.ID).SelectMany(b => b.UsersInTeams.Where(c => c.StatusID == 1)).Select(d => new Member
{
ID = d.UserID,
email = d.UserInfo.EmailAddress,
screenName = d.UserInfo.ScreenName
})))
})
}
After that you may loop Gamesand make the assignment Members = MembersOfType2 if Type == 1 for a certain game.
I had this error too. I had code like this:
var Games = context.Games.Select(a => new GameModel
{
Members = (!filters.GetDatailedDataToo ? null : new List<MemberModel>())
};
This error occurs when null is used in ? : operation.
This is not that case, written up here, but I've wasted lot of time, I think anyone uses this case, who searches this error text..

How do I use LINQ-to-Entities to insert data into a specific table?

Question: what is the LINQ-to-Entity code to insert an order for a specific customer?
Update
Here is the a solution (see one of the submitted answers below for a much cleaner solution):
using (OrderDatabase ctx = new OrderDatabase())
{
// Populate the individual tables.
// Comment in this next line to create a new customer/order combination.
// Customer customer = new Customer() { FirstName = "Bobby", LastName = "Davro" };
// Comment in this line to add an order to an existing customer.
var customer = ctx.Customers.Where(c => c.FirstName == "Bobby").FirstOrDefault();
Order order = new Order() { OrderQuantity = "2", OrderDescription = "Widgets" };
// Insert the individual tables correctly into the hierarchy.
customer.Orders.Add(order);
// Add the complete object into the entity.
ctx.Customers.AddObject(customer);
// Insert into the database.
ctx.SaveChanges();
}
Your code isn't far off. Just change your second line to read as follows:
Customer customer = ctx.Customer.FirstOrDefault(c => c.FirstName == "Bobby");
if (customer != null)
{
//...
Just replace the c.FirstName == "Bobby" with something that can strongly identify the customer you're looking for (e.g. c.Id == customerID if you already know what the ID is).
Notice that Order has a Customer property. You don't have to add the Order to the Customer -- you can do it the other way around. So, instead of creating a new Customer, get the Customer using Linq, then add it to your new Order.
using (OrderDatabase ctx = new OrderDatabase())
{
ctx.AddOrder(new Order()
{
OrderQuantity = 2,
OrderDescription = "Widgets",
Customer = ctx.Customers.First<Customer>(c => c.CustomerId == yourId)
});
ctx.SaveChanges();
}
I don't get what the problem is, exactly.
var mycustomer = context.Customers.Where(x => x.id == 100).FirstOrDefault();
if(mycustomer != null)
{
mycustomer.Orders.Add(myorder);
}
context.SaveChanges();
L2E does not support set-based operations currently (update without select). See Use linq to generate direct update without select

C#: How to see if a Linq2SQL entity is in the database

I would like to check if an entity is already added to the database. So, how can I see this difference between a and b?
var a = dataContext.Things.First(x => x.Name == something);
var b = new Thing { Name = something };
To make it clearer, if I have this:
var thing = dataContext.Things.FirstOrDefault(x => x.Name == something)
?? new Thing { Name = something };
How can I see if thing needs to be inserted?
If you use FirstOrDefault instead of First, that will return null if there are no matches.
As for knowing whether you need to insert - just remember whether or not it was null to start with:
var a = dataContext.Things.FirstOrDefault(x => x.Name == something);
bool needsInsertion = (a == null);
a = a ?? new Thing { Name = something };
Alternatively, if there's an ID field in Thing which is automatically populated by the database, you can just use that to detect whether it's already in the database or not.

Categories