I'm currently working on a customer project were I have to store data from the entities in a sqlite DB. I'm working with EF6 and I've chosen the "code first" approach.
Problem: I would like to store geographic coordinates (as single Point in Boo and as a List of Points in Loo) as System.Drawing.PointF in the DB. Because it's not a primitive type it's unfortunately not working like I taught.
Question: What do I have to change in my Entity definition and/or in my Model Configuration to store the single Point of Boo and also the List of Points of Loo in the DB? Thank you in advance!
My Entity classes:
public class Collection
{
[Key]
public int Id { get; set; }
public virtual List<Foo> Foos { get; set; }
}
public class Foo
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual Collection FooCollection;
}
public class Boo : Foo
{
public PointF Location { get; set; }
}
public class Loo : Foo
{
public List<PointF> Line { get; set; }
}
My Model Configuration:
public class ModelConfiguration
{
public static void Configure(DbModelBuilder modelBuilder)
{
ConfigureFooCollectionEntity(modelBuilder);
}
private static void ConfigureFooCollectionEntity(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Collection>().ToTable("base.MyTable")
.HasRequired(t => t.Foos)
.WithMany()
.WillCascadeOnDelete(false);
}
private static void ConfigureGridElementEntity(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>()
.HasRequired(p => p.FooCollection)
.WithMany(fooCollection => fooCollection.Foos)
.WillCascadeOnDelete(true);
}
}
My DbContext:
public class DbContext : DbContext
{
public DbSet<Collection> DataCollection { get; set; }
public DbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
Configure();
}
public DbContext(DbConnection connection, bool contextOwnsConnection)
: base(connection, contextOwnsConnection)
{
Configure();
}
private void Configure()
{
Configuration.ProxyCreationEnabled = true;
Configuration.LazyLoadingEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
ModelConfiguration.Configure(modelBuilder);
var initializer = new DbInitializer(modelBuilder);
Database.SetInitializer(initializer);
}
}
My DbInitializer:
class DbInitializer :
SqliteDropCreateDatabaseWhenModelChanges<DbContext>
{
public GDbInitializer(DbModelBuilder modelBuilder)
: base(modelBuilder, typeof(CustomHistory))
{ }
}
The easiest way to solve this problem is to work not with System.Drawing.PointF but with your own point type. This way you are able to give it a ID property and EF can store it as a separate DB-table.
This is how it works for me:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class GeoPoint
{
[Key]
public int Id { get; set; }
public float X { get; set; }
public float Y { get; set; }
[NotMapped]
public PointF GetPointF { get { return new PointF(X, Y); } }
public GeoPoint()
{
X = 0; Y = 0;
}
public GeoPoint(float x, float y)
{
X = x; Y = y;
}
public GeoPoint(PointF point)
{
X = point.X;
Y = point.Y;
}
}
Related
I have 3 sqlite databases, and each has only one table called "Logs".
These "Logs" tables have different columns:
Database1.db
Logs(Id, VarA, VarB)
Database2.db
Logs(Id, VarC, VarD)
Database3.db
Logs(Id, VarE, VarF)
I've modeled this with EntityFramework 6 as follows:
public class Database1Log
{
public int Id { get; set; }
public float? VarA { get; set; }
public float? VarB { get; set; }
}
public class Database2Log
{
public int Id { get; set; }
public float? VarC { get; set; }
public float? VarD { get; set; }
}
public class Database3Log
{
public int Id { get; set; }
public float? VarE { get; set; }
public float? VarF { get; set; }
}
I also have 3 different DbContext:
public class Database1DbContext : DbContext
{
public DbSet<Database1Log> Logs { get; set; }
public Database1DbContext ()
{
Database.SetInitializer<Database1DbContext >(null);
}
}
public class Database2DbContext : DbContext
{
public DbSet<Database2Log> Logs { get; set; }
public Database2DbContext ()
{
Database.SetInitializer<Database2DbContext >(null);
}
}
public class Database3DbContext : DbContext
{
public DbSet<Database3Log> Logs { get; set; }
public Database3DbContext ()
{
Database.SetInitializer<Database3DbContext >(null);
}
}
Question:
Is it possible to refactor the following code using interfaces and/or inheritance?
class Program
{
static void Main(string[] args)
{
if (args[0].Equals("Database1"))
{
var dbContext = new Database1DbContext();
var logs = dbContext.Logs();
Console.WriteLine(JsonConvert.SerializeObject(logs));
}
else if (args[0].Equals("Database2"))
{
var dbContext = new Database2DbContext();
var logs = dbContext.Logs();
Console.WriteLine(JsonConvert.SerializeObject(logs));
}
else if (args[0].Equals("Database3"))
{
var dbContext = new Database3DbContext();
var logs = dbContext.Logs();
Console.WriteLine(JsonConvert.SerializeObject(logs));
}
}
}
This is what I've tried so far:
public interface ILog
{
int Id { get; set; }
}
public interface IDbContext
{
DbSet<ILog> Logs { get; set; }
}
public class Database1DbContext : DbContext, IDbContext
{
public DbSet<ILog> Logs { get; set; }
public Database1DbContext ()
{
Database.SetInitializer<Database1DbContext >(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Database1Log>()
.HasKey(u => u.Id)
.ToTable("Logs");
}
}
public class Database2DbContext : DbContext, IDbContext
{
public DbSet<ILog> Logs { get; set; }
public Database2DbContext ()
{
Database.SetInitializer<Database2DbContext >(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Database2Log>()
.HasKey(u => u.Id)
.ToTable("Logs");
}
}
public class Database3DbContext : DbContext, IDbContext
{
public DbSet<ILog> Logs { get; set; }
public Database3DbContext ()
{
Database.SetInitializer<Database2DbContext >(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Database3Log>()
.HasKey(u => u.Id)
.ToTable("Logs");
}
}
class Program
{
static void Main(string[] args)
{
IDbContext dbContext = null;
List<ILog> logs = null;
if (args[0].Equals("Database1"))
{
dbContext = new Database1DbContext();
}
else if (args[0].Equals("Database2"))
{
dbContext = new Database2DbContext();
}
else if (args[0].Equals("Database3"))
{
dbContext = new Database3DbContext();
}
logs = dbContext.Logs();
Console.WriteLine(JsonConvert.SerializeObject(logs));
}
}
But it throws:
The type 'ConsoleApp.Models.ILog' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive or generic, and does not inherit from EntityObject.
You could use some kind of factory or injection since youre predicating which type of database to instantiate based on a string input from the user/program. Maybe start down a path like this:
public interface IDbContext
{
DbSet<ILog> Logs { get; set; }
}
public interface IDbContextFactory
{
IDbContext CreateDb(string type);
}
Then the actual factory:
public class DbFactory : IDbContextFactory
{
public IDbContext Create(string name)
{
if (name == "1")
{
return new Database1DbContext();
}
// else if 2
// else if 3
}
}
Then your consuming code could look something like:
IDbContextFactory factory = new DbContextFactory();
IDbContext db = factory.Create(args[0]);
DbSet<ILog> logs = db.Logs;
Console.WriteLine(JsonConvert.SerializeObject(logs));
I'm trying to create a One-to–Zero-or-One relationship between two tables in a Code First design with Fluent API. But when it gets created, the relationship in database has become a Many-to-One relationship. I have looked at several tutorials and many Questions on forums but I can't find the problem with my code.
Generated Database Scheme:
Scheme on Imgur
My DbContext file:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Eigendommen>().HasKey(e => e.Id);
modelBuilder.Entity<Gronden>().HasKey(g => g.Id);
modelBuilder.Entity<Eigendommen>()
.HasRequired(g => g.Grond)
.WithOptional(e => e.Eigendommen);
base.OnModelCreating(modelBuilder);
}
}
Base class of the table:
public abstract class EntityBase
{
public int Id { get; protected set; }
public EntityBase()
{
}
}
My Main table(One):
public class Eigendommen : EntityBase
{
public string Gemeente { get; set; }
public string Straat { get; set; }
public int Grond_Id { get; set; }
public virtual Gronden Grond { get; set; }
public Eigendommen() : base()
{
Gemeente = "Eksaarde";
Straat = "Ramstraat";
}
}
My second table(Zero-or-One):
public enum TypeGround { Bebouwd, Verkaveling };
public class Gronden : EntityBase
{
public double Opp { get; set; }
public TypeGround Type { get; set; }
public virtual Eigendommen Eigendommen { get; set; }
public Gronden() : base()
{
Opp = 11;
Type = TypeGround.Bebouwd;
}
}
I don't see a difference with the many tutorials I followed, I hope you can see the problem?
If you need more code, just ask!
Thank you in advance.
I did some refactoring to a code base, and created an abstract class SlottedHardware that held some common properties that other classes should use.
However, I am now getting the error:
Schema specified is not valid. Errors: The relationship 'MyProject.Models.NetworkDevice_Slots' was not loaded because the type 'MyProject.Models.Models.NetworkDevice' is not available.
when trying to create the database by setting my DbContext ctor to Database.SetInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
I have been at this for hours, and would really appreciate if someone can lend a helping hand. Here are some of the entity classes, as well as the Fluent API mapping:
public abstract class EntityBase
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
public abstract class SlottedHardware : EntityBase
{
public int MaxSlots { get; set; }
public virtual ICollection<Slot> Slots { get; set; }
}
public class Slot : EntityBase
{
public string SlotIdentifier { get; set; }
public List<Card> CompatibleCards { get; set; } = new List<Card>();
public State State { get; set; }
public virtual NetworkDevice NetworkDevice { get; set; }
}
public class NetworkDevice : SlottedHardware
{
public string Vendor { get; set; }
public string Model { get; set; }
public List<UnpublishedConfig> UnpublishedConfigs { get; set; }
public List<PublishedConfig> PublishedConfigs { get; set; }
public State State { get; set; }
/*** Constructors ***/
public NetworkDevice()
{
MaxSlots = 0;
Slots = new List<Slot>();
UnpublishedConfigs = new List<UnpublishedConfig>();
PublishedConfigs = new List<PublishedConfig>();
}
public NetworkDevice(string vendor, string model, int maxSlots) : this()
{
Vendor = vendor;
Model = model;
if(maxSlots > 0)
{
MaxSlots = maxSlots;
}
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// NetworkDevice entity
modelBuilder.Entity<NetworkDevice>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("NetworkDevices");
});
modelBuilder.Entity<NetworkDevice>().HasKey(t => t.Id);
modelBuilder.Entity<NetworkDevice>().
HasMany(t => t.Slots).
WithOptional(t => t.NetworkDevice);
modelBuilder.Entity<NetworkDevice>().
HasMany(t => t.PublishedConfigs).
WithMany();
modelBuilder.Entity<NetworkDevice>().
HasMany(t => t.UnpublishedConfigs).
WithMany();
modelBuilder.Entity<NetworkDevice>().Property(t => t.MaxSlots).IsRequired();
modelBuilder.Entity<NetworkDevice>().Property(t => t.Model).IsRequired();
// Slot entity
modelBuilder.Entity<Slot>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Slots");
});
modelBuilder.Entity<Slot>().HasKey(t => t.Id);
modelBuilder.Entity<Slot>().HasOptional(t => t.NetworkDevice).WithMany(x => x.Slots);
modelBuilder.Entity<Slot>().HasMany(t => t.CompatibleCards).WithMany(x => x.Slots);
modelBuilder.Entity<Slot>().Property(t => t.SlotIdentifier).IsRequired();
modelBuilder.Entity<Slot>().Ignore(t => t.Card);
}
I believe that I am having an issue with a filter incorrectly being applied to a relationship that it should not be. Here are my objects:
public enum Capability
{
Create = 1,
Edit = 2,
Delete = 3
}
public class Role
{
public virtual int TenantId {get;set;
public virtual IList<Capability> Capabilities { get; set; }
}
Here is a mapping override:
public class RoleOverride : IAutoMappingOverride<Role>
{
public void Override(AutoMapping<Role> mapping)
{
mapping.HasMany(x => x.Capabilities)
.Cascade.All()
.Table("RoleCapability")
.Element("CapabilityId", e => e.Type<NHibernate.Type.EnumType<Capability>>())
.AsBag()
.Not.LazyLoad();
}
}
Here is my filter:
public class FilterHasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.ApplyFilter<TenantFilter>("tenantid = :tid");
}
}
public class TenantFilter : FilterDefinition
{
public TenantFilter()
{
WithName("TenantFilter").AddParameter("tid", NHibernateUtil.String);
}
}
Now, when I'm trying to load my Role object, there is an issue where it is doing this:
SELECT capabiliti0_.RoleId as RoleId0_, capabiliti0_.CapabilityId as Capabili2_0_
FROM RoleCapability capabiliti0_ WHERE capabiliti0_.tenantid = :TenantFilter.tid and capabiliti0_.RoleId=?
The problem is that TenantId should not be applied to the RoleCapability relationship. Is there any way to stop this?
Thanks
This solved my issue:
public abstract class TenantEntity
{
public virtual int TenantId {get;set;}
}
public class Role : TenantEntity
{
public virtual int TenantId {get;set;
public virtual IList<Capability> Capabilities { get; set; }
}
public class FilterHasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
if (instance.ChildType.IsSubclassOf(typeof(TenantEntity)))
{
instance.ApplyFilter<TenantFilter>("tenantid = :tid");
}
}
}
I have the following table [PaymentComponent] created using following EF code first approach (TPH inheritance). It works fine. I need to change the database design – need to store GiftCouponPayments in GiftCouponPayment table and ClubCardPayments in ClubCardPayment table. What change need to be done in C# code to get the required database structure?
CODE
public abstract class PaymentComponent
{
public int PaymentComponentID { get; set; }
public int MyValue { get; set; }
public string MyType { get; set; }
public abstract int GetEffectiveValue();
}
public partial class GiftCouponPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
if (MyValue < 2000)
{
return 0;
}
return MyValue;
}
}
public partial class ClubCardPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
return MyValue;
}
}
public partial class Payment
{
public int PaymentID { get; set; }
public List<PaymentComponent> PaymentComponents { get; set; }
public DateTime PayedTime { get; set; }
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
public DbSet<Payment> Payments { get; set; }
}
CLIENT
static void Main(string[] args)
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
GiftCouponPayment giftCouponPayment = new GiftCouponPayment();
giftCouponPayment.MyValue=250;
giftCouponPayment.MyType = "GiftCouponPayment";
ClubCardPayment clubCardPayment = new ClubCardPayment();
clubCardPayment.MyValue = 5000;
clubCardPayment.MyType = "ClubCardPayment";
List<PaymentComponent> comps = new List<PaymentComponent>();
comps.Add(giftCouponPayment);
comps.Add(clubCardPayment);
var payment = new Payment { PaymentComponents = comps, PayedTime=DateTime.Now };
db.Payments.Add(payment);
int recordsAffected = db.SaveChanges();
}
}
REFERENCE:
How do I get Entity Framework 4.3 Code First to map a subclass using Table Per Type (TPT)?
http://weblogs.asp.net/manavi/archive/2011/04/24/associations-in-ef-4-1-code-first-part-4-table-splitting.aspx
http://www.robbagby.com/entity-framework/entity-framework-modeling-entity-splitting/
Entity Framework Mapping Scenarios - http://msdn.microsoft.com/en-us/library/cc716779.aspx
http://blogs.microsoft.co.il/blogs/gilf/archive/2009/03/06/entity-splitting-in-entity-framework.aspx
In your Context class in OnModelCreating:
modelBuilder.Entity<GiftCouponPayment>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("GiftCouponPayment");
});
modelBuilder.Entity<ClubCardPayment>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("ClubCardPayment");
});