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.
Related
I have to build a .net web application accessing tables of an existing db.
The db uses different tables for different companies: customers in company "ACorp" are stored in table "ACorpCustomers", those in company "B" are stored in table "BCorpCustomers".
Using ADO .NET Entity Model, I created a different Db Context for each Company:
public partial class ACorpContext : DbContext
{
public ACorpContext()
: base("name=ACorpContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<ACorpCustomer> ACorpCustomers { get; set; }
}
}
The edmx generates also the class
public partial class ACorpCustomer
{
public string Name { get; set; }
public string Phone { get; set; }
}
I created a parent class Customer to be used in the application, with the same properties:
public class ACorpCustomer
{
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
}
I havent't found a way to let the specific entity ACorpCustomers inherit from the parent Customer; the edmx returns the inheritance error, but there is no way to override the properties.
Update
In order to avoid edmx file usage, this is what I finally tried out:
I disabled the __MigrationHistory sql table creation using the AutomaticMigrationsEnabled parameter:
internal sealed class Configuration : DbMigrationsConfiguration<MyDomain.Models.ACorpContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
I disabled the db initialization in the App.config file setting
disableDatabaseInitialization="true"
Then I added a an ADO .NET Entity Model but chose the "code first from database".
In order to be sure not to change the db from the model, I disabled the DB Initializer:
public ACorpContext()
: base("name=ACorpContext")
{
Database.SetInitializer<ACorpContext>(null);
}
Now I expect to be my responsability to be keep in sync the domain model with the db.
Anyway, I feel sure that in case of misalignment no attempt will be done to modify the db.
Without the edmx, I have no more limitations defining inheritance from an abstract class Customer.
I cannot understand why Visual Studio considers this as "Code First" approach, anyway.
Your definition
public partial class ACorpCustomer
has nothing to do with inheritance. partial is a .NET moderator that signifies that your class definition is a part of the bigger definition. For example if you have your class split between 2 code files. .Net "puts" them together and you endup with one type
Here what you seem need to do is
public abstract class Customer
{
public string Name { get; set; }
public string Phone { get; set; }
}
public class ACorpCustomer : Customer
{
// may be, some unique properties here
}
public class BCorpCustomer : Customer
{
// may be, some unique properties here
}
The properties Name and Phone don't even need to be virtual. Looking back into your title, there is nothing that you need to override. Nothing that I see..
This is trivial in Code-First, which you can (and should) use with an existing database. Just map the single Customer entity to the correct table for each DbContext:
public partial class ACorpContext : MyBaseDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable("ACorpContext");
}
public virtual DbSet<Customer> Customers{ get; set; }
}
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.
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.
Sorry if somebody has already asked this question. I've been looking around but couldn't find anything related.
So, I am using Entity Framework and I am trying to load the list of "Idiomas" from the dataset using the ToList method through the following code:
//List<Idioma> ans = new List<Idioma>(contexto.Idiomas);
return contexto.Idiomas.ToList();
However, the method is returning an empty list, even though I can see through the debugger that the DataSet has elements.
EDIT
I have not put any extra code because there is not any extra code besides those two lines.
I just create a Entity Framework model-first, generated the database, and updated the model from the database to make sure everything was ok.
Contexto is a instance from the class LivroContexto, which implements DbContext (auto generated by vs2012, see below).
LivroContainer:
public partial class LivroContainer : DbContext
{
public LivroContainer() : base("name=LivroContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
public DbSet<Idioma> Idiomas { get; set; }
}
Idiomas:
public partial class Idioma
{
public Idioma()
{
this.Traducaos = new HashSet<Traducao>();
}
public int IdIdioma { get; set; }
public string Lingua { get; set; }
public virtual ICollection<Traducao> Traducaos { get; set; }
}
Thank you
nobody can see, why your code is not working, from the amount of code you have posted.
however, make sure following :
the class of the instance contexto inherits DbContext or any child class inheriting DbContext.
make sure, in case, you have not generated the database, through the code,rather mapped an existing database, to a codefirst frontend, that DbSet<Idioma> Idiomas is mapped to proper table. ie.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Idioma>().ToTable("IdiomaTableNameInDatabase");
}
make sure, your connectionString name, that you passed in the DbContext base constructor is present in the web.config, and if you have not passed anything, make sure a connectionString with the name of your DbContext inheriting class is there.
If all these are correct, there is absolutely no reason, why contexto.Idiomas.ToList() won't return anything.
and also, say, your context class is MyContext, then it should be like this:
public class MyContext:DbContext
{
public MyContext:base("ConnectionStringName")
{
}
//--dbSet properties
public DbSet<Idioma> Idiomas{get;set;}
//other overridden methods
}
which you use, on your upper layers like this:
public List<Idioma> GetAllIdiomas()
{
MyContext contexto = new MyContext();
return contexto.Idiomas.ToList();
}
I'm using Fluent-NHibernate and attempting to persist an object hierarchy using the table per subclass method:
public class AbstractProduct
{
public int ProductId { get; set; }
public string Name { get; set; }
}
public class SingleProduct : AbstractProduct
{
public int SingleProductId { get; set; }
public string SomeField { get; set; }
}
when saving an object
var singleProduct = new SingleProduct();
session.SaveOrUpdate(singleProduct);
I get this error:
NHibernate.Exceptions.GenericADOException: could not insert: [FluentNHibernateSubClassTest.SingleProduct#3][SQL: INSERT INTO SingleProductData (Field1, AbstractProduct_id) VALUES (?, ?)] ---> System.Data.SqlClient.SqlException: Invalid column name 'AbstractProduct_id'.
despite having the following overrides:
public class AbstractProductOverrides : IAutoMappingOverride<AbstractProduct>
{
public void Override(AutoMapping<AbstractProduct> mapping)
{
mapping.Id(x => x.ProductId).Column("ProductId");
//this mapping provided to illustrate the overrides are picked up
mapping.Table("ProductsData");
mapping.JoinedSubClass<SingleProduct>("ProductId");//ignored??
}
}
public class SingleProductOverrides : IAutoMappingOverride<SingleProduct>
{
public void Override(AutoMapping<SingleProduct> mapping)
{
mapping.Id(x => x.SingleProductId);
mapping.Table("SingleProductData");
mapping.Map(x => x.SomeField).Column("Field1");
}
}
It doesn't appear to matter what column name I supply to JoinedSubClass it ignores it and uses AbstractProduct_id instead.
How can I tell nhibernate the key column is ProductId and not AbstractProduct_id?
I have a test project demonstrating the issue available here (you need to create the db)
UPDATE
I've got around this by providing the following convention:
public class JoinedSubclassConvention : IJoinedSubclassConvention
{
public void Apply(IJoinedSubclassInstance instance)
{
if (instance.EntityType == typeof(SingleProduct))
instance.Key.Column(("ProductId"));
}
}
which works but feels like its the wrong way or a hack.
mapping.Id in SingleProductOverrides is flawed. Subclasses don't have their own id, they inherit the Id from their base classes. Even mapping.JoinedSubClass<SingleProduct>("ProductId"); is redundant (probably ignored) if SingleProduct is automapped as well (it is as seen from the Override for it). JoinedSubclassConvention is the right way to do this.