Update a foreign key in Entity Framework - entity-framework

I have created a partial class of an entity for getting a foreign key property on it.
public partial class Artikel
{
public int WarengruppenID
{
get
{
if (WarengruppeReference.EntityKey == null) return 0;
return (int)WarengruppeReference.EntityKey.EntityKeyValues[0].Value;
}
set
{
WarengruppeReference.EntityKey =
new EntityKey("ConsumerProtectionEntities.Warengruppe", "WarengruppenID", value);
}
}
}
Now I change the foreign key (property) but nothing happens?!
What do I have to do to update a foreign key for an entity?
I use EF 3.5 SP1 in ASP.NET MVC 2
EDIT
id = ArtikelID
updatedArtikel = Artikel properties
ArtikelWarengruppen = selected DropDownList value
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, Artikel updatedArtikel, string ArtikelWarengruppen)
{
try
{
int artikelWarengruppenID = int.Parse(ArtikelWarengruppen);
var originalArtikel = (from art in _db.Artikel.Include(typeof(Warengruppe).Name)
where art.ArtikelID == id
select art).FirstOrDefault();
_db.Attach(originalArtikel);
updatedArtikel.ArtikelID = id;
// Update FK
updatedArtikel.WarengruppenID = artikelWarengruppenID;
_db.ApplyPropertyChanges(typeof(Artikel).Name, updatedArtikel);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}

I've solved the problem.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, Artikel updatedArtikel, string ArtikelWarengruppen)
{
try
{
// update foreign key
int artikelWarengruppenID = int.Parse(ArtikelWarengruppen);
var originalArtikel = (from art in _db.Artikel.Include(typeof(Warengruppe).Name)
where art.ArtikelID == id
select art).FirstOrDefault();
originalArtikel.WarengruppenID = artikelWarengruppenID;
_db.Attach(originalArtikel);
// update properties
updatedArtikel.ArtikelID = id;
_db.ApplyPropertyChanges(typeof(Artikel).Name, updatedArtikel);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
But is there a better way?

This should be the optimal way to achieve what you want:
int artikelWarengruppenID = int.Parse(ArtikelWarengruppen);
updatedArtikel.WarengruppenID = artikelWarengruppenID;
updatedArtikel.ArtikelID = id;
_db.Entry(updatedArtikel).State = EntityState.Modified;
_db.SaveChanges();
This does assume that your posted Artikel contains all the data that you want to keep in the entity.

Related

EF Core 2 - Only Request Two Columns

I'm trying to write a LINQ statement for EF Core 2 that only requests SQL return two columns: UserID and FirstNamePreferred.
Everything I've tried creates a SQL statement that requests all columns.
Here is my code:
var model = db.FamilyMember
.Where(fm => fm.UserID == message.CurrentUserID)
.Select(fm => new Model
{
UserID = message.CurrentUserID,
FirstNamePreferred = fm.FirstNamePreferred
})
.AsNoTracking()
.FirstOrDefault();
My view model:
public class Model
{
public string UserID { get; set; }
public string FirstNamePreferred { get; set; }
}
This generates the following SQL via Profiler:
exec sp_executesql N'SELECT TOP(1) [fm].[ID], [fm].[DOB], [fm].[DOD], [fm].[EyeColourID], [fm].[FamilyID], [fm].[FirstName], [fm].[FirstNameKnownAs], [fm].[Gender], [fm].[HairColour], [fm].[IdentifiesAs], [fm].[LastName], [fm].[MiddleNames], [fm].[NickName], [fm].[PictureExt], [fm].[PictureUID], [fm].[PrimaryEmailAddress], [fm].[SkinTone], [fm].[UserID]
FROM [FamilyMember] AS [fm]
WHERE [fm].[UserID] = #__message_CurrentUserID_0',N'#__message_CurrentUserID_0 nvarchar(450)',#__message_CurrentUserID_0=N'b0e1fe4c-f218-4903-8117-465a68cd99fc'
Update: The issue was that FirstNamePreferred was not actually a database field. Sorry I didn't drill down this far in the code, rookie mistake.
public string FirstNamePreferred
{
get
{
if (FirstNameKnownAs == null)
{
return FirstName;
}
else
{
return FirstNameKnownAs;
}
}
}
Update: The issue was that FirstNamePreferred was not actually a database field. Sorry I didn't drill down this far in the code, rookie mistake.
public string FirstNamePreferred
{
get
{
if (FirstNameKnownAs == null)
{
return FirstName;
}
else
{
return FirstNameKnownAs;
}
}
}
This will work:
var model = db.FamilyMember
.Where(fm => fm.UserID == message.CurrentUserID)
.Select(fm => new
{
UserID = message.CurrentUserID,
FirstNamePreferred = fm.FirstNamePreferred
})
.AsNoTracking()
.FirstOrDefault();
Using a DTO that is not part of your DbContext will cause SELECT *

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();

EF 4.4 Preventing AutoGen Navigation Properties and/or Relationships

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.

Entity Framework, Update The UPDATE statement conflicted with the FOREIGN KEY constraint "FK

I wrote a Silverlight MVVM (SimpleMVVM toolkit) Ria Services App with EntityFramework Model generated from exsisting DB.
At first ViewModel of Page which display list parent entities I load parents, after selecting one, I can further go to edition page, where I do menagent of child entities. Before that, I pass parent Entity by pageDataHelper - key value pair Dictionary, which it holds, to ViewModel of Edition Page, and refresh it from database.
Next, I can load by entity primary key, it's childs to ListBox. By this, i can do CRUD's of childs. Anfortunatly, when I want to update one one of them, I got:
The UPDATE statement conflicted with the FOREIGN KEY constraint
"FK_Pozycje_Kosztorysy" The conflict occurred in database
"D:...\APP_DATA\EMS.MDF", table "dbo.Kosztorysy", column 'KosID'.
Aplication makes updates in DB, but entityframework always throws exception, which I handle...
Both tables in db have set On Update/Delete Cascade.
Entities looks:
internal sealed class KosztorysMetadata
{
public int KosID { get; set; }//Primary Key
public EntityCollection<Pozycja> Pozycje { get; set; } //Childs
}
}
internal sealed class PozycjaMetadata
{
public int KosID { get; set; }// Foreign Key
public int PozID { get; set; }//Primary Key
}
}
In App.Web, functins to get entities:
public IQueryable<Kosztorys> GetKosztorysByID(int id)
{
var query = from k in this.ObjectContext.Kosztorysy
where k.KosID==id
select k;
return query;
}
public IQueryable<Pozycja> GetPozycjeGlowne(Int32 id)
{
var query = from k in this.ObjectContext.Pozycje
where k.KosID == id && (k.NadPozID == null || k.TypRMS == 0 || k.TypRMS == TypRMSBVals.Dzial)
select k;
return query;
}
In ViewModel;
class KosztorysViewModel{
public Kosztorys WybranyKosztorys
{
get { return wybranyKosztorys; }
set{ WybranyKosztorys = value;}}
private ObservableCollection<Kosztorys> kosztorysy;
public ObservableCollection<Kosztorys> Kosztorysy{}}
public void OdsiwezKosztorys()//Refresh parent
{ this.serviceAgent.PobKosztorys(WybranyKosztorys.KosID, (encje, ex) => { kosztorysOdswiezony(encje, blad); });
}
void kosztorysOdswiezony(List<Kosztorys> encje, Exception exc) //Refreshed Parent
{ Kosztorysy = new ObservableCollection<Kosztorys>(encje);
WybranyKosztorys = Kosztorysy[0];}
public void PobDzialy()
{
if (WybranyKosztorys != null)
{
this.serviceAgent.PobGlownePozycje(WybranyKosztorys.KosID, (encje, ex) => dzialyPobrane(encje, ex));
}
}
void dzialyPobrane(List<Pozycja> encje, Exception exc) //callback
{
Dzialy.Clear();
Dzialy = new ObservableCollection<Pozycja>(encje);
}}
What's wrong? I use ;EF4.3, Silverlight 5, MS SQL Server 2008R

