How should I prevent multiple inserts here? - c#

I am inserting 5000 transactions with about 6 banks, but I get 5000 bank rows in the database with duplicate names.
public class Transaction
{
[Key]
public int Id { get; set; }
public DateTime TransDate { get; set; }
public decimal Value { get; set; }
public string Description { get; set; }
public Bank Bank { get; set; }
}
public class Bank
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class FinancialRecordContext : DbContext
{
public FinancialRecordContext() : base("FinancialRecordDatabase") { }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<Bank> Banks { get; set; }
public Bank FindOrInsertBank(string bankName)
{
var bank = Banks.SingleOrDefault(b => b.Name == bankName);
if (bank == null)
{
bank = new Bank { Name = bankName };
Banks.Add(bank);
}
return bank;
}
}
Then to insert I am looping through some data and inserting thusly:
using (var context = new FinancialRecordContext())
{
foreach (var t in data)
{
var tran = new Transaction
{
Description = t.Description,
Value = t.Value,
TransDate = t.TransDate,
Bank = context.FindOrInsertBank(t.BankName)
};
context.Transactions.Add(tran);
}
context.SaveChanges();
}
It would appear that the FindOrInsertBank method is going to the database all the time and not looking locally at it's recently added, but not committed banks. How can/should I be doing this?
Should I SaveChanges after each bank insert? Not really what I want to do I want this to be all one transaction.

A couple of suggestions to try:
1) Check the Local collection of DbSet<Bank> first.
public Bank FindOrInsertBank(string bankName)
{
var bank = Banks.Local.SingleOrDefault(b => b.Name == bankName);
if (bank == null)
{
var bank = Banks.SingleOrDefault(b => b.Name == bankName);
if (bank == null)
{
bank = new Bank { Name = bankName };
Banks.Add(bank);
}
}
return bank;
}
2) Force a call to DetectChanges() after each update
context.Transactions.Add(tran);
context.ChangeTracker.DetectChanges();

You can try to query the change tracker (which is an in-memory query, not a database query):
using (var context = new FinancialRecordContext())
{
foreach (var t in data)
{
Bank bank = context.ChangeTracker.Entries()
.Where(e => e.Entity is Bank && e.State != EntityState.Deleted)
.Select(e => e.Entity as Bank)
.SingleOrDefault(b => b.Name == t.BankName);
if (bank == null)
bank = context.FindOrInsertBank(t.BankName);
var tran = new Transaction
{
Description = t.Description,
Value = t.Value,
TransDate = t.TransDate,
Bank = bank
};
context.Transactions.Add(tran);
}
context.SaveChanges();
}
Edit
Using the change tracker here could be bad for performance because Bank.Name is not the key of the entity and I guess the query would be a linear search through the entries. In this case using a handwritten dictionary might be the better solution:
using (var context = new FinancialRecordContext())
{
var dict = new Dictionary<string, Bank>();
foreach (var t in data)
{
Bank bank;
if (!dict.TryGetValue(t.BankName, out bank))
{
bank = context.FindOrInsertBank(t.BankName);
dict.Add(t.BankName, bank);
}
var tran = new Transaction
{
Description = t.Description,
Value = t.Value,
TransDate = t.TransDate,
Bank = bank
};
context.Transactions.Add(tran);
}
context.SaveChanges();
}

Related

Model one to many in RavenDb for better performance

