Entity Framework generic recursive saving C# - c#

I have been asked to create a generic library that will be able to handle saving objects while they are not in trackable state using EF.
This is my object
[Table("Address")]
public class Address : Entity
{
public string Name { get; set; }
public virtual Country Country { get; set; }
}
And object Get<Country>(false) indicates that the object is as AsNoTracking state
var user = new User() { UserName = "Admin" };
user.Adress = new System.Collections.Generic.List<Address>() { new Address() { Name = "Line 3", Country = repository.Get<Country>(false).First() } };
user.Adress.First().Country.Name = "sdsdsd"; // Only update!!!
repository.Save(user);
My saving method
/// <summary>
/// Update or insert record, this will loop throw all object recursive and find out items state
/// and do update or insert or delete, depending on item primary key and ItemState (optional to be included in the object)
/// be aware that deleting an object will also result to delete all other objects that are connected to it.
/// be also sure to include all objects that are depending on the current object when you removing an item, or else a dependency exception will be thrown in
/// </summary>
/// <param name="iEntityList"></param>
/// <param name="saveChanges">false if handling commit outside the function</param>
/// <returns></returns>
protected IEntity Save(List<IEntity> iEntityList, bool saveChanges = true, ItemState? overrideState = null, bool isExternalData = false)
{
if (iEntityList == null || !iEntityList.Any())
return null;
if (overrideState != null)
iEntityList.ForEach(x => x.State = overrideState.Value);
var db = context.Set(iEntityList.First().GetType());
foreach (var entry in iEntityList)
{
var state = entry.State;
if (state == ItemState.Removed && entry.ID <= 0) // no need to be included in Transaction. it dose not exist in the db anyway
continue;
DbEntityEntry<IEntity> obj = context.Entry(entry);
if (state == ItemState.Added)
{
if (entry.ID > 0 && (obj.State == EntityState.Detached || obj.State == EntityState.Added || isExternalData))
{
if (isExternalData)
obj.State = EntityState.Unchanged;
var existingItem = db.Find(entry.ID);
obj = context.Entry(existingItem as IEntity);
if (isExternalData)
{
obj.State = EntityState.Detached;
continue;
}
obj.CurrentValues.SetValues(entry);
obj.State = EntityState.Modified;
}
foreach (var prop in entry.GetType().GetProperties().
Where(x =>
(x.GetCustomAttribute<ExecludeFromEntityFramework>(false) == null) &&
(x.PropertyType.IsGenericList() ||
(x.PropertyType.IsClass && x.PropertyType != typeof(string)))))
{
if (!prop.CanRead || !prop.CanWrite || prop.GetMethod.IsStatic || prop.SetMethod.IsStatic)
continue;
var propValue = entry.GetProperty(prop.Name).GetValue(entry, null);
if (propValue != null)
{
List<IEntity> value;
if (prop.PropertyType.IsGenericList())
value = (propValue as System.Collections.IList).Cast<IEntity>().ToList();
else value = (propValue as IEntity).Convert(prop.PropertyType).Cast<IEntity>().ToList();
if (value != null && value.Any())
{
if (prop.PropertyType.IsGenericList())
{
if (value.Any(x => x.ID <= 0 && x.State != ItemState.Removed) && prop.GetCustomAttribute<ExternalData>() == null)
obj.Entity.GetProperty(prop.Name, value.FindAll(x => x.ID <= 0 && x.State != ItemState.Removed).Convert(propValue.GetType()));
}
else
if (value.First().ID <= 0 && value.First().State != ItemState.Removed && prop.GetCustomAttribute<ExternalData>() == null)
obj.Entity.GetProperty(prop.Name, value.First());
Save(value, false, null, prop.GetCustomAttribute<ExternalData>() != null);
}
}
}
obj.State = entry.ID > 0 ? EntityState.Modified : EntityState.Added;
}
else
{
if (obj.State == EntityState.Detached || isExternalData)
{
if (isExternalData)
obj.State = EntityState.Unchanged;
var existingItem = db.Find(entry.ID);
obj = context.Entry(existingItem as IEntity);
if (isExternalData)
{
obj.State = EntityState.Detached;
continue;
}
obj.CurrentValues.SetValues(entry);
}
foreach (var prop in entry.GetType().GetProperties().
Where(x =>
(x.GetCustomAttribute<ExecludeFromEntityFramework>(false) == null) &&
(x.PropertyType.IsGenericList() ||
(x.PropertyType.IsClass && x.PropertyType != typeof(string)))))
{
if (!prop.CanRead || !prop.CanWrite || prop.GetMethod.IsStatic || prop.SetMethod.IsStatic)
continue;
var propValue = entry.GetProperty(prop.Name).GetValue(entry, null);
if (propValue != null)
{
List<IEntity> value;
if (prop.PropertyType.IsGenericList())
value = (propValue as System.Collections.IList).Cast<IEntity>().ToList();
else value = (propValue as IEntity).Convert(prop.PropertyType).Cast<IEntity>().ToList();
Save(value, false, ItemState.Removed, prop.GetCustomAttribute<ExternalData>() != null);
}
}
obj.State = EntityState.Deleted;
}
}
if (saveChanges)
{
this.SaveChanges();
return iEntityList.FirstOrDefault();
}
else
return iEntityList.FirstOrDefault();
}
The problem is that Country has the primary key ID so it should only be updated but for some reason when i indicate that address is new object Country state changes to new to so I will get new object of type Country in the database.
I only need to have new address and a reference to the country.
If Country.ID == 0 then mark country as new and add it to the database

