EF 4.4 Preventing AutoGen Navigation Properties and/or Relationships - entity-framework

I started a db schema using EF and ran into multiple issues when tring to mannually modify the CLR's and/or db tables. First was a "Employee_ID" column that EF placed in a table. I deleted it, the dbo.EdmMetaData and the dbo.__MigrationHistory tables and fumbled through the run-time errors that insued. Now, I'm grapling with the following error:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'EmployeeID'.
My implementation uses a TimeCardEntity CLR that has 3 computed columns. These columns just so happens to map to another table's Primary Key. This other table is EmployeeRecord.
GOAL) I don't WANT EF to auto map thse 3 columns. I intend to fill them myself due to the complications EF offers, but I can't tell EF to stop creating navigation relationships and/or referential constraints.
Point #1) I have a EmployeeRecord table that has a Guid ID primary key, it maps to CLR class EmployeeRecord
Point #2) I have a TimeCardEntity table that has has 3 computed columns called EmployeeID, ManagerID, DivisionManagerID that relate back to EmployeeRecord. All are NULL declared but EmployeeID is required, obviously, because you can't have a time card without declaring the employee. The ManagerID and DivisionManagerID get filled later.
Point #3) Please don't ask me "Why are these computed?", because there is a reason. I alos feel it is illrelevant to the issue. In short, computed EmployeeID's (whether employee, manager or division mananger), are stored in an xml property with the data of approval and signature of employee - which provides non reputiation.
Point #4) I have 3 stored functions called fxGetEmployeeID(xml), fxGetManagerID(xml), and getDivisonManagerID(xml). Each of these are used in the computed columns EmployeeID, ManagerID and DivisionManagerID respectively.
Here is the class declarations simplified for brevity:
public enum TimeCardEmployeeTypeEnum {
Employee,
Manager,
DivisionManager
}
[DataContract]
[Serializable]
[Table("EmployeeRecord", Schema = "TimeCard")]
public class EmployeeRecord {
#region Exposed Propert(y|ies)
[DataMember]
public Guid ID { get; set; }
/// <summary>
/// Customers internal company employee ID. Can be null, SSN, last 4, or what ever...
/// I included it just in case it was part of my pains...
/// </summary>
[CustomValidation(typeof(ModelValidator), "EmployeeRecord_EmployeeID", ErrorMessage = "Employee ID is not valid.")]
public string EmployeeID { get; set; }
#endregion
}
[DataContract]
[Serializable]
[Table("TimeCardEntry", Schema = "TimeCard")]
public class TimeCardEntry {
#region Member Field(s)
[NonSerialized]
XDocument m_TimeEntries;
#endregion
#region Con/Destructor(s)
public TimeCardEntry() {
this.m_TimeEntries = "<root />".ToXDocument();
}
public TimeCardEntry(Guid employeeID) {
if (employeeID == Guid.Empty)
throw new ArgumentNullException("employeeID");
this.m_TimeEntries = "<root />".ToXDocument();
this.EmployeeID = employeeID;
}
#endregion
#region Exposed Propert(y|ies)
[NotMapped]
[IgnoreDataMember]
public XDocument TimeEntries {
get {
if (this.m_TimeEntries == null) {
if (!string.IsNullOrEmpty(this.TimeEntriesXml))
this.m_TimeEntries = this.TimeEntriesXml.ToXDocument();
}
return this.m_TimeEntries;
}
set {
this.m_TimeEntries = value;
if (this.m_TimeEntries != null)
this.TimeEntriesXml = this.m_TimeEntries.ToString();
else
this.TimeEntriesXml = null;
this.OnPropertyChanged("TimeEntriesXml");
this.OnPropertyChanged("TimeEntries");
}
}
[DataMember]
[EditorBrowsable(EditorBrowsableState.Never)]
[Required]
public string TimeEntriesXml {
get {
if (this.m_TimeEntries == null)
return null;
return this.m_TimeEntries.ToString();
}
set {
this.m_TimeEntries = value.ToXDocument();
this.OnPropertyChanged("TimeEntriesXml");
this.OnPropertyChanged("TimeEntries");
}
}
[IgnoreDataMember]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public Guid? EmployeeID {
get {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeID");
if (attribute != null)
return (Guid)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.Manager))
throw new ArgumentException("Property cannot be changed once the manager signature has been set.", "EmployeeID");
if (value != null && value.Value != Guid.Empty)
this.m_TimeEntries.Root.SetAttributeValue("EmployeeID", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeID");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("EmployeeID");
}
}
public virtual EmployeeRecord Employee { get; set; }
[NotMapped]
[IgnoreDataMember]
public DateTime? EmployeeApprovalDate {
get {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeApprovalDate");
if (attribute != null)
return (DateTime)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.Manager))
throw new ArgumentException("Property cannot be changed once the manager signature has been set.", "EmployeeApprovalDate");
if (value.HasValue)
this.m_TimeEntries.Root.SetAttributeValue("EmployeeApprovalDate", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("EmployeeApprovalDate");
}
}
[NotMapped]
[IgnoreDataMember]
public byte[] EmployeeSignature {
get {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeSignature");
if (attribute != null)
return Convert.FromBase64String((string)attribute);
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.Manager))
throw new ArgumentException("Property cannot be changed once the manager signature has been set.", "EmployeeSignature");
if (value != null) {
if (value.Length > 1024)
throw new ArgumentException("Signature cannot be larger than 1KB.", "EmployeeSignature");
this.m_TimeEntries.Root.SetAttributeValue("EmployeeSignature", Convert.ToBase64String(value));
} else {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("EmployeeSignature");
}
}
[IgnoreDataMember]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public Guid? ManagerID {
get {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerID");
if (attribute != null)
return (Guid)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.DivisionManager))
throw new ArgumentException("Property cannot be changed once the division manager signature has been set.", "ManagerID");
if (value.HasValue) {
if (value.Value == Guid.Empty)
throw new ArgumentNullException("ManagerID");
this.m_TimeEntries.Root.SetAttributeValue("ManagerID", value);
} else {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerID");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("ManagerID");
}
}
public virtual EmployeeRecord Manager { get; set; }
[NotMapped]
[IgnoreDataMember]
public DateTime? ManagerApprovalDate {
get {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerApprovalDate");
if (attribute != null)
return (DateTime)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.DivisionManager))
throw new ArgumentException("Property cannot be changed once the division manager signature has been set.", "ManagerApprovalDate");
if (value.HasValue)
this.m_TimeEntries.Root.SetAttributeValue("ManagerApprovalDate", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("ManagerApprovalDate");
}
}
[NotMapped]
[IgnoreDataMember]
public byte[] ManagerSignature {
get {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerSignature");
if (attribute != null)
return Convert.FromBase64String((string)attribute);
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.DivisionManager))
throw new ArgumentException("Property cannot be changed once the division manager signature has been set.", "ManagerSignature");
if (value != null) {
if (value.Length > 1024)
throw new ArgumentException("Signature cannot be larger than 1KB.", "ManagerSignature");
this.m_TimeEntries.Root.SetAttributeValue("ManagerSignature", Convert.ToBase64String(value));
} else {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerSignature");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("ManagerSignature");
}
}
[IgnoreDataMember]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public Guid? DivisionManagerID {
get {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerID");
if (attribute != null)
return (Guid)attribute;
return null;
}
set {
if (value.HasValue) {
if (value.Value == Guid.Empty)
throw new ArgumentNullException("DivisionManagerID");
this.m_TimeEntries.Root.SetAttributeValue("DivisionManagerID", value);
} else {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerID");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("DivisionManagerID");
}
}
public virtual EmployeeRecord DivisionManager { get; set; }
[NotMapped]
[IgnoreDataMember]
public DateTime? DivisionManagerApprovalDate {
get {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerApprovalDate");
if (attribute != null)
return (DateTime)attribute;
return null;
}
set {
if (value.HasValue)
this.m_TimeEntries.Root.SetAttributeValue("DivisionManagerApprovalDate", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("DivisionManagerApprovalDate");
}
}
[NotMapped]
[IgnoreDataMember]
public byte[] DivisionManagerSignature {
get {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerSignature");
if (attribute != null)
return Convert.FromBase64String((string)attribute);
return null;
}
set {
if (value != null) {
if (value.Length > 1024)
throw new ArgumentException("Signature cannot be larger than 1KB.", "DivisionManagerSignature");
this.m_TimeEntries.Root.SetAttributeValue("DivisionManagerSignature", Convert.ToBase64String(value));
} else {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerSignature");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("DivisionManagerSignature");
}
}
#endregion
}
This is the db context declaration
public sealed class DatabaseContext : DbContext {
public DatabaseContext(bool autoDetectChangesEnabled = false, bool lazyLoadingEnabled = false, bool proxyCreationEnabled = false, bool validateOnSaveEnabled = false) {
this.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
this.Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
this.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
this.Configuration.ValidateOnSaveEnabled = validateOnSaveEnabled;
}
public DbSet<EmployeeRecord> EmployeeRecords { get; set; }
public DbSet<TimeCardEntry> TimeCards { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<System.Data.Entity.Infrastructure.IncludeMetadataConvention>();
}
}
UPDATE
I have to add another observed behavior of EF. When I add the "NotMappedAttribute" to the EmployeeID column of TimeCardEntry, I get another issue. The EF addes a "Employee_ID" column back to the auto-gen schema. See the TSQL Profile Trace below:
exec sp_executesql N'SELECT
[Limit1].[C1] AS [C1],
[Limit1].[ID] AS [ID],
[Limit1].[TimeEntriesXml] AS [TimeEntriesXml],
[Limit1].[ManagerID] AS [ManagerID],
[Limit1].[DivisionManagerID] AS [DivisionManagerID],
[Limit1].[CreatedBy] AS [CreatedBy],
[Limit1].[Created] AS [Created],
[Limit1].[UpdatedBy] AS [UpdatedBy],
[Limit1].[Updated] AS [Updated],
[Limit1].[Employee_ID] AS [Employee_ID]
FROM ( SELECT TOP (2)
[Extent1].[ID] AS [ID],
[Extent1].[TimeEntriesXml] AS [TimeEntriesXml],
[Extent1].[ManagerID] AS [ManagerID],
[Extent1].[DivisionManagerID] AS [DivisionManagerID],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[Created] AS [Created],
[Extent1].[UpdatedBy] AS [UpdatedBy],
[Extent1].[Updated] AS [Updated],
[Extent1].[Employee_ID] AS [Employee_ID],
1 AS [C1]
FROM [TimeCard].[TimeCardEntry] AS [Extent1]
WHERE [Extent1].[ID] = #p0
) AS [Limit1]',N'#p0 uniqueidentifier',#p0='10F3E723-4E12-48CD-8750-5922A1E42AA3'

EF is trying to declare Employee_ID in the database because it needs column for foreign key to Employee table. It cannot use your EmployeeID property and its columns as foreign key because it is declared as computed - foreign keys in EF must not be declared as computed or identity (it is not supported).
Solution for your model either requires abandoning navigation properties and work with IDs only (and loading related employees manually) or abandoning those computed columns - I can imagine that both options may be quite annoying.

Related

The operation failed: The relationship could not be changed because one or more of the foreign-key - EF Code first

I am using Entity Framework Code First - I am getting the following message in deleting any of the tables data - like CaseNov, ViolationsNov and ViolationTypeNov are three tables which are to handle many to many relationships between Case-Nov, Violation-Nov and ViolationType-Nov, I am getting error messages even if I trying to delete the detailed tables like: CaseNov, ViolationsNov, ViolationTypeNov or row directly from NOV table, I am getting the similar type of message - any help please? I am using Entity Framework Code First for deleting it
The operation failed: The relationship could not be changed because one or more of the foreign-key
properties is non-nullable. When a change is made to a relationship, the related foreign-key
property is set to a null value. If the foreign-key does not support null values, a new relationship
must be defined, the foreign-key property must be assigned another non-null value or the unrelated
object must be deleted.
And here is my Code for delete operation:
public bool Delete(NOV nov, bool performCommit = true)
{
System.Data.Entity.DbContextTransaction dbOperation = null;
if (performCommit)
dbOperation = UnitOfWork.BeginTransaction();
try
{
//deleting all OneToMany references to NOV
List<ViolationTypeNOV> novRels = UnitOfWork.ViolationTypeNOVRepository
.GetAll().Where(x => x.NOVId == nov.NOVId).ToList();
foreach (ViolationTypeNOV o in novRels)
{
nov.ViolationTypeNOVs.Remove(o);
this.UnitOfWork.ViolationTypeNOVRepository.Delete(o.ViolationTypeNOVId);
}
novRels.RemoveAll(x => x.NOVId == nov.NOVId);
List<ViolationNOV> violationNOVs = UnitOfWork.ViolationNOVRepository
.GetAll().Where(x => x.NOVId == nov.NOVId).ToList();
foreach (ViolationNOV o in violationNOVs)
{
nov.ViolationNOVs.Remove(o);
this.UnitOfWork.ViolationNOVRepository.Delete(o.ViolationNOVId);
}
violationNOVs.RemoveAll(x => x.NOVId == nov.NOVId);
List<CaseNOV> caseNOVs = UnitOfWork.CaseNOVRepository
.GetAll().Where(x => x.NOVId == nov.NOVId).ToList();
foreach (CaseNOV o in caseNOVs)
{
nov.CaseNOVs.Remove(o);
this.UnitOfWork.CaseNOVRepository.Delete(o.CaseNOVId);
}
caseNOVs.RemoveAll(x => x.NOVId == nov.NOVId);
UnitOfWork.NOVRepository.Delete(nov.NOVId);
if (dbOperation != null)
dbOperation.Commit();
LogHandler.LogInfo(2521, "Deleted NOV " + nov.NOVNumber);
return true;
}
catch (Exception ex)
{
LogHandler.LogError(2523, "Commit Fail in NOV Delete", ex);
if (dbOperation != null)
dbOperation.Rollback();
throw ex;
}
}
Unit of Work Code:
public DataAccessUnitOfWork UnitOfWork { get; set; }
public RoleManager<ApplicationRole> RoleManager { get; set; }
public UserManager<ApplicationUser> UserManager { get; set; }
public ApplicationUser CurrentUser { get; set; }
public DomainLogicManager(DataAccessUnitOfWork unitOfWork)
{
this.UnitOfWork = unitOfWork;
this.RoleManager = new RoleManager<ApplicationRole>(new RoleStore<ApplicationRole>(UnitOfWork.Context));
this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(UnitOfWork.Context));
}
Delete method in one of the repository classes is as follows:
public bool Delete(IdT id)
{
T item = this.DbSet.Find(id);
var entry = this.Context.Entry(item);
entry.State = EntityState.Deleted;
this.DbSet.Attach(item);
this.DbSet.Remove(item);
this.Context.SaveChanges();
BuildMetaData(item, false, false);
return true;
}

How to insert an entity and keep the same Object Guid with EF

I need keep the same original Id (GUID) after save data because is a replication job. (SQL -> SQL remote). Then, the model can not be changed. After SaveChanges() EF insert a new random Guid as Id, then this changes my original object, and do not want that. A compact sample:
class EFInsertTest
{
public void InsertTest()
{
var id = new Guid("D75C887D-BF25-E611-943B-080027BA87E8"); // dummy
var entity = new Something { Id = id, Name = "ELENOR" };
using (var db = new SomethingContext())
{
db.Things.Add(entity);
db.SaveChanges();
// TEST
if (db.Things.Find(id) != null)
{
Console.WriteLine($"Great! Expected behavior");
}
else
{// run this:
Console.WriteLine($"Failed! Id has another value");
}
Console.ReadKey();
// SQL hard code (works fine)
db.Database.ExecuteSqlCommand($"INSERT INTO [Something] VALUES('{id}', '{entity.Name}')");
db.SaveChanges();
// TEST
if (db.Things.Find(id) != null)
{
Console.WriteLine($"Great! Expected behavior");
}
else
{
Console.WriteLine($"Failed! Id has another value");
}
Console.ReadKey();
}
}
}
public class SomethingContext : DbContext
{
public virtual DbSet<Something> Things { get; set; }
}
public class Something
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
}