ef4 record stamping, inserted_at, inserted_by

is there any way of going through all the new/modified entities and setting their, inserted_at, updated_at fields?
With ObjectStateManager I can get a list of those entities but could not find a way of setting the entity property values.
foreach (var item in db.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
{
System.Data.Objects.DataClasses.EntityObject entity = (System.Data.Objects.DataClasses.EntityObject)(item.Entity);
// now how can I set its .inserted_at to DateTime.Now
}
here is my current solution
public interface IUpdateTrack
{
DateTime? updated_at { get; set; }
Guid? updated_by { get; set; }
}
public interface IInsertTrack
{
DateTime? inserted_at { get; set; }
Guid? inserted_by { get; set; }
}
implement the interface in the partial class
public partial class crm_customer : BaseDB.IInsertTrack, BaseDB.IUpdateTrack
in the repository class
public void Save()
{
foreach (var item in db.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
{
System.Data.Objects.DataClasses.EntityObject entity = (System.Data.Objects.DataClasses.EntityObject)(item.Entity);
if (item.Entity is BaseDB.IInsertTrack)
{
IInsertTrack insert_track = (IInsertTrack)(item.Entity);
insert_track.inserted_at = DateTime.Now;
insert_track.inserted_by = BaseDB.SessionContext.Current.ActiveUser.UserUid;
}
}
foreach (var item in db.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
if (item.Entity is BaseDB.IUpdateTrack)
{
IUpdateTrack update_track = (IUpdateTrack)(item.Entity);
update_track.updated_at = DateTime.Now;
update_track.updated_by = BaseDB.SessionContext.Current.ActiveUser.UserUid;
}
}
I would like a solution that does not require implementing the interface for each class in the model, its error prone, you might forget to implement this interfaces for some classes.
I am using EF4 using database-first approach.
Yes, there is a perfect way to accomplish this in Entity Framework 4.0, Thanks to Julia Lerman for pointing out this nice trick.
using System.Data.Common;
using System.Data.Metadata.Edm;
...
var entries = from e in db.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified)
where e.Entity != null
select e;
foreach (var entry in entries) {
var fieldMetaData = entry.CurrentValues.DataRecordInfo.FieldMetadata;
FieldMetadata updatedAtField = fieldMetaData
.Where(f => f.FieldType.Name == "updated_at").FirstOrDefault();
if (updatedAtField.FieldType != null) {
string fieldTypeName = updatedAtField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.DateTime.ToString()) {
entry.CurrentValues.SetDateTime(updatedAtField.Ordinal,
DateTime.Now);
}
}
}
You can then call this code from within the SavingChanges event to be sure that any
updated_at field is automatically updated.
By the way, the System.Data.Metadata.Edm namespace gives you access to
the PrimitiveTypeKind class.