Related

Getting System.StackOverflowException , in a recursive function call?

I have to two function which is used to find tags inside the tags like, there is a tag A=B(C(D(E))) so i have to find all the tags inside B then all the tags inside C and so on. I write two function but getting the error System.StackOverflowException. In the first function i am providing the tag ID and against that tag id i am getting getNestesCalTagsId and then calling the getNestedCalTagsIngredients() function. But when there are lot of recursion calls i get the error System.StackOverflowException. Below is my whole code.
public List<int?> getNestedCalTags(int? calTagId)
{
var getNestesCalTagsId = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == calTagId && x.Status == true && x.Cal_Tag_Id_FK!=null).Select(x => x.Cal_Tag_Id_FK).ToList();
if (getNestesCalTagsId.Count > 0)
{
nestedCalFTags.AddRange(getNestesCalTagsId);
foreach (var item in getNestesCalTagsId)
{
if (item != null)
{
getNestedCalTagsIngredients(item.Value);
}
}
}
if (nestedCalFTags.Count > 0)
{
int countedTags = nestedCalFTags.Count;
List<int?> tags = new List<int?>(nestedCalFTags);
for (int i = 0; i < tags.Count; i++)
{
if (tags[i] != null)
{
getNestedCalTagsIngredients(tags[i].Value);
}
}
}
return nestedRawTags;
}
public bool getNestedCalTagsIngredients(int nestCalTagId)
{
var getCalTags = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == nestCalTagId && x.Status == true).ToList();
if (getCalTags.Count > 0)
{
foreach (var item in getCalTags)
{
if (item.Cal_Tag_Id_FK != null)
{
var getNestedCalTagParent = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == item.Cal_Tag_Id_FK && x.Status == true && x.Cal_Tag_Id_FK!=null).Select(x => x.Cal_Tag_Id_FK).ToList();
if (getNestedCalTagParent != null)
{
nestedCalFTags.AddRange(getNestedCalTagParent);
getNestedCalTags(item.Cal_Tag_Id_FK);
}
}
else
{
var rawTagId = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == item.Cal_Tag_P_Id && x.Real_Tag_Id_FK!=null).Select(x => x.Real_Tag_Id_FK).ToList();
if (rawTagId != null)
{
foreach (var rawItem in rawTagId)
{
if (rawItem!=null)
{
if (nestedRawTags.IndexOf(rawItem.Value) == -1)
{
nestedRawTags.Add(rawItem.Value);
}
}
}
}
nestedCalFTags.Remove(nestCalTagId);
}
}
}
return true;
}

