Set database schema name without setting table name - c#

I want to set database schema name for a table, but the only way do it is using method ToTable:
modelBuilder.Entity<MyEntity>().ToTable("MyTable", schemaName);
However I don't want to set table name explicitly, I need something like this:
modelBuilder.Entity<MyEntity>().ToSchema(schemaName);
Can anyone help me with it?

Using this extension method:
using System.Data.Entity.ModelConfiguration;
public static class ModelConfigurationHelper
{
public static EntityTypeConfiguration<T> ToSchema<T>(this EntityTypeConfiguration<T> config, string schema)
{
return config.ToTable(typeof(T).Name, schema);
}
}
you can do:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>().ToSchema("someSchema);
}

Related

What is the Entity Framework Core attribute equivalent to modelBuilder's HasDefaultValueSql?

I want to use annotations for setting the default value for my properties in Entity Framework Core. The issue is that the database is not setting the default values so the value is not being passed down to the database layer.
I want to do something similar to modelBuilder's HasDefaultValueSql:
[DefaultValue("400")]
public int LengthInMeters {get; set;}
How do you convert the below code to attributes?
modelBuilder.Entity<Patient>().Property(c => c.LengthInMeters).HasDefaultValueSql("400");
Using default values by themselves doesn't work. I want to use attributes alone without having to mess with the migrations.
Problems: I've tried other methods with EF but Entity Framework Core doesn't have some items. Such as modelBuilder.Conventions nor AttributeToColumnAnnotationConvention nor CSharpMigrationCodeGenerator nor modelBuilder.Properties()
This is what I ended up doing, if someone has a cleaner not as intensive way of implementation let me know.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
var memberInfo = property.PropertyInfo ?? (MemberInfo)property.FieldInfo;
if (memberInfo == null) continue;
var defaultValue = Attribute.GetCustomAttribute(memberInfo, typeof(DefaultValueAttribute)) as DefaultValueAttribute;
if (defaultValue == null) continue;
property.SqlServer().DefaultValue = defaultValue.Value;
}
}
}
I can set the default value in the database using the default value attribute
[DefaultValue("400")]
public int LengthInMeters {get; set;}
Struggled a while getting this job done in another way using EF-Core conventions. I discovered a way to add so called "Plugins" which implement the IConventionSetPlugin interface with which you can add custom conventions. It needs some additional code to get EntityFramework to use the plugin.
But first things first, let's create our PropertyAttributeConvention.
public class DefaultValueAttributeConvention : PropertyAttributeConventionBase<DefaultValueAttribute>
{
public DefaultValueAttributeConvention(ProviderConventionSetBuilderDependencies dependencies) : base(dependencies) { }
protected override void ProcessPropertyAdded(IConventionPropertyBuilder propertyBuilder, DefaultValueAttribute attribute,
MemberInfo clrMember, IConventionContext context)
{
propertyBuilder.HasDefaultValue(attribute.Value, fromDataAnnotation: true);
}
}
Here we just say the ef propertybuilder to use the default value defined in our [DefaultValue] attribute.
To add the convention we need to create a custom plugin class:
public class CustomConventionSetPlugin : IConventionSetPlugin
{
public ConventionSet ModifyConventions(ConventionSet conventionSet)
{
conventionSet.PropertyAddedConventions.Add(new DefaultValueAttributeConvention(null));
return conventionSet;
}
}
For our plugin to get used, we have to create an ef extension class (which itself contains another ExtensionInfo class)
public class CustomDbContextOptionsExtension : IDbContextOptionsExtension
{
public void ApplyServices(IServiceCollection services)
{
services.AddSingleton<IConventionSetPlugin, CustomConventionSetPlugin>();
}
public void Validate(IDbContextOptions options) { }
public DbContextOptionsExtensionInfo Info => new CustomDbContextOptionsExtensionInfo(this);
private class CustomDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo
{
public CustomDbContextOptionsExtensionInfo(IDbContextOptionsExtension extension) : base(extension) { }
public override long GetServiceProviderHashCode() => 0;
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo) { }
public override bool IsDatabaseProvider => false;
public override string LogFragment => "";
}
}
In the extension class we're adding our plugin class to the EF-ServiceCollection.
The last step is to go to our DbContext and add our extension. This can be done in the OnConfigure method:
public class MyDatacontext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(new CustomDbContextOptionsExtension());
}
}
Now the [DefaultValue] attribute can be used on our entity properties.
If we want to add different custom conventions we dont have to create all that extension/plugin classes again. Just create a new convention class and add it through our existing plugin class to the convetionSet.
Install Microsoft.EntityFrameworkCore.Relational package, it should solve most of your migration issues when moving to EF core.