I'm approaching to document database and I'm little bit confused how to map documents relationship, in a situation as follow
public class Person
{
public Person()
{
}
public int Id { get; set; }
public string Name { get;set;}
public string Surname { get; set; }
public DateTime? BirthDate { get; set; }
}
public class Car
{
public Car() { }
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set;}
}
A person has one or more cars for example in this way I can query the db as follow
public Car Get(int id)
{
Car car = null;
using (IDocumentSession session = store.OpenSession())
{
car = session.Include<Car, Person>(x => x.PersonId).Load<Car>(id);
bool isLoaded = session.Advanced.IsLoaded("people/" + car.PersonId); // true!
}
return car;
}
and it's everything ok, the client makes just one request, but if I have a person and I want to show all his cars how can I query the db to do just a request?
I think tha I must modify the model putting a List<int> Cars in Person for reference his cars.
Note that I don't want to embed Cars in the Person document because Cars can be referenced from others document.
Thanks.
You can do it like this:
using (IDocumentSession session = store.OpenSession())
{
var carsForOne = session.Query<Car>()
.Include(x=>x.PersonId)
.Where(x=>x.PersonId == "people/1")
.ToList();
var person = session.Load<Person>("people/1");
}
This make just a single db request.
You can index the Cars collection and load all the cars from the index.
The index would look like this:
public class CarIndex : AbstractIndexCreationTask<Car, CarView>
{
public CarIndex()
{
Map = cars => from car in cars
select new
{
car.Id,
car.Name,
car.PersonId,
};
}
}
The CarView class is identical to the Car class, but can be changed to better fit the indexing needs.
public class CarView
{
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set; }
}
You'll need to execute the index before being able to use it:
new CarIndex().Execute(store);
Loading the cars for a certain person would look like this:
using (IDocumentSession session = store.OpenSession())
{
session.Store(new Person { Id = 1, Name = "A", Surname = "A" });
session.Store(new Car { Id = 1, Name = "A", PersonId = 1 });
session.Store(new Car { Id = 2, Name = "B", PersonId = 1 });
session.Store(new Car { Id = 3, Name = "C", PersonId = 2 });
session.SaveChanges();
}
WaitForIndexing(store); // from RavenTestBase
using (IDocumentSession session = store.OpenSession())
{
var resultsForId1 = session
.Query<CarView, CarIndex>()
.ProjectFromIndexFieldsInto<CarView>()
.Where(x => x.PersonId == 1);
Assert.Equal(2, resultsForId1.Count());
var resultsForId2 = session
.Query<CarView, CarIndex>()
.ProjectFromIndexFieldsInto<CarView>()
.Where(x => x.PersonId == 2);
Assert.Equal(1, resultsForId2.Count());
}
If you want to load the person and their cars in a single database request, use lazy loading:
var resultsForId1 = session
.Query<CarView, CarIndex>()
.ProjectFromIndexFieldsInto<CarView>()
.Where(x => x.PersonId == 1).Lazily();
var person = session.Advanced.Lazily.Load<Person>(1);
var personValue = person.Value;
var resultsValue = resultsForId1.Value;
Complete test (needs xunit and RavenDB.Tests.Helpers nugets):
using Raven.Client;
using Raven.Client.Indexes;
using Raven.Tests.Helpers;
using System;
using System.Linq;
using Xunit;
namespace SO41547501Answer
{
public class SO41547501 : RavenTestBase
{
[Fact]
public void SO41547501Test()
{
using (var server = GetNewServer())
using (var store = NewRemoteDocumentStore(ravenDbServer: server))
{
new CarIndex().Execute(store);
using (IDocumentSession session = store.OpenSession())
{
session.Store(new Person { Id = 1, Name = "A", Surname = "A" });
session.Store(new Car { Id = 1, Name = "A", PersonId = 1 });
session.Store(new Car { Id = 2, Name = "B", PersonId = 1 });
session.Store(new Car { Id = 3, Name = "C", PersonId = 2 });
session.SaveChanges();
}
WaitForAllRequestsToComplete(server);
WaitForIndexing(store);
using (IDocumentSession session = store.OpenSession())
{
var resultsForId1 = session
.Query<CarView, CarIndex>()
.ProjectFromIndexFieldsInto<CarView>()
.Where(x => x.PersonId == 1);
Assert.Equal(2, resultsForId1.Count());
var resultsForId2 = session
.Query<CarView, CarIndex>()
.ProjectFromIndexFieldsInto<CarView>()
.Where(x => x.PersonId == 2);
Assert.Equal(1, resultsForId2.Count());
}
using (IDocumentSession session = store.OpenSession())
{
server.Server.ResetNumberOfRequests();
var resultsForId1 = session
.Query<CarView, CarIndex>()
.ProjectFromIndexFieldsInto<CarView>()
.Where(x => x.PersonId == 1).Lazily();
var person = session.Advanced.Lazily.Load<Person>(1);
var personValue = person.Value;
var resultsValue = resultsForId1.Value;
Assert.Equal("A", personValue.Name); // person data loaded
Assert.Equal("A", resultsValue.First().Name); // cars data loaded
Assert.Equal(1, server.Server.NumberOfRequests); // only one request sent to the server
}
}
}
}
public class CarIndex : AbstractIndexCreationTask<Car, CarView>
{
public CarIndex()
{
Map = cars => from car in cars
select new
{
car.Id,
car.Name,
car.PersonId,
};
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime? BirthDate { get; set; }
}
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set; }
}
public class CarView
{
public int Id { get; set; }
public string Name { get; set; }
public int PersonId { get; set; }
}
}

