I have a problem when I update a line with a foreign key. The principle idea is to update a row in the database with a generic method but I have an exception when I save modification in the data base so I try to make the state of entity os modified but not the worker.
else if (ModeButtonVMCaracteristiquesType == ModeButtonVMCaracteristiqueType.EDITIONCaracteristiqueTypeItem)
{
int idCaracteristiqueSelected = Convert.ToInt32(CaracteristiqueSelected.idCharacteristicItem);
var LineModified = (from x in ImItemsModel.imtypeitems select x.imcharacteristicsitems).ToList();
LineModified.ForEach(p => ImItemsModel.Entry(p).State = System.Data.Entity.EntityState.Modified);
var UpdateCaracteristicTypeItem = StaticGenericUpdate.UpadateRowInModel<imcharacteristicsitem>(ImItemsModel, "idCharacteristicItem", idCaracteristiqueSelected, "fk_idTypeItemIMCaracteristicsItems", fk_value, propertiesForModel, propertiesForView, this);
ImItemsModel.SaveChanges();
So I have two models:
public partial class imcharacteristicsitem
{
public imcharacteristicsitem()
{
this.imvaluesofitemscaracteristics = new HashSet<imvaluesofitemscaracteristic>();
}
public int idCharacteristicItem { get; set; }
public string characteristicItem { get; set; }
public string unitCaracteristicItem { get; set; }
public int fk_idTypeItemIMCaracteristicsItems { get; set; }
public byte[] typeValueCaracteristicItem { get; set; }
public virtual ICollection<imvaluesofitemscaracteristic> imvaluesofitemscaracteristics { get; set; }
public virtual imtypeitems imtypeitem { get; set; }
}
and:
public partial class imtypeitems
{
public imtypeitems()
{
this.imcharacteristicsitems = new HashSet<imcharacteristicsitem>();
this.imitems = new HashSet<imitem>();
}
public int idTypeItem { get; set; }
public string DesignationTypeItem { get; set; }
public byte[] SymbolTypeItem { get; set; }
public Nullable<int> MaxNumberConnectionsTypeItem { get; set; }
public virtual ICollection<imcharacteristicsitem> imcharacteristicsitems { get; set; }
public virtual ICollection<imitem> imitems { get; set; }
}
and the generic method is:
public static T UpadateRowInModel<T>(DbContext Model, string NameiD, int IdSelected, string NameFk_key ,int fk_key, IList<PropertyInfo> propertiesModel, IList<PropertyInfo> propertiesView, VMCaracteristicType vm) where T : class
{
T item = Model.Set<T>().Find(IdSelected);
foreach (var property in propertiesModel)
{
if (propertiesView.Count != 0)
{
property.SetValue(item, propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name)) == null ? null :
propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name)).GetValue(vm));
}
if (property.Name == NameiD)
{
property.SetValue(item, IdSelected);
}
if (property.Name == NameFk_key)
{
property.SetValue(item, null);
Model.Entry(item).Property(NameFk_key).IsModified = false;
}
}
return item;
}
EDIT :
so i realise that the probleme is the entity framework can't save because my table imtypeitems have a collection of caractéristique
public virtual ICollection<imcharacteristicsitem> imcharacteristicsitems { get; set; }
so i must delet the row that i wish update it from this table and after that i will save so i can't delet the row of collection i try like this :
var RowBeforupdate = ImItemsModel.imcharacteristicsitems.Include("imtypeitem").Single(row => row.idCharacteristicItem == idCaracteristiqueSelected);
var UpdateCaracteristicTypeItem = StaticGenericUpdate.UpadateRowInModel<imcharacteristicsitem>(ImItemsModel, "idCharacteristicItem", idCaracteristiqueSelected, "fk_idTypeItemIMCaracteristicsItems", fk_value, propertiesForModel, propertiesForView, this);
ImItemsModel.Entry(RowBeforupdate).CurrentValues.SetValues(UpdateCaracteristicTypeItem);
just a ps : i am not a expert in entity framework :(
The most likely cause for the FK null exception is that the FK entity property is processed/set in the foreach loop after the FK_ID property. So even if you change the FK_ID IsModified value to false, you're most likely later setting the imtypeitem property to null.
Also you are changing all model properties based on view properties; so if there's no particular view property you'll be setting the model property to null. This is seldom the thing you want to be doing in this scenario. Usually you want to change just the corresponding view properties and leave the other model properties unchanged.
So the foreach part of your generic method should look like this:
if (propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name)) != null)
{
property.SetValue(item, propertiesView.First(elem => elem.Name.Equals(property.Name)).GetValue(vm));
}
If you have certain properties that are part of view properties but you don't want them to be updated you can just skip them (using your Nameid and NameFk_key):
if(property.Name == NameiD || property.Name == NameFk_key)
continue;
So the final form of the generic method might look something like this:
public static T UpadateRowInModel<T>(DbContext Model, string NameiD, int IdSelected, string NameFk_key ,int fk_key, IList<PropertyInfo> propertiesModel, IList<PropertyInfo> propertiesView, VMCaracteristicType vm) where T : class
{
T item = Model.Set<T>().Find(IdSelected);
foreach (var property in propertiesModel)
{
if(property.Name == NameiD || property.Name == NameFk_key)
continue;
var viewP = propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name));
if (viewP != null)
{
property.SetValue(item, viewP.GetValue(vm));
}
}
return item;
}
There's also a variation possible where you do a foreach on the propertiesView; also the parameters for the method can be adjusted.
On a sidenote: I'm not sure if
var LineModified = (from x in ImItemsModel.imtypeitems select x.imcharacteristicsitems).ToList();
LineModified.ForEach(p => ImItemsModel.Entry(p).State = System.Data.Entity.EntityState.Modified);
does something useful, or if it does anything. The p in lambda is an ICollection of enity types and not a separate DB context entity. Not to mention that you want to set Modified state for ALL entities of type imcharacteristicsitem. Maybe you can explain the reason for this.
Related
Ok this one is confusing me. I'm trying to return objects with included linked objects, but it fails when I use a dto:
class MyObject1
{
public int Id { get; set; }
[Required]
public MyObject2 Mo2 { get; set }
//Other properties
}
class MyObject2
{
public int Id { get; set; }
//Other properties
}
class MyObject3
{
public int Id { get; set; }
public int MyIntValue { get; set; }
//Other properties
}
class MyDTO
{
public MyObject1 Mo1 { get; set; }
public int MyIntValue { get; set; }
}
When I use the anonymous selector, i get the linked objects loaded:
void ThisWorks()
{
var result = await
(
from mo1 in context.MyObject1s
.Include(item => item.Mo2)
//No nav-path or foriegn key setup, so do it the old fashioned way
join mo3 in context.MyObject3s on mo1.Id equals mo3.Id into mo3s
from mo3 in mo3s.DefaultIfEmpty()
select new
{
mo1,
myIntValue = mo3 == null ? 0 : mo3.MyIntValue
}
)
.ToListAsync();
bool success = result.Mo1.Mo2 != null;
//result.Mo1.Mo2 is not null!
}
But when I try to use the Dto, it fails:
void ThisFails()
{
var result = await
(
from mo1 in context.MyObject1s
.Include(item => item.Mo2)
//No nav-path or foriegn key setup, so do it the old fashioned way
join mo3 in context.MyObject3s on mo1.Id equals mo3.Id into mo3s
from mo3 in mo3s.DefaultIfEmpty()
select new MyDto
{
Mo1 = mo1,
MyIntValue = mo3 == null ? 0 : mo3.MyIntValue
}
)
.ToListAsync();
bool success = result.Mo1.Mo2 != null;
//result.Mo1.Mo2 is null - Why? And how do I solve it?
}
Why does the linked object not load when I create a Dto? I'm using the query in a few different places, so I want to define it once, but I need a dto to hold the MyIntValue. Is there a way to do this?
Include has no effect on projections.
You must create a separate property to hold the child entity:
class MyDTO
{
public MyObject1 Mo1 { get; set; }
public MyObject2 Mo2 { get; set; }
public int MyIntValue { get; set; }
}
and project like this:
select new MyDto
{
Mo1 = mo1,
Mo2 = mo1.Mo2,
MyIntValue = mo3 == null ? 0 : mo3.MyIntValue
}
I would suggest to not hold entities inside your DTOs. Would be better to flatten the values, or create "child DTOs" (like Dto1 and Dto2 for each entity).
I have more than 100 tables in data base in which 60+ table's contain column called ShortCode nvarchar(12) which represent globally unique code of that record.
Now is there any way to find that the ShortCode value eg. AST_SHIP_FIRE present in any of the table in database.
Note:ShortCode is user define.
currently I am try below code,it works but I have to code for all table.
if (entities.Table1.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower())
{return false;}
else if(entities.Table2.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower()))
{return false;}
else if( entities.Talble3.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower()))
{return false;}
.
.
.
else
{
//insert code
}
I think there may be more efficient way.
Ok, maybe not very straightforward but lets do it!
First of all define an interface for ShortCode property and implement it by any entity that has it:
public interface ITableWithShortCode
{
public string ShortCode { get; set; }
}
public class Table1 : ITableWithShortCode
{
public long Id { get; set; }
public string ShortCode { get; set; }
}
public class Table2 : ITableWithShortCode
{
public long Id { get; set; }
public string ShortCode { get; set; }
}
Now using power of Reflection you can write a method like this:
public bool IsExistShortCode(string shortCode)
{
using (var context = new AppDbContext())
{
/*
find all tables that are defined in your DbContext and are implemented ITableWithShortCode like:
public DbSet<Table1> Table1 { get; set; }
public DbSet<Table2> Table2 { get; set; }
...
*/
var properties = typeof(AppDbContext).GetProperties()
.Where(p => p.PropertyType.IsGenericType
&& typeof(ITableWithShortCode).IsAssignableFrom(p.PropertyType.GenericTypeArguments[0]));
foreach (var property in properties)
{
var contextProp = (IQueryable<ITableWithShortCode>)property.GetValue(context);
bool isExist = contextProp.Any(p => p.ShortCode == shortCode);
if (isExist)
return true;
}
return false;
}
}
Note: You can do some optimization on this code, I prefered to keep it in its simplest state to show the idea. But in production, for example you can easily cache DbContext properties on startup and use it afterward
Multiple answers have led me to the following 2 solutions, but both of them do not seem to be working correctly.
What I have are 2 objects
public class DatabaseAssignment : AuditableEntity
{
public Guid Id { get; set; }
public string User_Id { get; set; }
public Guid Database_Id { get; set; }
}
public class Database : AuditableEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Server { get; set; }
public bool IsActive { get; set; }
public Guid DatabaseClientId { get; set; }
}
Now, the front-end will return all selected Database objects (as IEnumerable) for a given user. I am grabbing all current DatabaseAssignments from the database for the given user and comparing them to the databases by the Database.ID property. My goal is to find the DatabaseAssignments that I can remove from the database. However, my solutions keep returning all DatabaseAssignments to be removed.
if (databases != null)
{
var unitOfWork = new UnitOfWork(_context);
var userDatabaseAssignments = unitOfWork.DatabaseAssignments.GetAll().Where(d => d.User_Id == user.Id);
//var assignmentsToRemove = userDatabaseAssignments.Where(ud => databases.Any(d => d.Id != ud.Database_Id));
var assignmentsToRemove = userDatabaseAssignments.Select(ud => userDatabaseAssignments.FirstOrDefault()).Where(d1 => databases.All(d2 => d2.Id != d1.Database_Id));
var assignmentsToAdd = databases.Select(d => new DatabaseAssignment { User_Id = user.Id, Database_Id = d.Id }).Where(ar => assignmentsToRemove.All(a => a.Database_Id != ar.Database_Id));
if (assignmentsToRemove.Any())
{
unitOfWork.DatabaseAssignments.RemoveRange(assignmentsToRemove);
}
if (assignmentsToAdd.Any())
{
unitOfWork.DatabaseAssignments.AddRange(assignmentsToAdd);
}
unitOfWork.SaveChanges();
}
I think u are looking for an Except extension, have a look at this link
LINQ: Select where object does not contain items from list
Or other way is with contains see below Fiddler link :
https://dotnetfiddle.net/lKyI2F
I am building a system of filters to manipulate a simple collection that looks like:
public class ProcessedData
{
public byte ProcessId { get; set; }
public List<EventsData> EventsData { get; set; } = new List<EventsData>();
}
And EventsData object has its id property inside something like:
public class EventData
{
public byte EventDataId { get; set; }
}
Now the idea is simple, I have a collection of selected ProcessId and EventId in form of the two list like:
public List<byte> SelectedProcessFilter { get; set; }
public List<byte> SelectedEventFilter { get; set; }
So I have prepared aggregate where conditions function what looks like:
public IEnumerable<ProcessedData> DataFilter(IEnumerable<ProcessedData> preprocessedData, Expression<Func<ProcessedData, bool>>[] filters)
{
if (filters != null)
{
preprocessedEvents = filters.Aggregate(preprocessedData, (current, filter) => current.Where(filter.Compile()));
}
return preprocessedEvents.ToList();
}
And now i can declare where conditions something like:
private Expression<Func<ProcessedData, bool>>[] BuildPredicate()
{
var predicates = new List<Expression<Func<ProcessedData, bool>>>();
if (SelectedProcessFilter != null && SelectedProcessFilter .Any())
{
predicates.Add(data => SelectedProcessFilter.Contains(data.ProcessId));
}
if (SelectedEventFilter != null && SelectedEventFilter.Any())
{
predicates.Add(data => SelectedEventFilter.Any(value => data.EventsData.Any(target => target.EventDataId == (byte)value.Value)));
}
return predicates.ToArray();
}
Usage is simple just:
IEnumerable<ProcessedData> filteredPreprocessedData = service.DataFilter(preprocessedEvents, BuildPredicate());
And basically works, but there is one thins when i filter on EventsDataId, it checks if such value is in that property only, Is it possible to fit somehow possibility to rebuild such property so it will contain only selected EventId ?
For now as what i did, I fallowed NetMage advise and just made a copy of object with new properties values.
The following are the entity classes to make more understanding of relationships:
public class EmployeeCv : UserEntity
{
public byte ProfileImage { get; set; }
public virtual List<Header> Headers { get; set; }
public virtual List<ProjectExperience> ProjectExperiences { get; set; }
public virtual List<Tag> Tags { get; set; } //many to many relationship between employeeCv and tags
[NotMapped]
public List<TagsByTypes> TagsbyTypes
{
get
{
List<TagsByTypes> ListOfTagTypes = new List<TagsByTypes>();
if (Tags != null)
{
var GroupedList = Tags.GroupBy(x => x.TagType.Title).ToList().Select(grp => grp.ToList());
foreach (var currentItem in GroupedList)
{
var TagType = new TagsByTypes()
{
Title = currentItem.FirstOrDefault().TagType.Title,
Tags = currentItem
};
ListOfTagTypes.Add(TagType);
}
return ListOfTagTypes;
}
else
return null;
}
}
}
public class Tag : AuditableEntity<int>
{
public string Title { get; set; }
public virtual List<EmployeeCv> EmployeeCv { get; set; }
public virtual TagType TagType { get; set; }
//To post Id's Not added to the database
[NotMapped]
public int TagTypeId { get; set; }
[NotMapped]
public int EmployeeCv_Id { get; set; }
}
public class TagType : AuditableEntity<int>
{
public string Title { get; set; }
public virtual List<Tag> Tags { get; set; }
}
I am writing a function to add new tag to the employeeCv based on the existing tag type. I have got Unit of work and Repositories setup to add/update/delete records in DB. Here is my implementation:
public void UpdateEmployeeCVWithTag(Tag tag)
{
using (var repository = new UnitOfWork<EmployeeCv>().Repository)
{
var EmployeeCv = repository.GetSingleIncluding(tag.EmployeeCv_Id,
x => x.Headers, x => x.Tags,
x => x.ProjectExperiences,
x => x.ProjectExperiences.Select(p => p.AssociatedProject),
x => x.ProjectExperiences.Select(p => p.ProjectSkills));
//x => x.ProjectExperiences.Select(p => p.ProjectSkillTags.Select(s => s.AssociatedSkill)));
//tag.TagType = EmployeeCv;
var repositoryTagType = new UnitOfWork<TagType>().Repository;
var tagtype = repositoryTagType.GetItemById(tag.TagTypeId);
tag.TagType = tagtype; //even after assignment new tagtype is creating everytime code runs
//repositoryTag.UpdateItem(tagtype);
EmployeeCv.Tags.Add(tag);
//EmployeeCv.ProjectExperiences[projectId - 1].ProjectSkills.Add(tag);
repository.UpdateItem(EmployeeCv);
}
}
This function works correctly except one issue. It is creating a new TagType in the database and ignoring the one that already exist. Below is my updateItem code in the repository classs:
public virtual void UpdateItem(TEntity entityToUpdate)
{
var auditableEntity = entityToUpdate as IAuditableEntity;
if (auditableEntity != null)
{
auditableEntity.UpdatedDate = DateTime.Now;
}
//_context
//Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
_context.SaveChanges();
}
My guess without seeing the full functionality, is that you are using different context for this.
You should update the foreign key not the entire object so there is no need to add the entire TagType object since the tagTypeId is already set. The foreign key should work as is.
Please look into this link for further information.