How to refactor Relational() to NetCore3.0 - c#

I don't know how to choose the property after the revision.
private void ApplySnakeCaseNames(ModelBuilder modelBuilder)
{
var mapper = new NpgsqlSnakeCaseNameTranslator();
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
// modify column names
foreach (var property in entity.GetProperties())
{
property.Relational().ColumnName = mapper.TranslateMemberName(property.Relational().ColumnName);
}
// modify table name
entity.Relational().TableName = mapper.TranslateMemberName(entity.Relational().TableName);
// move asp_net tables into schema 'identity'
if (entity.Relational().TableName.StartsWith("asp_net_"))
{
entity.Relational().TableName = entity.Relational().TableName.Replace("asp_net_", string.Empty);
entity.Relational().Schema = "identity";
}
}
}
I expect the output of keep the original.

This is breaking change in Entity Framework Core 3
IProperty.Relational().ColumnName -> IProperty.GetColumnName()
Reference: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#id=%22provider-specific-metadata-api-changes%22

For IMutableEntityType and IMutableProperty, they expose the method instead of properties to change the Name.
Try something like below:
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
// modify column names
foreach (var property in entity.GetProperties())
{
property.SetColumnName(mapper.TranslateMemberName(property.GetColumnName()));
}
// modify table name
entity.SetTableName(mapper.TranslateMemberName(entity.GetTableName()));
// move asp_net tables into schema 'identity'
if (entity.GetTableName().StartsWith("asp_net_"))
{
entity.SetTableName(entity.GetTableName().Replace("asp_net_", string.Empty));
entity.SetSchema("identity");
}
}

Related

Unique Indexes convention in EF6

How to create custom index and key conventions for different type of indexes. I need different naming for following key or index types:
PK_TableName Primary keys
FK_SourceTable_Column_TargetTable for foreign keys
IX_TableName_Column1_Column2 Non-unique indexes
UX_TableName_Column1_Column2 Unique indexes
By defaults, Entity Framework uses following namings:
PK_schemaname.TableName for primary keys
FK_schemaname.SourceTable_schemaname.TargetTable_Column1 for foreign keys
IX_Column1 for non-unique indexes
ColumnName for unique indexes
I've found out that I can implement IStoreModelConvention<T>, but I haven't found particular type to use as type parameter.
Moreover, there're can be Custom Code-First Conventions, but my research is ended with no results. How I can get mentioned naming rules when I use Entity Framework Code First? It can be anything: package, sample, or just direction for following researches.
Mission impossible for PK and FK. The problems is that there is no special EdmModel property/attribute/annotation for naming the store constraint - in the model they are basically represented as list of columns (properties) and the naming convention is hardcoded inside the migration builder classes. Please note that some examples mentioned in the comments are showing how to rename the FK columns (properties), not the FK constraint itself.
Luckily for indexes, although not simple, but it's possible, thanks to the IndexAttribute and IndexAnnotation. This is because the annotation (with attribute) is associated with column (entity property), and then consolidated by an internal class called ConsolidatedIndex.
So in order to achieve the goal, you have to create IStoreModelConvention<EntityType>, prepare a consolidated index info from properties similar to how ConsolidatedIndex class does it, determine the new name based on your rules for the unnamed indexes or indexes with default name generated for FK constrains by the ForeignKeyIndexConvention, and update the corresponding IndexAnnotation of the properties.
With that being said, here is the code for applying your index name convention:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
public class IndexNameConvention : IStoreModelConvention<EntityType>
{
public void Apply(EntityType item, DbModel model)
{
// Build index info, consolidating indexes with the same name
var indexInfo = new List<IndexInfo>();
foreach (var p in item.Properties)
{
foreach (var mp in p.MetadataProperties)
{
var a = mp.Value as IndexAnnotation;
if (a == null) continue;
foreach (var index in a.Indexes)
{
var info = index.Name != null ? indexInfo.FirstOrDefault(e => e.Name == index.Name) : null;
if (info == null)
{
info = new IndexInfo { Name = index.Name };
indexInfo.Add(info);
}
else
{
var other = info.Entries[0].Index;
if (index.IsUnique != other.IsUnique || index.IsClustered != other.IsClustered)
throw new Exception("Invalid index configuration.");
}
info.Entries.Add(new IndexEntry { Column = p, Annotation = mp, Index = index });
}
}
}
if (indexInfo.Count == 0) return;
// Generate new name where needed
var entitySet = model.StoreModel.Container.EntitySets.First(es => es.ElementType == item);
foreach (var info in indexInfo)
{
var columns = info.Entries.OrderBy(e => e.Index.Order).Select(e => e.Column.Name);
if (info.Name == null || info.Name == IndexOperation.BuildDefaultName(columns))
{
bool unique = info.Entries[0].Index.IsUnique;
var name = string.Format("{0}_{1}_{2}", unique ? "UX" : "IX", entitySet.Table, string.Join("_", columns));
if (name.Length > 128) name = name.Substring(0, 128);
if (info.Name == name) continue;
foreach (var entry in info.Entries)
{
var index = new IndexAttribute(name);
if (entry.Index.Order >= 0)
index.Order = entry.Index.Order;
if (entry.Index.IsUniqueConfigured)
index.IsUnique = entry.Index.IsUnique;
if (entry.Index.IsClusteredConfigured)
index.IsClustered = entry.Index.IsClustered;
entry.Index = index;
entry.Modified = true;
}
}
}
// Apply the changes
foreach (var g in indexInfo.SelectMany(e => e.Entries).GroupBy(e => e.Annotation))
{
if (g.Any(e => e.Modified))
g.Key.Value = new IndexAnnotation(g.Select(e => e.Index));
}
}
class IndexInfo
{
public string Name;
public List<IndexEntry> Entries = new List<IndexEntry>();
}
class IndexEntry
{
public EdmProperty Column;
public MetadataProperty Annotation;
public IndexAttribute Index;
public bool Modified;
}
}
All you need is to add it to the DbModelBuilder.Conventions in your OnModelCreating:
modelBuilder.Conventions.Add<IndexNameConvention>();