Entity Framework related entities on object is not updated

Whenever I try to change Department and Position objects of the Employee class they are not updating inside database on the Employee entity. For example, I want to change employee Position from junior (id - 1, name of the position - junior) to middle (id - 2, name of the position - middle). But, whenever I try to save this changes to the database they don't apply.
My Employee entity
public class Employee
{
public int EmployeeId { get; set; }
public string FullName { get; set; }
public Department Department { get; set; }
public EmployeePosition Position { get; set; }
public string Head { get; set; }
public double Salary { get; set; }
public Employee()
{
Department = new Department();
Position = new EmployeePosition();
}
}
EmployeePosition entity
public class EmployeePosition
{
public int Id { get; set; }
public string Name { get; set; }
}
My modify employee method in ViewModel
public void ModifyEmployee(int id)
{
Employee employee = employeeRepository.GetEmployee(id);
using (var context = new PowerCoEntity())
{
employee.Position = context.EmployeePositions.FirstOrDefault(d => d.Id == SelectedPositionId);
employee.Department = context.Deprtments.FirstOrDefault(d => d.DepartmentId == SelectedDepartmentId);
if (SelectedHeadId != null)
employee.Head = employeeRepository.GetHeadName(SelectedHeadId.Value);
else employee.Head = "";
}
employee.Salary = Employee.Salary;
employee.FullName = Employee.FullName;
employeeRepository.ModifyEmployee(employee);
}
Modify method in the context class
public void ModifyEmployee(Employee employee)
{
using (var context = new PowerCoEntity())
{
context.Entry(employee).State = EntityState.Modified;
context.SaveChanges();
}
}
UPDATE 1
I got it working by changing ModifyEmployee methods in the ViewModel and Context class. But I need to pass all the parameters through method now. How can I be able to use different contexts to modify object?
Context class:
public void ModifyEmployee(int id, int selectedPositionId, int selectedDepartmentId, int? selectedHeadId, Employee employee)
{
using (var context = new PowerCoEntity())
{
Employee emp = context.Employees.FirstOrDefault(e=> e.EmployeeId == 32);
emp.Salary = employee.Salary;
emp.FullName = employee.FullName;
emp.Department = employee.Department;
emp.Position = context.EmployeePositions.FirstOrDefault(d => d.Id == selectedPositionId);
emp.Department = context.Deprtments.FirstOrDefault(d => d.DepartmentId == selectedDepartmentId);
if (selectedHeadId != null)
emp.Head = GetHeadName(selectedHeadId.Value);
else emp.Head = "";
emp.Salary = employee.Salary;
emp.FullName = employee.FullName;
context.Entry(emp).State = EntityState.Modified;
context.SaveChanges();
}
}
ViewModel:
public void ModifyEmployee(int id)
{
Employee employee = new Employee();
employee.Salary = Employee.Salary;
employee.FullName = Employee.FullName;
employeeRepository.ModifyEmployee(id, SelectedPositionId, SelectedDepartmentId, SelectedHeadId, employee);
}