entity-framework get entityreference value

I have an entity object which is connected to another entities.
I want to loop through all entity properties , if the property is String then do something with the value.
If the property is EntityReference, I want to get it's value (it has only one), and do something with the value as well.
I was able to determine if the property is string or EntityReference.
I get the String value by -
value = typeof(entity).GetProperty(property.Name).GetValue(request, null);
but how do I get the value of an entityreference ?
Just trace the property tree.
You have the first step. repeat for lower properties.
var TopLevelProp = poco.GetType().GetProperty(property.Name).GetValue(poco, null);
var LowerProp = TopLevelProp.GetType().GetProperty("aLowerPropName").GetValue(TopLevelProp, null);
although you tagged this EF. What did you mean by entity reference ?
edit: in the hope i have covered the entity and its key question
Here is a sample Repository covering EF Context and entity access. See the Entity field and Entity KEY field methods...
public class Repository<T> : IRepositoryEF<T> where T : BaseObject {
public RepositoryEF(DbContext context) { Context = context; }
public DbEntityEntry<T> Entry(T entity) { return Context.Entry(entity); }
public DbSet<T> EntityDbSet() { return Context.Set<T>(); }
public ObjectContext ObjectContext { get { return ((IObjectContextAdapter) this.Context).ObjectContext; } }
public DbContext Context { get; protected set; }
public EntityState GetEntityState(object entity) { return Context.Entry(entity).State; }
public ObjectSet<T> GetObjectSet() { return ObjectContext.CreateObjectSet<T>(); }
public IList<string> GetEntityFields() {
var entityFields = GetObjectSet().EntitySet.ElementType.Properties;
return entityFields.Select(e => e.Name).ToList();
}
public string[] GetEntityKeyFields() { return GetObjectSet().EntitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray(); }
public EntityKey GetEntityKey(T entity) {
if (entity == null) {
return null;
}
return ObjectContext.CreateEntityKey(GetObjectSet().EntitySet.Name, entity);
}
public string GetEntityKeyAsString(T entity) {
if (entity == null) {
return string.Empty;
}
var eK = GetEntityKey(entity);
var keyAsString = eK.EntityKeyValues.Aggregate("", (current, keyField) => current + keyField.Key + "=" + keyField.Value + ",");
return keyAsString;
}
}
If you want to get all the metadata in the Context:
ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
IEnumerable<EntityType> managedTypes = workspace.GetItems<EntityType>(DataSpace.OSpace);
You can go to town on the meta data. see all enums values in DataSpace to get at various parts of the model