Code first track database changes [duplicate]

Is there a way to get all the changes made to a object in the Entity Framework before it saves all changes. The reason for this is that i want to create a log table in our clients database:
so...
Is there a way to get the current database values(old) and the new values(current) before changes are saved?
If not, how can i achieve this in a generic way, so all my View Models can inherit from this?(I am using the MVVM + M Structure)
You can use ObjectContext's ObjectStateManager,GetObjectStateEntry to get an object's ObjectStateEntry, which holds its original and current values in the OriginalValues and CurrentValues properties. You can get the names of the properties that changed using the GetModifiedProperties method.
You can write something like:
var myObjectState=myContext.ObjectStateManager.GetObjectStateEntry(myObject);
var modifiedProperties=myObjectState.GetModifiedProperties();
foreach(var propName in modifiedProperties)
{
Console.WriteLine("Property {0} changed from {1} to {2}",
propName,
myObjectState.OriginalValues[propName],
myObjectState.CurrentValues[propName]);
}
For EF5 upwards you can log your changes in the SaveChanges() method like this:
public override int SaveChanges()
{
var changes = from e in this.ChangeTracker.Entries()
where e.State != System.Data.EntityState.Unchanged
select e;
foreach (var change in changes)
{
if (change.State == System.Data.EntityState.Added)
{
// Log Added
}
else if (change.State == System.Data.EntityState.Modified)
{
// Log Modified
var item = change.Cast<IEntity>().Entity;
var originalValues = this.Entry(item).OriginalValues;
var currentValues = this.Entry(item).CurrentValues;
foreach (string propertyName in originalValues.PropertyNames)
{
var original = originalValues[propertyName];
var current = currentValues[propertyName];
if (!Equals(original, current))
{
// log propertyName: original --> current
}
}
}
else if (change.State == System.Data.EntityState.Deleted)
{
// log deleted
}
}
// don't forget to save
base.SaveChanges();
}
I use this extension function that provides details on the entity being changed, the old and new values, the datatype, and the entity key.
This is tested with EF 6.1 using ObjectContext and uses log4net for output.
/// <summary>
/// dump changes in the context to the debug log
/// <para>Debug logging must be turned on using log4net</para>
/// </summary>
/// <param name="context">The context to dump the changes for</param>
public static void DumpChanges(this ObjectContext context)
{
context.DetectChanges();
// Output any added entries
foreach (var added in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
{
Log.DebugFormat("{0}:{1} {2} {3}",
added.State,
added.Entity.GetType().FullName,
added.Entity.ToString(),
string.Join(",",
added.CurrentValues.GetValue(1),
added.CurrentValues.GetValue(2))
);
}
foreach (var modified in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
// Put original field values into dictionary
var originalValues = new Dictionary<string,int>();
for (var i = 0; i < modified.OriginalValues.FieldCount; ++i)
{
originalValues.Add(modified.OriginalValues.GetName(i), i);
}
// Output each of the changed properties.
foreach (var entry in modified.GetModifiedProperties())
{
var originalIdx = originalValues[entry];
Log.DebugFormat("{6} = {0}.{4} [{7}][{2}] [{1}] --> [{3}] Rel:{5}",
modified.Entity.GetType(),
modified.OriginalValues.GetValue(originalIdx),
modified.OriginalValues.GetFieldType(originalIdx),
modified.CurrentValues.GetValue(originalIdx),
modified.OriginalValues.GetName(originalIdx),
modified.IsRelationship,
modified.State,
string.Join(",",
modified.EntityKey.EntityKeyValues
.Select(v => string.Join(" = ", v.Key, v.Value))
)
);
}
}
// Output any deleted entries
foreach (var deleted in context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted))
{
Log.DebugFormat("{1} {0} {2}",
deleted.Entity.GetType().FullName,
deleted.State,
string.Join(",",
deleted.CurrentValues.GetValue(1),
deleted.CurrentValues.GetValue(2))
);
}
}
Use the IsModified field of each property, which is accessible by Context.Entry(Entity).Properties.
In this example, the modified entries are listed as a Tuple of the original and current values, indexed by name. Use any conversion that is required to build the audit log.
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
//...
// gets somewhere in the scope
DbContext Context;
// Some entity that has been modified, but not saved and is being tracked by Context
object Entity;
//...
Dictionary<string, System.Tuple<object, object>> modified =
Context.Entry(Entity)
.Properties.Where(p => p.IsModified)
.ToDictionary(p => p.Metadata.Name,
p => new System.Tuple<object,object>(p.OriginalValue, p.CurrentValue));
//...
Uses Entity Framework Core 3.1. Try it for EF 6.4, but it may not work.

