Using interfaces in DbContext and DbSets for similar databases - c#

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));

Related

C# CodeFirst Enum EntityFramework

I'm trying to use codefirstdatabase in my applicantion, but I'm nothing having success in mapping the enum in my application.
I'm receiving that error when I try to use it.
(6,10) : error 3032: Problem in mapping fragments starting at line 6:Condition member 'BasicAccount.AccountType' with a condition other than 'IsNull=False' is mapped. Either remove the condition on BasicAccount.AccountType or remove it from the mapping.
public class PersonalDate
{
public int id { get; set; }
public ICollection<BaseAccount> BaseAccounts{ get; set; }
public PersonalDate()
{ }
}
public class BasicAccount
{
public int id { get; set; }
public AccountType AccountType { get; set; }
public PersonalDate PersonalDate { get; set }
public BasicAccount()
{
}
}
public class CurrentAccount: BasicAccount
{
public CurrentAccount()
{
AccountType = AccountType.CurrentAccount;
}
}
public class SavingAccount: BasicAccount
{
public SavingAccount()
{
AccountType = AccountType.SavingAccount;
}
}
public class SalaryAccount: BasicAccount
{
public SalaryAccount()
{
AccountType = AccountType.SalaryAccount;
}
}
public enum AccountType: int
{
undefined= 0,
SavingAccount= 1,
CurrentAccount= 2,
SalaryAccount= 3
}
public class BancoContext : DbContext
{
public DbSet<DadoPessoa> DadosPessoas { get; set; }
public DbSet<ContaBase> ContaBases { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BasicAccount>()
.HasRequired<PersonalDate>(x => x.PersonalDate);
modelBuilder.Entity<BasicAccount>()
.Map<BasicAccount>( x => x.Requires("AccountType").HasValue((int)AccountType.Undefined))
.Map<SavingAccount>(x => .Requires("AccountType").HasValue((int)AccountType.SavingAccount))
.Map<CurrentAccount>(x => x.Requires("AccountType").HasValue((int)AccountType.CurrentAccount))
.Map<SalaryAccount>(x => x.Requires("AccountType").HasValue((int)AccountType.SalaryAccount));
}

Entity Framework 6 - Removed default discriminator column for inherited abstract class - error 3032: Problem in mapping

Error:
error 3032: Problem in mapping fragments starting at line: Condition member '' with a condition other than 'IsNull=False' is mapped. Either remove the condition on or remove it from the mapping.
Code:
public enum MyEnum
{
Value1, Value2
}
public class MyBaseClass
{
public MyEnum MyEnum { get; protected set; }
}
public class DerivedOne: MyBaseClass
{
public DerivedOne()
{
MyEnum = MyEnum.Value1;
}
public string MyDerivedOneString { get; set; }
}
public class DerivedTwo: MyBaseClass
{
public DerivedTwo()
{
MyEnum = MyEnum.Value2;
}
}
public class MyDbContext : DbContext
{
DbSet<MyBaseClass> MyBaseClass { get; set; }
DbSet<DerivedOne> DerivedOne { get; set; }
DbSet<DerivedTwo> DerivedTwo { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyBaseClass>()
.Map<DerivedOne>(x => x.Requires("MyEnum").HasValue((int)MyEnum.Value1))
.Map<DerivedTwo>(x => x.Requires("MyEnum").HasValue((int)MyEnum.Value2));
}
}
static void Main(string[] args)
{
var db = new MyDbContext();
var derivedOne = new DerivedOne();
derivedOne.MyDerivedOneString = "test";
db.DerivedOne.Add(derivedOne);
//Exception
db.SaveChanges();
}
https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-inheritance-with-the-entity-framework-in-an-asp-net-mvc-application
Solved it like this, it was a table-per-hierarchy (TPH) inheritance error:
Classes not shown are not edited.
public abstract class MyBaseClass
{
[NotMapped]
public MyEnum MyEnum { get; protected set; }
}
public class MyDbContext : DbContext
{
DbSet<MyBaseClass> MyBaseClass { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyBaseClass>()
.Map<DerivedOne>(x => x.Requires("MyEnum").HasValue((int)MyEnum.Value1))
.Map<DerivedTwo>(x => x.Requires("MyEnum").HasValue((int)MyEnum.Value2));
}
}
static void Main(string[] args)
{
var db = new MyDbContext();
var derivedOne = new DerivedOne();
derivedOne.MyDerivedOneString = "test";
db.MyBaseClass.Add(derivedOne);
db.SaveChanges();
var test = db.MyBaseClass.OfType<DerivedOne>().FirstOrDefault();
}

Code first approach to store System.Drawing.PointF in Sqlite DB

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;
}
}

Filter incorrectly being applied to child relationship

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");
}
}
}

Entity Framework: Split table into multiple tables

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");
});

Categories