Entity Framework Create Audit Table/History table? [duplicate]

This question already has answers here:
how to create an audit trail with Entity framework 5 and MVC 4
(8 answers)
Closed 2 years ago.
I want to create a History/Audit Table for a particular entity. This is a complex entity with many child tables and we are using Repository Patter for our application.
I looked into overriding DbContext SaveChanges?. Is it good practice to use this specially for one entity?.
What are my other options?.
Thanks in advance.
I've been working on a library that might help.
Take a look at Audit.EntityFramework library, it intercepts SaveChanges() and can be configured to filter the entities you want to audit.
#thepirat000 solution probably works fine but I l like to have a minimum of NuGet dependencies, preferably 0, that are not backed by a large community/corporation and that depends heavily on a single developer.
https://github.com/thepirat000/Audit.NET/graphs/contributors
You can do it like this without any external library:
using (var context = new SampleContext())
{
// Insert a row
var customer = new Customer();
customer.FirstName = "John";
customer.LastName = "doe";
context.Customers.Add(customer);
await context.SaveChangesAsync();
// Update the first customer
customer.LastName = "Doe";
await context.SaveChangesAsync();
// Delete the customer
context.Customers.Remove(customer);
await context.SaveChangesAsync();
}
Model:
public class Audit
{
public int Id { get; set; }
public string TableName { get; set; }
public DateTime DateTime { get; set; }
public string KeyValues { get; set; }
public string OldValues { get; set; }
public string NewValues { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class SampleContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Audit> Audits { get; set; }
}
DbContext:
public class SampleContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Audit> Audits { get; set; }
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
var auditEntries = OnBeforeSaveChanges();
var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await OnAfterSaveChanges(auditEntries);
return result;
}
private List<AuditEntry> OnBeforeSaveChanges()
{
ChangeTracker.DetectChanges();
var auditEntries = new List<AuditEntry>();
foreach (var entry in ChangeTracker.Entries())
{
if (entry.Entity is Audit || entry.State == EntityState.Detached || entry.State == EntityState.Unchanged)
continue;
var auditEntry = new AuditEntry(entry);
auditEntry.TableName = entry.Metadata.Relational().TableName;
auditEntries.Add(auditEntry);
foreach (var property in entry.Properties)
{
if (property.IsTemporary)
{
// value will be generated by the database, get the value after saving
auditEntry.TemporaryProperties.Add(property);
continue;
}
string propertyName = property.Metadata.Name;
if (property.Metadata.IsPrimaryKey())
{
auditEntry.KeyValues[propertyName] = property.CurrentValue;
continue;
}
switch (entry.State)
{
case EntityState.Added:
auditEntry.NewValues[propertyName] = property.CurrentValue;
break;
case EntityState.Deleted:
auditEntry.OldValues[propertyName] = property.OriginalValue;
break;
case EntityState.Modified:
if (property.IsModified)
{
auditEntry.OldValues[propertyName] = property.OriginalValue;
auditEntry.NewValues[propertyName] = property.CurrentValue;
}
break;
}
}
}
// Save audit entities that have all the modifications
foreach (var auditEntry in auditEntries.Where(_ => !_.HasTemporaryProperties))
{
Audits.Add(auditEntry.ToAudit());
}
// keep a list of entries where the value of some properties are unknown at this step
return auditEntries.Where(_ => _.HasTemporaryProperties).ToList();
}
private Task OnAfterSaveChanges(List<AuditEntry> auditEntries)
{
if (auditEntries == null || auditEntries.Count == 0)
return Task.CompletedTask
foreach (var auditEntry in auditEntries)
{
// Get the final value of the temporary properties
foreach (var prop in auditEntry.TemporaryProperties)
{
if (prop.Metadata.IsPrimaryKey())
{
auditEntry.KeyValues[prop.Metadata.Name] = prop.CurrentValue;
}
else
{
auditEntry.NewValues[prop.Metadata.Name] = prop.CurrentValue;
}
}
// Save the Audit entry
Audits.Add(auditEntry.ToAudit());
}
return SaveChangesAsync();
}
}
public class AuditEntry
{
public AuditEntry(EntityEntry entry)
{
Entry = entry;
}
public EntityEntry Entry { get; }
public string TableName { get; set; }
public Dictionary<string, object> KeyValues { get; } = new Dictionary<string, object>();
public Dictionary<string, object> OldValues { get; } = new Dictionary<string, object>();
public Dictionary<string, object> NewValues { get; } = new Dictionary<string, object>();
public List<PropertyEntry> TemporaryProperties { get; } = new List<PropertyEntry>();
public bool HasTemporaryProperties => TemporaryProperties.Any();
public Audit ToAudit()
{
var audit = new Audit();
audit.TableName = TableName;
audit.DateTime = DateTime.UtcNow;
audit.KeyValues = JsonConvert.SerializeObject(KeyValues);
audit.OldValues = OldValues.Count == 0 ? null : JsonConvert.SerializeObject(OldValues);
audit.NewValues = NewValues.Count == 0 ? null : JsonConvert.SerializeObject(NewValues);
return audit;
}
}
Source:
https://www.meziantou.net/entity-framework-core-history-audit-table.htm and comment from #rasputino
You can also read more about Slowly changing dimension types and from there create a solution that fits your needs.
If you need entire Entity Framework Snapshot History look at this answer.