How to get the TableName a particular entity property is mapped to

Entity properties can be split across different tables, meaning that a single Entity can have it's columns mapped to different tables. How does one then, in code, retrieve the info on the particular table an entity property is mapped to.
foreach(PropertyInfo pi in typeof(DbContext).GetProperties())
{
if(pi.PropertyType.IsGenericType && pi.PropertyType.Name.Contains("DbSet"))
{
var t = pi.PropertyType.GetGenericArguments().FirstOrDefault();
var tables = t.GetCustomAttributes(true).OfType<TableAttribute>();
foreach (var entityProperty in t.GetProperties())
{
if (entityProperty.GetCustomAttributes(true).OfType<RequiredAttribute>().Any<RequiredAttribute>())
{
var fieldname = entity.Name;
//I need to match this column with the table it belongs to here
}
}
}
}
So far, I have the code below to get at the entity property, from the object itself, how do I determine the particular table the current property is mapped to, in my database? Thanks in advance.
Consider using the OR/M for the actual generation of the SQL you actually need.
var dbContext = new DbContext();
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var set = objectContext.Set<Foo>();
var query = from x in set
where {stuff}
select new { x.Bar, x.Baz ...};
var objectQuery = (ObjectQuery)query;
var command = objectQuery.CommandText;
var parameters = objectQuery.Parameters;

How to create and save a serialized and then deserialized entity in EF