ORM and prefixes for the tables

Task this, when registering the customer in the database we need to create a set of tables with a unique prefix, it is generally possible to implement using EF?
I haven't tested this but it should work, or at least be a good start point. First you need to tell your context the prefix you will be using, I'd do this in the constructor. Then in the OnModelCreating override, use the prefix for the table names:
public class MyContext : DbContext
{
private readonly string _prefix;
public MyContext(string prefix)
{
_prefix = prefix;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Types()
.Configure(entity => entity.ToTable(_prefix + entity.ClrType.Name));
}
}

Customize FluentMigrator Postgres VersionInfo Schema

I am trying to use FluentMigrator with PostgreSQL.
I have it running the migrations successfully, however the VersionInfo table is always in the public schema. I read on the FluentMigrator Wiki that I could override the schema name, but it is not working.
Here is my class that I wrote to override the settings:
namespace YARA.Migrations
{
using FluentMigrator.VersionTableInfo;
[VersionTableMetaData]
public class YaraVersionTable : DefaultVersionTableMetaData
{
public override string SchemaName
{
get { return "dbo"; }
}
public override string TableName
{
get
{
return "MigrationInfo";
}
}
}
}
And here is a screenshot of the db after running the migrations; with neither the schema or changing the table name taking effect for the VersionInfo table.
Thoughts?
You have to create your class, in my case:
[VersionTableMetaData]
public class CustomMetadataTable : DefaultVersionTableMetaData
{
public override string TableName => "__migrations_metadata";
}
Then add it to your services
var serviceProvider = new ServiceCollection()
.UseStandardConfiguration()
.AddFluentMigratorCore()
.ConfigureRunner(rb => rb
.AddPostgres92()
.WithGlobalConnectionString(c => c.GetService<IConfiguration>().GetConnectionString("THECONNECTION_STRING"))
.ScanIn(THEASSEMBLY).For.Migrations())
.AddLogging(lb => lb.AddFluentMigratorConsole())
.AddTransient<IVersionTableMetaData, CustomMetadataTable>()
.BuildServiceProvider(false);
Just make sure is the last thing you register (just right above the last line)

Fluent NHibernate automap PostGIS geometry type

Given the following model:
using NetTopologySuite.Geometries;
public class bounding_box
{
public virtual int id { get; protected set; }
public virtual Polygon area { get; set; }
}
How do I automap the area property to a area geometry(Polygon) column when generating the DB schema using Fluent Nhibernate? Note that I do not care about being able to read / update the geometry column using NHibernate since I will be using GDAL in my code.
I know I can do it by implementing a manual override, i.e.:
public class bounding_boxMappingOverrride : IAutoMappingOverride<bounding_box>
{
public void Override(AutoMapping<bounding_box> mapping)
{
mapping.Map(x => x.area)
.CustomSqlType("geometry(Polygon)");
}
}
However, I have many tables with geometry columns so I would much prefer to be able to specify a custom type mapping.
For some reason, the area property is never intercepted by the following property convention:
public class PostgisTypesConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Type == typeof(Polygon))
{
instance.CustomSqlType("geometry(Polygon)"); // Never reached
}
}
}
I have the same problem if I use GeoAPI.Geometries.IPolygon instead of NetTopologySuite.Geometries.Polygon...
I was finally able to resolve this by defining a custom UserTypeConvention, i.e.:
using NetTopologySuite.Geometries;
using NHibernate.Spatial.Type;
public class PostGisPolygonUserTypeConvention : UserTypeConvention<PostGisGeometryType>
{
public override void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(c => c.Type == typeof(Polygon));
}
public override void Apply(IPropertyInstance instance)
{
// Have to set CustomType to be able to read/write rows using NHibernate
instance.CustomType<PostGisGeometryType>();
// Have to set CustomSqlType to generate correct SQL schema
instance.CustomSqlType("geometry(Polygon)");
}
}
The same principle can also be used to create UserTypeConventions for other geometries, such as Point, LineString, MultiPoint, etc.