Handling Related Data when using Entity Framework Code First

I have two Classes: LicenseType and EntityType.
[Table("LicenseType")]
public class LicenseType : ComplianceBase, INotifyPropertyChanged
{
private List<Certification> _certifications = new List<Certification>();
private List<EntityType> _entityTypes = new List<EntityType>();
public List<EntityType> EntityTypes
{
get { return _entityTypes; }
set { _entityTypes = value; }
}
public List<Certification> Certifications
{
get { return _certifications; }
set { _certifications = value; }
}
}
and
[Table("EntityType")]
public class EntityType : ComplianceBase, INotifyPropertyChanged
{
private List<LicenseType> _licenseTypes = new List<LicenseType>();
public List<LicenseType> LicenseTypes
{
get { return _licenseTypes; }
set
{
_licenseTypes = value;
// OnPropertyChanged();
}
}
}
The both derive from ComplianceBase,
public class ComplianceBase
{
private int _id;
private string _name;
private string _description;
public string Description
{
get { return _description; }
set
{
if (_description == value) return;
_description = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public int Id
{
get { return _id; }
set
{
if (value == _id) return;
_id = value;
OnPropertyChanged();
}
}
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
What I want is to be able to do is associate an EntityType with one or more LicenseTypes, so for instance, an EntityType "Primary Lender" could be associated with say two LicenseTypes, "Lender License" and "Mortgage License". In this situation, I want one record in the EntityType table, "Primary Lender" and two records in my LicenseType table: "Lender License" and "Mortgage License".
The code for adding related LicenseTypes to my EntityType is done by calling:
_currentEntity.LicenseTypes.Add(licenseType);
and then calling _context.SaveChanges();
There is an additional table, "EntityTypeLicenseTypes" that serves as the lookup table to relate these two tables. There are two records to join the EntityType with the two related LicenseTypes.
And this works. However, my code also adds (it duplicates) the LicenseType record and adds it in the LicenseType table for those records that are being associated.
How can I stop this from happening?
In order to avoid the duplication you must attach the licenseType to the context:
_context.LicenseTypes.Attach(licenseType);
_currentEntity.LicenseTypes.Add(licenseType);
_context.SaveChanges();

Entity Framework Generic Repository

I am writing a generic repository to be used for my every model CRUD operation using entity framework CTP5 as following:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : BaseEntity
{
public DbContext Context { get; set; }
public void Insert(TEntity entity)
{
if (Context.Entry<TEntity>(entity).State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Set<TEntity>().Add(entity);
Context.SaveChanges();
}
public void Delete(int id)
{
TEntity entity = Context.Set<TEntity>().Find(id);
if (Context.Entry<TEntity>(entity).State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Set<TEntity>().Remove(entity);
Context.SaveChanges();
}
public void Delete(TEntity entity)
{
Context.Set<TEntity>().Remove(entity);
Context.SaveChanges();
}
public void Update(TEntity entity)
{
TEntity status = Context.Set<TEntity>().Find(entity.Id);
status = entity;
Context.SaveChanges();
}
public TEntity GetFirst()
{
var entity = Context.Set<TEntity>().FirstOrDefault();
if (entity == null) return null;
return entity;
}
public TEntity GetNext(int id)
{
var entity = (from u in Context.Set<TEntity>()
where u.Id > id
select u).FirstOrDefault();
if (entity == null) return null;
return entity;
}
public TEntity GetPrevoius(int id)
{
var entity = (from u in Context.Set<TEntity>()
where u.Id < id
orderby u.Id descending
select u).FirstOrDefault();
if (entity == null) return GetFirst();
return entity;
}
public TEntity GetLast()
{
var entity = (Context.Set<TEntity>().OrderByDescending(u => u.Id)).FirstOrDefault();
if (entity == null) return null;
return entity;
}
public TEntity GetById(int id)
{
return Context.Set<TEntity>().Find(id);
}
public int GetMaxId()
{
var max = Context.Set<TEntity>().Count()+ 1;
return max;
}
}
everything works fine but Update method which nither doesnt generate any error nor save any changes back to database.
Can anybody guid me how to solve this issue?
You can use CurrentValues.SetValues:
public void Update(TEntity entity)
{
TEntity status = Context.Set<TEntity>().Find(entity.Id);
Context.Entry(status).CurrentValues.SetValues(entity);
Context.SaveChanges();
}
It updates scalar and complex properties but not navigation properties.
You're overwriting the variable status with a totally new object, taking the one from the database out of scope, but not actually modifying the object that is attached to the context, which is what you'll want to do.
The only way I can think off the top of my head is to use reflection to read all the properties of the type, and assign the values to the original object based on the new one, something like:
foreach (var prop in typeof(TEntity).GetProperties())
{
prop.SetValue(status, prop.GetValue(entity, null), null);
}