I was using a convention like this in the beta version of EF6:
public class NavigationPropertyConfigurationConvention : IConfigurationConvention<PropertyInfo, NavigationPropertyConfiguration>
{
public void Apply(PropertyInfo propertyInfo, Func<NavigationPropertyConfiguration> configuration)
{
var foreignKeyProperty = propertyInfo.DeclaringType.GetProperty("Id" + propertyInfo.Name);
if (foreignKeyProperty != null && configuration().Constraint == null)
{
var fkConstraint = new ForeignKeyConstraintConfiguration();
fkConstraint.AddColumn(foreignKeyProperty);
configuration().Constraint = fkConstraint;
}
}
}
But with the IConfigurationConvention interface has been marked as internal, I can't upgrade my EF's references. Have searched by many places, but not found how to reproduce that functionality in the RTM version.
I have also tried this, but seems only works with independent associations (IAs), what is not my case cause I have the FKs in CLR objects.
Has anyone done it works again?
Thanks!
Related
I am trying to upgrade my project to .net Core 2.0,
I am unable to think any solution to change the below lines.
Old Implementation:
public void ResetChangeTracking<T>(T model) where T : class, ICloneable, ICommonModel
{
// We might get called with a null model. If so, just return.
if (model == null) return;
// Calling ObjectContext.GetObjectType helps when DynamicProxies are being used.
// https://msdn.microsoft.com/en-us/data/jj592886.aspx
string key = $"{ObjectContext.GetObjectType(model.GetType())}-{model.Id}";
if (_originalValues.ContainsKey(key))
{
_originalValues.Remove(key);
}
}
How to make this work in .net Core 2.0?
Thanks
(This is not a dupe, please read my comment.)
I've just migrated from EF Core Preview 5 to Preview 6.
This seems to be a breaking change, especially the mapping will break to the existing Databases if this remains in the release version.
In preview 5 I used:
entityType.Relational.TableName = entityType.DisplayName();
Now it seems Relational property was removed. I would not fall back to manually declare the TableName for all dozens of entities, instead just instruct EF Core model builder do not pluralize automatically them.
EF Core 3 introduces, starting preview6, breaking changes on Provider-specific Metadata API. This includes removal of RelationalMetadataExtensions together with its extension methods such as Relational(this IMutableEntityType entityType).
It is replaced by RelationalEntityTypeExtensions where you can do the following:
IMutableEntityType entity = ...;
entity.SetTableName(entity.DisplayName());
With that, removing automatic pluralization can be done as described in this answer on a related question
using Microsoft.EntityFrameworkCore.Metadata;
public static class ModelBuilderExtensions
{
public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
{
foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
{
entity.SetTableName(entity.DisplayName());
}
}
}
Improved version of Jan Paolo Go's Answer This prevents intermediate table to become something like TeacherStudent Dictionary<string, object>
public static class ModelBuilderExtensions
{
public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
{
foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
{
if (entity is EntityType { IsImplicitlyCreatedJoinEntityType: true })
{
continue;
}
entity.SetTableName(entity.DisplayName());
}
}
}
I'm attempting to update an entity and its related child entities using Entity Framework Core 1.0 RC 1, where the entities are detached from DbContext. I've done this previously using a solution similar to the one described in this answer.
However, it seems that we are no longer able to do the following using Entity Framework 7:
DbContext.Entry(existingPhoneNumber).CurrentValues.SetValues();
Visual Studio complains that:
EntityEntry does not contain a definition for 'CurrentValues'
etc...
I presume this means that this has not (yet?) been implemented for EF Core 1.0? Apart from manually updating the properties, is there any other solution?
As you have noticed, this API is not implemented yet in EF Core. See this work item: https://github.com/aspnet/EntityFramework/issues/1200
I know this is an old question but I ran into this issue today, and it appears it still isn't implemented in EF Core. So I wrote an extension method to use in the meantime that will update any object's properties with the matching values of any other object.
public static class EFUpdateProperties
{
public static TOrig UpdateProperties<TOrig, TDTO>(this TOrig original, TDTO dto)
{
var origProps = typeof(TOrig).GetProperties();
var dtoProps = typeof(TDTO).GetProperties();
foreach(PropertyInfo dtoProp in dtoProps)
{
origProps
.Where(origProp => origProp.Name == dtoProp.Name)
.Single()
.SetMethod.Invoke(original, new Object[]
{
dtoProp.GetMethod.Invoke(dto, null) });
}
);
return original;
}
}
Usage:
public async Task UpdateEntity(EditViewModel editDto)
{
// Get entry from context
var entry = await _context.Items.Where(p => p.ID == editDto.Id).FirstOrDefaultAsync();
// Update properties
entry.UpdateProperties(editDto);
// Save Changes
await _context.SaveChangesAsync();
}
I'm a little confused as to the purpose of a data model in Entity Framework code-first. Because EF will auto-generate a database from scratch for you if it doesn't already exist using nothing more than the data model (including data annotations and Fluent API stuff in DbContext.OnModelCreating), I was assuming that the data model should fully describe your database's structure, and you wouldn't need to modify anything fundamental after that.
However, I came across this Codeplex issue in which one of the EF Triage Team members suggests that custom indexes be added in data migrations, but not as annotations to your data model fields, or Fluent API code.
But wouldn't that mean that anyone auto-generating the database from scratch would not get those custom indexes added to their DB? The assumption seems to be that once you start using data migrations, you're never going to create the database from scratch again. What if you're working in a team and a new team member comes along with a new SQL Server install? Are you expected to copy over a database from another team member? What if you want to start using a new DBMS, like Postgres? I thought one of the cool things about EF was that it was DBMS-independent, but if you're no longer able to create the database from scratch, you can no longer do things in a DBMS-independent way.
For the reasons I outlined above, wouldn't adding custom indexes in a data migration but not in the data model be a bad idea? For that matter, wouldn't adding any DB structure changes in a migration but not in the data model be a bad idea?
Are EF code-first models intended to fully describe a database's structure?
No, they don't fully describe the database structure or schema.Still there are methods to make the database fully described using EF. They are as below:
You can use the new CTP5’s ExecuteSqlCommand method on Database class which allows raw SQL commands to be executed against the database.
The best place to invoke SqlCommand method for this purpose is inside a Seed method that has been overridden in a custom Initializer class. For example:
protected override void Seed(EntityMappingContext context)
{
context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
}
You can even add Unique Constraints this way.
It is not a workaround, but will be enforced as the database will be generated.
OR
If you are badly in need of the attribute, then here it goes
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
public IndexAttribute(string name, bool unique = false)
{
this.Name = name;
this.IsUnique = unique;
}
public string Name { get; private set; }
public bool IsUnique { get; private set; }
}
After this , you will have an initializer which you will call in your OnModelCreating method as below:
public class IndexInitializer<T> : IDatabaseInitializer<T> where T : DbContext
{
private const string CreateIndexQueryTemplate = "CREATE {unique} INDEX {indexName} ON {tableName} ({columnName});";
public void InitializeDatabase(T context)
{
const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
Dictionary<IndexAttribute, List<string>> indexes = new Dictionary<IndexAttribute, List<string>>();
string query = string.Empty;
foreach (var dataSetProperty in typeof(T).GetProperties(PublicInstance).Where(p => p.PropertyType.Name == typeof(DbSet<>).Name))
{
var entityType = dataSetProperty.PropertyType.GetGenericArguments().Single();
TableAttribute[] tableAttributes = (TableAttribute[])entityType.GetCustomAttributes(typeof(TableAttribute), false);
indexes.Clear();
string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;
foreach (PropertyInfo property in entityType.GetProperties(PublicInstance))
{
IndexAttribute[] indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])property.GetCustomAttributes(typeof(NotMappedAttribute), false);
if (indexAttributes.Length > 0 && notMappedAttributes.Length == 0)
{
ColumnAttribute[] columnAttributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), false);
foreach (IndexAttribute indexAttribute in indexAttributes)
{
if (!indexes.ContainsKey(indexAttribute))
{
indexes.Add(indexAttribute, new List<string>());
}
if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
{
string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
indexes[indexAttribute].Add(columnName);
}
else
{
indexes[indexAttribute].Add(property.PropertyType.Name + "_" + GetKeyName(property.PropertyType));
}
}
}
}
foreach (IndexAttribute indexAttribute in indexes.Keys)
{
query += CreateIndexQueryTemplate.Replace("{indexName}", indexAttribute.Name)
.Replace("{tableName}", tableName)
.Replace("{columnName}", string.Join(", ", indexes[indexAttribute].ToArray()))
.Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);
}
}
if (context.Database.CreateIfNotExists())
{
context.Database.ExecuteSqlCommand(query);
}
}
private string GetKeyName(Type type)
{
PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (propertyInfo.GetCustomAttribute(typeof(KeyAttribute), true) != null)
return propertyInfo.Name;
}
throw new Exception("No property was found with the attribute Key");
}
}
Then overload OnModelCreating in your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new IndexInitializer<MyContext>());
base.OnModelCreating(modelBuilder);
}
Apply the index attribute to your Entity type, with this solution you can have multiple fields in the same index just use the same name and unique.
OR
You can do the migrations later on.
Note:
I have taken a lot of this code from here.
The question seems to be if there is value in having migrations added mid-stream, or if those will cause problems for future database initializations on different machines.
The initial migration that is created also contains the entire data model as it exists, so by adding migrations (enable-migrations in the Package Manager Console) you are, in effect, creating the built-in mechanism for your database to be properly created down the road for other developers.
If you're doing this, I do recommend modifying the database initialization strategy to run all your existing migrations, lest EF should start up and get the next dev's database out of sync.
Something like this would work:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourNamespace.YourDataContext, Migrations.Configuration>());
So, no, this won't inherently introduce problems for future work/developers. Remember that migrations are just turned into valid SQL that executes against the database...you can even use script mode to output the TSQL required to make the DB modifications based on anything in the migrations you have created.
Cheers.
I'm using the Poco & DbContext T4 template in vs 2010 to generate the pocos for my data access, i've modified it to match some syntax required by my data access layer.
I've one problem that, the identity column (StoreGeneratedPattern = Identity) in the edmx file dosn't affect the T4 generating process, here is my code for it:
var identity = edmProperty.TypeUsage.Facets.Where(f => f.Name == "StoreGeneratedPattern").FirstOrDefault();
if (identity != null && ((System.Data.Metadata.Edm.StoreGeneratedPattern)identity.Value) == System.Data.Metadata.Edm.StoreGeneratedPattern.Identity)
isIdentity = true;
this is always be false , is there a any reason for this?
StoreGeneratedPattern is not stored in facets but in MetadataProperties try something like this:
var identity = edmProperty.MetadataProperties
.Where(m => m.Name == "http://schemas.microsoft.com/ado/2009/02/edm/annotation:StoreGeneratedPattern")
.FirstOrDefault();
bool isIdentity = identity != null && identity.Value == System.Data.Metadata.Edm.StoreGeneratedPattern.Identity.ToString();
Ladislav has indeed right.
(this post hopefully can spare time for someone who are trying to use IsStoreGeneratedIdentity, IsStoreGeneratedComputed and StoreGeneratedPattern regardless of T4.) It did cost me more than 2 hours to locate the problem and with narrowed googling keywords locate this post)
Interestingly enough even MS does not know about this fact, in its latest EF 6 in 2015, and EdmProperty class exposes trivially not working properties. It also interesting how can these simple (not working) properties pass a unit test in the last 4 years? The question arises, how many places these not working properties are utilized inside and dependent utilities and cause issues?
Not working IsStoreGeneratedIdentitycode from MS EF6 assembly:
public bool IsStoreGeneratedIdentity
{
get
{
Facet facet;
if (this.TypeUsage.Facets.TryGetValue("StoreGeneratedPattern", false, out facet))
return (StoreGeneratedPattern) facet.Value == StoreGeneratedPattern.Identity;
return false;
}
}
Not working IsStoreGeneratedComputedcode from MS EF6 assembly:
public bool IsStoreGeneratedComputed
{
get
{
Facet facet;
if (this.TypeUsage.Facets.TryGetValue("StoreGeneratedPattern", false, out facet))
return (StoreGeneratedPattern) facet.Value == StoreGeneratedPattern.Computed;
return false;
}
}
And the the StoreGeneratedPattern property uses this underlying and not working code in MS EF6 assembly:
internal static StoreGeneratedPattern GetStoreGeneratedPattern(EdmMember member)
{
Facet facet;
if (member.TypeUsage.Facets.TryGetValue("StoreGeneratedPattern", false, out facet) && facet.Value != null)
return (StoreGeneratedPattern) facet.Value;
return StoreGeneratedPattern.None;
}
Code comes from:
// Type: System.Data.Entity.Core.Metadata.Edm.EdmMember
// Assembly: EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: 9A9FD4AC-352D-4B91-95F8-2AF29ABDC792
// Assembly location: C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\EntityFramework.dll
Finally I've ended with the following extension methods as a replacement of the not working original ones:
public static class EdmMemberExtensions
{
public static StoreGeneratedPattern StoreGeneratedPattern2(this EdmMember #this)
{
const string name = "http://schemas.microsoft.com/ado/2009/02/edm/annotation:StoreGeneratedPattern";
var metaDataProperty = #this.MetadataProperties.FirstOrDefault(m => m.Name == name);
if (metaDataProperty == null)
{
return StoreGeneratedPattern.None;
}
return (StoreGeneratedPattern) Enum.Parse(typeof (StoreGeneratedPattern), (string) metaDataProperty.Value);
}
public static bool IsStoreGeneratedIdentity2(EdmMember #this)
{
return StoreGeneratedPattern2(#this) == StoreGeneratedPattern.Identity;
}
public static bool IsStoreGeneratedComputed2(EdmMember #this)
{
return StoreGeneratedPattern2(#this) == StoreGeneratedPattern.Computed;
}
}