Emit DbContext.OnModelCreating on every context creation

I use entity framework code first to work with my database.
I have several tables with different names but same structure, and this tables dynamically appears in database. How could I map EntityFramework to one of that tables at run-time and use data from just like I work this over entities of DbContext?
What I've done to make it work:
For example, my class what describes structure of dynamically created table is SetElement.
Here is my context:
public class DataContext : DbContext
{
public DataContext()
: base("RepositoryConnectionString") { }
string setElementsTableId; // the name of table that need to be dynamicly mapped to
// Enforce model recreating
public DataContext(string setElementsTableId)
: this()
{
this.setElementsTableId = setElementsTableId;
}
/* some other entities */
public DbSet<Entities.SetElement> SetElements { get; set; } // dynamicly mapped entity
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
/* come configurations */
if (!string.IsNullOrEmpty(setElementsTableId))
{
modelBuilder.Entity<Entities.SetElement>().Map(x => x.ToTable(setElementsTableId)); // map SetElements property to dynamicly created table
}
}
}
How I use this:
public static void AddSetElements(ICollection<SetElement> setElements, string tableId)
{
using (ctx = new DataContext(tableId)) // configere DataContext to map tableId table for entity SetElements
try
{
var num = ctx.SetElements.Count();
ctx.SetElements.AddRange(setElements);
ctx.SaveChanges();
}
catch (Exception e)
{
}
}
I have also some methods to get, udtate and remove data from dynamicly created tables that are same to AddSetElements.
All works just as I wish but only if AddSetElements runs first, because at the first datacontext creating DbContext.OnModelCreating runs and configure all mappings. But next instance creation doesn't call DbContext.OnModelCreating.
So, my question is: how to call DbContext.OnModelCreating everytime of creating an instance of DataContext then I use DataContext(string setElementsTableId) to create it?
I know, my question is similar to 'dynamic table mapping in EF' but I found nothing in the results.
By the way. If you know another way to solve my problem, you are welcome.
There is a built-in feature which may address your issue : `IDbModelCacheKey ; the implementation of which is to be registered in your configuration.
The point is to generate a different key for your different contexts.
I would go for something like :
First, the configuration
public class EntityFrameworkConfiguration: DbConfiguration
{
public EntityFrameworkConfiguration()
{
this.SetModelCacheKey(ctx => new EntityModelCacheKey((ctx.GetType().FullName + ctx.Database.Connection.ConnectionString).GetHashCode()));
}
}
Then the implementation of the IDbModelCacheKey
public class EntityModelCacheKey : IDbModelCacheKey
{
private readonly int _hashCode;
public EntityModelCacheKey(int hashCode)
{
_hashCode = hashCode;
}
public override bool Equals(object other)
{
if (other == null) return false;
return other.GetHashCode() == _hashCode;
}
public override int GetHashCode()
{
return _hashCode;
}
}
Finally, your DataContext
public class DataContext : DbContext
{
string setElementsTableId;
// use the setElementsTableId as extended property of the
// connection string to generate a custom key
public DataContext(string setElementsTableId)
: base(ConfigurationManager.ConnectionStrings["RepositoryConnectionString"]
+ "; Extended Properties=\"setElementsTableId=" + setElementsTableId + "\"")
{
this.setElementsTableId = setElementsTableId;
}
public DbSet<Entities.SetElement> SetElements { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (!string.IsNullOrEmpty(setElementsTableId))
{
modelBuilder.Entity<Entities.SetElement>().Map(x => x.ToTable(setElementsTableId));
}
}
}
I hope this will be of some help
Look like nobody knows answer...
Otherwise, one man told me that my question is meaningless because of storage data in several tables will not give any achievement. More better to add indexes to database, partitioning table or something else. In other words this is Database Management System problem. But if some one knows answer I'll be very pleasured to hear something about EF hack.

Categories