How to overcome foreach loop for list object dynamically

I'm swapping my values in List Object on some conditions and update List Object value.
Currently, what I'm doing is
- Looping on each object through List
- Check If condition is net
- Swap values
public static void SwapMinMaxIfNull<T>(this IEnumerable<T> rows, string reportfor)
{
if (reportfor.Equals("Comparison"))
{
var row = rows as IEnumerable<RiskBoardDataToExport>;
try
{
if (rows.Any())
{
var Tests = row.Where(min => min.MinGaitSpeed == null && min.MaxGaitSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinGaitSpeed = test.MaxGaitSpeed;
test.MaxGaitSpeed = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinTUGTime == null && min.MaxTUGTime != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinTUGTime = test.MaxTUGTime;
test.MaxTUGTime = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinBergScoreSpeed == null && min.MaxBergScoreSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinBergScoreSpeed = test.MaxBergScoreSpeed;
test.MaxBergScoreSpeed = null;
}
}
//.. for brevity
}
}
}
Can I do it in better way? I know about PropertyInfo i.e. Can check property name and get value etc, but, not having any hint to get this done.
Thanks
It's not exactly what you're asking for, but you can combine the clauses in your Where statements and then have a few if statements in the body:
public static void SwapMinMaxIfNull(this IEnumerable<RiskBoardDataToExport> rows,
string reportfor)
{
if (rows = null) return;
if (reportfor.Equals("Comparison", StringComparison.OrdinalIgnoreCase))
{
foreach (var row in rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null)))
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
}
}
As this is an operation where order of the items in the list does not matter, you can easily speed this up by parallelization (you can read up on that here).
So, what you should do, is handle this foreach loop in a parallel way and combine it with Rufus L's optimized code for the fastest result.
var rows = rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null))
Parallel.ForEach(rows, (row) => {
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
Note that this requires the System.Threading.Tasks namespace, that's where the Parallel class is.

how can i get property and value of a class by reflection

i started with this! and i would like to avoid this
private IQueryable<Customer> FilterResult(string search, List<Customer> dtResult, List<string> columnFilters)
{
IQueryable<Customer> results = dtResult.AsQueryable();
results = results.Where(p => (search == null || (p.Name != null && p.Name.ToLower().Contains(search.ToLower()) || p.City != null && p.City.ToLower().Contains(search.ToLower())
&& (columnFilters[0] == null || (p.Name != null && p.Name.ToLower().Contains(columnFilters[0].ToLower())))
&& (columnFilters[1] == null || (p.City != null && p.City.ToLower().Contains(columnFilters[1].ToLower())))
);
return results;
}
by using reflection maybe.... because imagine i have 100 properties it would be easy to mess up ...so i tried this way i would like to use reflection to loop on all properties instead of making references to each of them(properties)
public IQueryable<Aangifte> FilterColumn<T>()
{
try
{
List<Aangifte> v = AangifteGetData.GetData(StartDate, EndDate);
List<Aangifte> v2 = new List<Aangifte>();
Aangifte Foo = new Aangifte();
List<string> propertEntity = new List<string>();
var search = Request.Form.GetValues("search[value]").FirstOrDefault();
int count = -1;
var results = v.AsQueryable();
List<string> columnName = new List<string>();
foreach (var prop in Foo.GetType().GetProperties())
{
var t = "";
if (!prop.Name.Contains("FORMAT"))
{
TVALUE = prop.GetValue(Foo, null);
t= prop.Name;
propertEntity.Add(t);
count++;
}
if (count < propertEntity.Count())
{
var newt = t;
var Z = Request.Form.GetValues("columns[" + count + "][search][value]").FirstOrDefault();
results = results.Where
(
p => (search == null || (t != null && t.ToLower().Contains(search.ToLower())))
&& (Request.Form.GetValues("columns[" + count + "][search][value]").FirstOrDefault() == null || (t != null && t.ToLower().Contains(Request.Form.GetValues("columns[" + count + "][search][value]").FirstOrDefault().ToLower())))
);
}
}
return results;
}
catch (Exception EX)
{
throw EX;
}
}

Update m-to-m relationship, duplicate primary key

i am trying to update a m-to-m relation with a ListBox. My entity-model looks like this:
I have a ListBox with Checkboxes where the user can decide which Player is in the league and which is not (IsSelected-Property). There are two problems: At first i can't check and then uncheck a Player (it won't be deleted). Second Problem: the first try, everything works and when i do the selection again, i get the following exception:
_innerException {"An error occurred while updating the entries. See the inner exception for details."} System.Exception {System.Data.Entity.Core.UpdateException}
_innerException {"Violation of PRIMARY KEY constraint 'PLID'. Cannot insert duplicate key in object 'dbo.PlayerLeague'. The duplicate key value is (2, 2).\r\nThe statement has been terminated."} System.Exception {System.Data.SqlClient.SqlException}
using (BettingLeagueEntities entities = new BettingLeagueEntities())
{
foreach (PlayerCheckBoxList p in this.PlayerList)
{
if(p.IsSelected == true)
{
PlayerLeague pl = new PlayerLeague();
pl.League = this.ActiveLeague;
pl.Player = p.ActivePlayer;
var local = entities.Set<Player>().Local.FirstOrDefault(x => x.PID == p.ActivePlayer.PID);
if(local != null)
{
entities.Entry(local).State = System.Data.Entity.EntityState.Detached;
}
var localLeague = entities.Set<League>().Local.FirstOrDefault(x => x.LID == this.ActiveLeague.LID);
if (localLeague != null)
{
entities.Entry(localLeague).State = System.Data.Entity.EntityState.Detached;
}
if (entities.Entry(p.ActivePlayer).State == System.Data.Entity.EntityState.Detached)
{
entities.Player.Add(p.ActivePlayer);
entities.Entry(p.ActivePlayer).State = System.Data.Entity.EntityState.Modified;
}
if (entities.Entry(this.ActiveLeague).State == System.Data.Entity.EntityState.Detached)
{
entities.League.Add(this.ActiveLeague);
entities.Entry(this.ActiveLeague).State = System.Data.Entity.EntityState.Modified;
}
if(p.ActivePlayer.PlayerLeague.All(x => x.LID != this.ActiveLeague.LID))
{
p.ActivePlayer.PlayerLeague.Add(pl);
this.ActiveLeague.PlayerLeague.Add(pl);
}
}
else
{
PlayerLeague local = entities.Set<PlayerLeague>().Local.FirstOrDefault(x => x.LID == this.ActiveLeague.LID && x.PID == p.ActivePlayer.PID);
if(local != null)
{
entities.PlayerLeague.Attach(local);
entities.PlayerLeague.Remove(local);
}
entities.SaveChanges();
}
}
entities.SaveChanges();
}
I have no clue how to solve this, do you have any suggestions?
I have it! I tried to comment a little bit to make it understandable.
The first problem was that i checked if my PlayerLeague already exists too lately. I moved this condition in my first if(statement).
The second error was, that in my else block, my statement to find a playerleague returned alsways null. Now i check if there is any entity and if this is true, i delete it.
using (BettingLeagueEntities entities = new BettingLeagueEntities())
{
foreach (PlayerCheckBoxList p in this.PlayerList)
{
// Check if the Player is seleceted and if the ActivePlayer has the Active League
if (p.IsSelected == true && p.ActivePlayer.PlayerLeague.All(x => x.LID != this.ActiveLeague.LID))
{
// Define the new PlayerLeague
PlayerLeague pl = new PlayerLeague {PID = p.ActivePlayer.PID, LID = this.ActiveLeague.LID};
var localPlayer = entities.Set<Player>().Local.FirstOrDefault(x => x.PID == p.ActivePlayer.PID);
if (localPlayer != null)
{
entities.Entry(localPlayer).State = System.Data.Entity.EntityState.Detached;
}
if (entities.Entry(p.ActivePlayer).State == System.Data.Entity.EntityState.Detached)
{
entities.Player.Add(p.ActivePlayer);
entities.Entry(p.ActivePlayer).State = System.Data.Entity.EntityState.Modified;
}
var localLeague = entities.Set<League>().Local.FirstOrDefault(x => x.LID == this.ActiveLeague.LID);
if (localLeague != null)
{
entities.Entry(localLeague).State = System.Data.Entity.EntityState.Detached;
}
if (entities.Entry(this.ActiveLeague).State == System.Data.Entity.EntityState.Detached)
{
entities.League.Add(this.ActiveLeague);
entities.Entry(this.ActiveLeague).State = System.Data.Entity.EntityState.Modified;
}
p.ActivePlayer.PlayerLeague.Add(pl);
this.ActiveLeague.PlayerLeague.Add(pl);
}
else
{
// Check if there is a PlayerLeague for this Player and league
bool hasPlayerLeague =
entities.PlayerLeague.Any(x => x.LID == this.ActiveLeague.LID && x.PID == p.ActivePlayer.PID);
if (hasPlayerLeague && p.IsSelected == false)
{
// Find PlayerLeague
PlayerLeague pl =
entities.PlayerLeague.FirstOrDefault(
x => x.LID == this.ActiveLeague.LID && x.PID == p.ActivePlayer.PID);
// Attach and Remove PlayerLeague
entities.PlayerLeague.Attach(pl);
entities.PlayerLeague.Remove(pl);
}
entities.SaveChanges();
}
}
}

linq conditional query

What would be the best practice for setting a status depending on several other "columns" retrieved in a linq query.
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = 0
});
I'd like to set the status to either 0, 1, 2.
(ApprovedDate == null and DeclinedDate == null) --> 0
(ApprovedDate != null and DeclinedDate == null) --> 1
(DeclinedDate != null) --> 3
So perhaps something like:
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = (q.CreatedDate == null && q.DeclinedDate == null) ? 0 : (q.ApprovedDate != null && q.DeclinedDate == null) ? 1 : 2
});
I might add even more status combinations, so should I try and do this in the linq select query, in my repository object.. Or later on in the controller where I would do a .ToList() and then foreach the list to set the correct status code?
Having even more than 3 statuscodes, the linq query gets "hard" to read.
What about moving status calculation to Item class? If status property depends on other properties value, then it's definitely calculated property:
var result = from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate
});
And
public class Item
{
// other properties
public int Status
{
get
{
if (ApprovedDate == null and DeclinedDate == null)
return 0;
if (ApprovedDate != null and DeclinedDate == null)
return 1;
if (DeclinedDate != null)
return 3;
// etc
}
}
}
Actually I think it's best option, because in this case status calculation logic will be close to required data. If (for some reason) you can't use this approach, then move setting statuses to local items collection:
var items = result.ToList().ForEach(i => i.Status = CalculateStatus(i));
Maybe wrapped all in a function An do a linq like this
var result = (from q in query sele q).AsEnumerable()
.Select( x => new Item()
{
ApprovedDate = x.ApprovedDate,
CreatedDate = x.CreatedDate,
DeclinedDate = x.DeclinedDate,
Status = MyStatusFunction(x.CreatedDate,q.DeclinedDate)
});
public int MyStatusFunction(DateTime ApprovedDate , Datetime DeclinedDate)
{
if (ApprovedDate == null and DeclinedDate == null) return 0;
else if(ApprovedDate != null and DeclinedDate == null) return 1;
else if (DeclinedDate != null) return 3;
}

Categories