Unable to set field/property CartItems on entity type BusinessEntitys.Product

I spent many hours (including wide Google search) trying to figure out what the problem is. Unfortunately, without success.
The error that I get is: "Unable to set field/property CartItems on entity type BusinessEntitys.Product. See InnerException for details."
the innerExeption is null.
The error occurs at addToCart() function when I try to call a function addCartItem through the WCF. When the call get to DAL and try to save the new object in db context it's failed.
public void AddToCart(int id)
{
// Retrieve the product from the database.
ShoppingCartId = GetCartId();
CartItem cartItem = new BLFrontend().getAllCartItems().SingleOrDefault(
c => c.CartItemId == ShoppingCartId
&& c.ProductId == id);
if (cartItem == null)
{
Product temp = new BLFrontend().getAllProducts().SingleOrDefault(
p => p.ProductId == id);
// Create a new cart item if no cart item exists.
cartItem = new CartItem
{
CartItemId = Guid.NewGuid().ToString(),
ProductId = id,
CartId_ = ShoppingCartId,
Product = temp,
Quantity = 1,
};
new BLFrontend().addCartItem(cartItem);//The problem starts from here
}
else
{
cartItem.Quantity++;
//update
}
I'm using EF in my project, this is the Model:
namespace BusinessEntitys
{
using System;
using System.Collections.Generic;
public partial class CartItem
{
public string CartItemId { get; set; }
public string CartId_ { get; set; }
public int Quantity { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
}
class Product that contained in CratItem:
namespace BusinessEntitys
{
using System;
using System.Collections.Generic;
using System.Linq; // added this
public partial class Product
{
public Product()
{
this.ItemInOrders = new HashSet<ItemInOrder>();
this.CartItems = new HashSet<CartItem>();
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public double ProductBasePrice { get; set; }
public double ProductDiscount { get; set; }
public string ProductDescription { get; set; }
public string ProductImageURL { get; set; }
public string ProductQualityLevel { get; set; }
public string ProductCategory { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
}
}
My BusinessEntitys is:
namespace BusinessEntitys
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class StoreDBEntities : DbContext
{
public StoreDBEntities()
: base("name=StoreDBEntities")
{
base.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Client> Clients { get; set; }
public virtual DbSet<ItemInOrder> ItemInOrders { get; set; }
public virtual DbSet<Order> Orders { get; set; }
public virtual DbSet<Product> Products { get; set; }
public virtual DbSet<StockManagment> StockManagments { get; set; }
public virtual DbSet<CartItem> CartItems { get; set; }
}
}
Execption occurs here in my DAL at 2nd line when trying to execute the command:
context.CartItems.Add(CartItemToAdd)
public bool addCartItem(CartItem CartItemToAdd)
{
try
{
var context = new StoreDBEntities();
context.CartItems.Add(CartItemToAdd);
return context.SaveChanges() > 0;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
The stackTracer:
StackTrace "at DataAccess.Dal.addCartItem(CartItem CartItemToAdd) in
c:\Users\User\Documents\Visual Studio
2013\Projects\OrGarden\DataAccess\Dal.cs:line 406\r\n at
BusinessLogicBackend.BLBackend.addCartItem(CartItem cartItemToAdd) in
c:\Users\User\Documents\Visual Studio
2013\Projects\OrGarden\BusinessLogicBackend\BLBackend.cs:line
293\r\n at SyncInvokeaddCartItem(Object , Object[] , Object[] )\r\n
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object
instance, Object[] inputs, Object[]& outputs)\r\n at
System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&
rpc)" string
Important Update: When i deleted this assignment Product = temp, the addCartItem call succeed saved without a problem. Only that the field Product remains null.
The code after that change:
Product temp = business.getAllProducts().SingleOrDefault(
p => p.ProductId == id);
// Create a new cart item if no cart item exists.
cartItem = new CartItem
{
CartItemId = Guid.NewGuid().ToString(),
ProductId = id,
CartId_ = ShoppingCartId,
Product = temp,
Quantity = 1,
};
business.addCartItem(cartItem);
Whene i retrieved CrartItem from the context the field Product remains null.
Can anyone help me fix that bad problem??
Thank you very much,
Ron
First of all, I strongly recommend you take a look at the Unit of Work pattern (MSDN version, if you prefer).
The problem is, you're creating a new instance of BLFrontEnd and StoreDBEntities every time. It seems that there's no reason to create the instances so.
I'm not sure what exactly going on inside the BLFrontEnd, but I bet every methods on the BLFrontEnd also create a new StoreDBEntities instance. And that might be the cause of the exception.
You need to manage the life cycle of instances. look at this:
public void AddToCart(int id)
{
// Begins new life-cycle of BLFrontend
var frontend = new BLFrontend();
// or you can wrap it using statement.
// using(var frontend = new BLFrontend())
// {
// ... code ...
// }
// Retrieve the product from the database.
ShoppingCartId = GetCartId();
CartItem cartItem = frontend.getAllCartItems().SingleOrDefault(
c => c.CartItemId == ShoppingCartId
&& c.ProductId == id);
if (cartItem == null)
{
Product temp = frontend.getAllProducts().SingleOrDefault(
p => p.ProductId == id);
// Create a new cart item if no cart item exists.
cartItem = new CartItem
{
CartItemId = Guid.NewGuid().ToString(),
ProductId = id,
CartId_ = ShoppingCartId,
Product = temp,
Quantity = 1,
};
frontend.addCartItem(cartItem);//The problem starts from here
}
else
{
cartItem.Quantity++;
//update
}
// end of life-cycle of the instance.
frontend.Dispose();
}
public class BLFrontEnd : IDispose
{
private readonly StoreDBEntities dbContext;
public BLFrontEnd()
{
dbContext = new StoreDBEntities();
}
public void Dispose()
{
dbContext.Dispose();
}
public IQueryable<Cart> getAllCartItems()
{
// your logics...
return dbContext.Carts.AsQueryable();
}
public IQueryable<Product> getAllProducts()
{
// your logics...
return dbContext.Products.AsQueryable();
}
public bool addCartItem(CartItem CartItemToAdd)
{
try
{
//var context = new StoreDBEntities();
dbContext.CartItems.Add(CartItemToAdd);
return context.SaveChanges() > 0;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
As you can see, 'A work' is beginning at the head of AddToCart() method, and ended at the tail of the method. That is the life-cycle of 'A work'. During the life-cycle, every resources are in the same context. That means every code in the life-cycle will use same instance of resources. It avoids unnecessary creation of new instance.
This code isn't that good, and you need to refactor in order to meet your use-cases and the architecture. But I hope you got the point.
Edited
Okay, to separate actual problem with your BL, could you try this:
public void AddToCart(int id)
{
// use StoreDBEntities directly, instead of BLFrontend.
using(var dbContext = new StoreDBEntities())
{
// Retrieve the product from the database.
ShoppingCartId = 123; // use magic number for this test.
// and use dbContext directly.
CartItem cartItem = dbContext.Carts.SingleOrDefault(
c => c.CartItemId == ShoppingCartId
&& c.ProductId == id);
if (cartItem == null)
{
Product temp = dbContext.Products.SingleOrDefault(
p => p.ProductId == id);
// Create a new cart item if no cart item exists.
cartItem = new CartItem
{
CartItemId = Guid.NewGuid().ToString(),
ProductId = id,
CartId_ = ShoppingCartId,
Product = temp,
Quantity = 1,
};
dbContext.Carts.Add(cartItem);
}
else
{
cartItem.Quantity++;
//update
}
dbContext.SaveChanges();
}
}

Nested LINQ query selection

Please consider following example class construct:
public class Company
{
public string CompanyName { get; set; }
public List<Subdivision> Subdivisions { get; set; }
}
public class Subdivision
{
public string SubdivisionName { get; set; }
public List<Employee> Employees { get; set; }
}
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
}
Example variable of a List:
List<Company> CompanyList = new List<Company>();
CompanyList.Add(new Company
{
CompanyName = "TestCompany",
Subdivisions = new List<Subdivision>
{
{ new Subdivision
{
SubdivisionName = "TestSubdivision",
Employees = new List<Employee>
{
{ new Employee
{
EmployeeID = 1,
EmployeeName = "John"
}
}
}
}}
}
});
I want to get the EmployeeName just by EmployeeID. Consider this code:
if (CompanyList.Any(x => x.Subdivisions.Any(y => y.Employees.Any(z => z.EmployeeID == 1))))
{
int i1 = CompanyList.IndexOf(CompanyList.Where(x => x.Subdivisions.Any(y => y.Employees.Any(z => z.EmployeeID == 1))).Select(x => x).First());
int i2 = CompanyList[i1].Subdivisions.IndexOf(CompanyList[i1].Subdivisions.Where(x => x.Employees.Any(z => z.EmployeeID == 1)).Select(x => x).First());
int i3 = CompanyList[i1].Subdivisions[i2].Employees.IndexOf(CompanyList[i1].Subdivisions[i2].Employees.Where(z => z.EmployeeID == 1).Select(x => x).First());
string i = CompanyList[i1].Subdivisions[i2].Employees[i3].EmployeeName;
Console.WriteLine(i);
}
else
{
Console.WriteLine("Employee with ID 1 not found!");
}
This works just fine; however, it seems rather bloated up if I just want to retrieve a piece of data without getting the indexes. Is there any other approach to this?
You can use SelectMany to search for all employees in all divisions in all companies and then use FirstOrDefault to make sure null would be returned if no employee would be found
var employee = CompanyList.SelectMany(company => company.Subdivisions.SelectMany(division => division.Employees))
.FirstOrDefault(emp => emp.EmployeeID == 1);
if (employee != null)
{
Console.WriteLine(employee.EmployeeName); //prints John
}
else
{
Console.WriteLine("Employee with ID 1 not found!");
}

Categories