I have the follow flow:
Entity is created
Current context is closed
Updated
By property setters, also by navigation properties
Put in ViewState
Serialized, type marked with the [Serializable] attribute)
Read from ViewState
Deserialized
Save
New context is open
How should I properly create an entity: stub (by default constructor) or DbSet<T>.Create()?
How should I properly save the entity: DbSet<T>.Add() or DbSet<T>.Attach()?
I'm getting various exceptions:
"Violation of PRIMARY KEY constraint 'PK_currency_types'. Cannot insert duplicate key in object 'dbo.currency_types'. The duplicate key value is (1). The statement has been terminated."
"A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship."
I'm using EF 4.3 Database First (I have database, designer but entities are auto-generated POCOs) with DbContext Generator extension. I'm new to EF and completely stuck.
Update: Here's my code, it's simple so I haven't added it from the beginning:
public IStatement Create()
{
using (var db = new ModelContainer())
{
// ID is auto-generated by db, INT IDENTITY(1,1)
return new Statement();
// or
// return b.Statement.Create();
}
}
// somewhere in the middle, for example:
statement.Currency = db.Currency.Single(c => c.Name == "Euro");
statement.Amount = 1000;
public void Save(IStatement[] statement)
{
using (var scope = new TransactionScope())
using (var db = new ModelContainer())
{
foreach (var s in statement)
{
// statement has a number of navigation properties, i.e. referenced by FK entities
// need to add/attach each back to db
}
db.SaveChanges();
scope.Complete();
}
}
}
var statement = new Statement();
using (var scope = new TransactionScope())
{
var result = 0;
foreach (var statement in arr.Cast<Statement>())
{
using (var db = new ModelContainer())
{
db.StatementTypes.Attach(statement.StatementType);
db.Entry(statement.StatementType).State = EntityState.Unchanged;
db.Currencies.Add(statement.Currency);
db.Entry(statement.Currency).State = EntityState.Unchanged;
db.Subjects.Attach(statement.Firm);
db.Entry(statement.Firm).State = EntityState.Unchanged;
db.Statement.Add(statement);
db.SaveChanges();
}
}
scope.Complete();

Getting all changes made to an object in the Entity Framework

Is there a way to get all the changes made to a object in the Entity Framework before it saves all changes. The reason for this is that i want to create a log table in our clients database:
so...
Is there a way to get the current database values(old) and the new values(current) before changes are saved?
If not, how can i achieve this in a generic way, so all my View Models can inherit from this?(I am using the MVVM + M Structure)
You can use ObjectContext's ObjectStateManager,GetObjectStateEntry to get an object's ObjectStateEntry, which holds its original and current values in the OriginalValues and CurrentValues properties. You can get the names of the properties that changed using the GetModifiedProperties method.
You can write something like:
var myObjectState=myContext.ObjectStateManager.GetObjectStateEntry(myObject);
var modifiedProperties=myObjectState.GetModifiedProperties();
foreach(var propName in modifiedProperties)
{
Console.WriteLine("Property {0} changed from {1} to {2}",
propName,
myObjectState.OriginalValues[propName],
myObjectState.CurrentValues[propName]);
}
For EF5 upwards you can log your changes in the SaveChanges() method like this:
public override int SaveChanges()
{
var changes = from e in this.ChangeTracker.Entries()
where e.State != System.Data.EntityState.Unchanged
select e;
foreach (var change in changes)
{
if (change.State == System.Data.EntityState.Added)
{
// Log Added
}
else if (change.State == System.Data.EntityState.Modified)
{
// Log Modified
var item = change.Cast<IEntity>().Entity;
var originalValues = this.Entry(item).OriginalValues;
var currentValues = this.Entry(item).CurrentValues;
foreach (string propertyName in originalValues.PropertyNames)
{
var original = originalValues[propertyName];
var current = currentValues[propertyName];
if (!Equals(original, current))
{
// log propertyName: original --> current
}
}
}
else if (change.State == System.Data.EntityState.Deleted)
{
// log deleted
}
}
// don't forget to save
base.SaveChanges();
}
I use this extension function that provides details on the entity being changed, the old and new values, the datatype, and the entity key.
This is tested with EF 6.1 using ObjectContext and uses log4net for output.
/// <summary>
/// dump changes in the context to the debug log
/// <para>Debug logging must be turned on using log4net</para>
/// </summary>
/// <param name="context">The context to dump the changes for</param>
public static void DumpChanges(this ObjectContext context)
{
context.DetectChanges();
// Output any added entries
foreach (var added in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
{
Log.DebugFormat("{0}:{1} {2} {3}",
added.State,
added.Entity.GetType().FullName,
added.Entity.ToString(),
string.Join(",",
added.CurrentValues.GetValue(1),
added.CurrentValues.GetValue(2))
);
}
foreach (var modified in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
// Put original field values into dictionary
var originalValues = new Dictionary<string,int>();
for (var i = 0; i < modified.OriginalValues.FieldCount; ++i)
{
originalValues.Add(modified.OriginalValues.GetName(i), i);
}
// Output each of the changed properties.
foreach (var entry in modified.GetModifiedProperties())
{
var originalIdx = originalValues[entry];
Log.DebugFormat("{6} = {0}.{4} [{7}][{2}] [{1}] --> [{3}] Rel:{5}",
modified.Entity.GetType(),
modified.OriginalValues.GetValue(originalIdx),
modified.OriginalValues.GetFieldType(originalIdx),
modified.CurrentValues.GetValue(originalIdx),
modified.OriginalValues.GetName(originalIdx),
modified.IsRelationship,
modified.State,
string.Join(",",
modified.EntityKey.EntityKeyValues
.Select(v => string.Join(" = ", v.Key, v.Value))
)
);
}
}
// Output any deleted entries
foreach (var deleted in context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted))
{
Log.DebugFormat("{1} {0} {2}",
deleted.Entity.GetType().FullName,
deleted.State,
string.Join(",",
deleted.CurrentValues.GetValue(1),
deleted.CurrentValues.GetValue(2))
);
}
}
Use the IsModified field of each property, which is accessible by Context.Entry(Entity).Properties.
In this example, the modified entries are listed as a Tuple of the original and current values, indexed by name. Use any conversion that is required to build the audit log.
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
//...
// gets somewhere in the scope
DbContext Context;
// Some entity that has been modified, but not saved and is being tracked by Context
object Entity;
//...
Dictionary<string, System.Tuple<object, object>> modified =
Context.Entry(Entity)
.Properties.Where(p => p.IsModified)
.ToDictionary(p => p.Metadata.Name,
p => new System.Tuple<object,object>(p.OriginalValue, p.CurrentValue));
//...
Uses Entity Framework Core 3.1. Try it for EF 6.4, but it may not work.

Categories