I´m new in entity framework. I´m using EF6 with VS2013.
I need to implement a database from "code-first" approach.
The problem is that i´m using an abstract class and i can´t understand why the next pice of code doesn´t work:
public abstract class AClaseT1
{
//private int idT1;
[Key]
public int idT1
{ get; set; }
}
public class ClaseT1Dev1:AClaseT1
{
//private int dataT1;
public int dataT1
{ get; set; }
}
public class Contexto : DbContext
{
public DbSet<AClaseT1> tipos1 { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Contexto, Configuration>());
using(var db = new Contexto())
{
Console.WriteLine("Int for Tipo1:");
int d = Console.Read();
ClaseT1Dev1 t1 = new ClaseT1Dev1() { dataT1 = d };
db.tipos1.Add(t1);
db.SaveChanges();
ClaseT1Dev1 query = (ClaseT1Dev1)from t in db.tipos1
select t;
Console.WriteLine("Int obtenido de Tipo1 metido en BDD: {0}", query.idT1);
Console.Read();
}
}
When i run the app, it breaks at the "query" line, because it can´t make the casting... Is there a way to make it works?
I need a table feeded with different derived objects from the base abstract class (i think it is a TPC inheritance approach).
Thanks for your help!!!
EDIT:
The exact exception thrown at the "query" line is:
InvalidCastException was unhandled
An unhandled exception of type 'System.InvalidCastException' occurred in PruebaCodeFirstDB.exe
Additional information: It is not possible to convert an object of type 'System.Data.Entity.Infrastructure.DbQuery`1[PruebaCodeFirstDB.AClaseT1]' to type 'PruebaCodeFirstDB.ClaseT1Dev1'.
from t in db.tipos1 select t will give you an IEnumerable. You need to change it so that it just returns the one you want
Related
I'm working on programmatically establishing a connection to PostgresSQL using Entity Framework 6. I have this class:
public class ClearspanDatabaseContext : DbContext
with this constructor:
public ClearspanDatabaseContext()
: base(buildConnectionString())
{
}
Here's the static method that makes the connection string programmatically:
private static string buildConnectionString()
{
RegisterDbProvider("Npgsql", ".Net Framework Data Provider for Postgresql", "Npgsql Data Provider", "Npgsql.NpgsqlFactory, Npgsql");
EntityConnectionStringBuilder entityConnectionStringBuilder = new EntityConnectionStringBuilder();
entityConnectionStringBuilder.Provider = "Npgsql";
entityConnectionStringBuilder.ProviderConnectionString = "host=192.168.168.140;Port=5432;username=ClearspanDevLogin;password=*******;database=ClearspanWebServerDev";
return entityConnectionStringBuilder.ToString();
}
And here's the method that registers Npgsql as a database provider, taken from this source:
public static bool RegisterDbProvider(string invariant, string description, string name, string type)
{
try
{
DataSet ds = ConfigurationManager.GetSection("system.data") as DataSet;
foreach (DataRow row in ds.Tables[0].Rows)
{
if (row["InvariantName"].ToString() == invariant)
{
return true;
}
}
ds.Tables[0].Rows.Add(name, description, invariant, type);
return true;
}
catch
{
}
return false;
}
This generates a string like this:
"provider=Npgsql;provider connection string=\"host=192.168.168.140;Port=5432;username=ClearspanDevLogin;password=********;database=ClearspanWebServerDev\""
But I get an ArgumentException:
Keyword not supported: 'provider'.
I think I am close to the programmatic connection, but am missing something small. What can I do to resolve this exception and properly setup this connection programmatically? No app.config answers, I'm working in a class library, which ignores app.config (see the comments of the accepted answer to this question). This program must remain this way because it is used as a plugin - it does not (nor should it) run on its own. Thanks in advance.
Ok, here is working example for you which I verified is working.
Using dummy code-first EF 6 model + custom DbConfiguration class:
public class Enrollment {
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
}
[DbConfigurationType(typeof (NpgsqlConfiguration))]
public class SchoolContext : DbContext {
public SchoolContext(string cs) : base(cs) {
}
public DbSet<Enrollment> Enrollments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
}
}
class NpgsqlConfiguration : System.Data.Entity.DbConfiguration
{
public NpgsqlConfiguration()
{
SetProviderServices("Npgsql", Npgsql.NpgsqlServices.Instance);
SetProviderFactory("Npgsql", Npgsql.NpgsqlFactory.Instance);
SetDefaultConnectionFactory(new Npgsql.NpgsqlConnectionFactory());
}
}
Then, instead of your buildConnectionString(), just pass postgre connection string in constructor:
using (var ctx = new SchoolContext("host=192.168.168.40;port=5432;...")) {
Console.WriteLine(ctx.Enrollments.ToArray());
}
And that is all. Config file is completely empty during that, and it works.
Have you looked at Code-Based Configuration? Create a DbConfiguration class with a public parameterless constructor in the same assembly as your DbContext
class MyConfiguration : System.Data.Entity.DbConfiguration
{
public MyConfiguration()
{
SetProviderServices("Npgsql", Npgsql.NpgsqlServices.Instance);
SetProviderFactory("Npgsql", Npgsql.NpgsqlFactory.Instance);
}
}
Now I think the DbContext should use that provider factory by default, and you can construct the DbContext with just the connection string. But if it's in a different assembly, then you have a bit more work to do, but that can be found in the link above.
A potential problem with the above solution is that any configuration in the config file will take precedence, so maybe it would be safer to use the option described in here:
var conn = DbProviderFactories.GetFactory("MY_CONN_PROVIDER").CreateConnection();
conn.ConnectionString = "MY_CONN_STR";
new DbContext(conn, true);
where your provider is "Npgsql", which was registered in RegisterDbProvider above.
Also see https://msdn.microsoft.com/en-us/library/dd0w4a2z(v=vs.110).aspx
I use EF 5 with the code first approach. Now I try to define a "code" table in which I want to have several different codes (like address code, medium code, etc.). In this table I just have the following properties: ID (Guid), Name (String), Description (String) and a discriminator (in this case something like the type of the code: address code, medium code, etc.).
So I defined the following base class:
public abstract class Code : EntityBase
{
public string Name { get; set; }
public string Beschreibung { get; set; }
}
Then I derived two classes from code
public class AddressCode : Code {}
public class MediumCode : Code {}
The class EntityBase is abstract and just defines the Id property, we use it for every POCO class...
The goal is that I can use AddressCode as a property on my address POCO class:
public class Adresse : EntityBase
{
#region Properties
public string Name1 { get; set; }
public virtual AddressCode AddressCode { get; set; }
#endregion
}
The question now is, how can I explain EF how to do that? Anyone can help?
Thanks
Marco
Thanks for your answer!
I tried to do it like you said. Unfortunately I get an error because of my EntityBase class:
public abstract class EntityBase
{
#region Properties
public virtual Guid Id { get; set; }
public virtual bool IsValid
{
get
{
{
return Validate();
}
}
}
[NotMappedAttribute]
public virtual IList<ValidationFailure> ValidationFailures { get; set; }
#endregion
#region Methods
private bool Validate()
{
var validatorFactory = new AttributedValidatorFactory();
IValidator validator = validatorFactory.GetValidator(GetType());
if (validator == null)
{
return true;
}
ValidationResult validationResult = validator.Validate(this);
ValidationFailures = validationResult.Errors;
return validationResult.IsValid;
}
#endregion
}
The error message is:
You cannot use Ignore method on the property 'ValidationFailures' on type 'Entities.AdresseCode' because this type inherits from the type 'Entities.EntityBase' where this property is mapped. To exclude this property from your model, use NotMappedAttribute or Ignore method on the base type.
As you can see I already defined the property ValidationFailures as NotMapped but still I get this error.. Do you have an idea?
Thanks
Marco
Just create a context (derived from DbContext)
public class AddressesDb : DbContext
{
public DbSet<Code> Codes { get; set; }
public DbSet<Adresse> Adressen { get; set; }
}
And (when used in code) EF will create a database with default table and column names. It will create a discriminator column of type text (nvarchar) which will contain the names of the classes that derive from Code.
If you want different names and/or types you should either use data annotations or fluent API to configure these.
Finally I got it work!
In the DBContext be aware to define DbSets for the code derived classes before all the other POCO's and then it works!
Using the ASP.NET WebApi 4 RC, is it possible to have an ApiController's return type be a base class and actually return instances of derived classes? Trying to do this now results in an internal server error (500) when returning xml. Returning json using this method works correctly.
public class Base
{
public int ID { get; set; }
}
public class Derived : Base
{
public string Message { get; set; }
}
public class ValuesController : ApiController
{
public IEnumerable<Base> Get()
{
return new Derived[] {
new Derived(){ Message="test"},
new Derived(){ Message="another"}
};
}
}
It would seem that the XML serialization is what's throwing the error but all I can see is the generic 500 error.
Yes, you need to use the knowntype serialization hint:
[System.Runtime.Serialization.KnownType(typeof(Derived))]
public class Base
{
public int ID { get; set; }
}
You might want to do it programmatically
private static Type[] GetKnownType()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var knownTypes = new List<Type>();
foreach (var assembly in assemblies)
{
knownTypes.AddRange(assembly.GetTypes().Where(x => x.BaseType == typeof (BaseResponse)).ToArray());
}
return knownTypes.ToArray();
}
Do remember your child class MUST have a default constructor else you will get runtime serialization error.
i have written a generic repository for my base windows which have a problem with.
lets be more specific, there is a little poco class called Unit as following:
public class Unit : BaseEntity
{
public string Name { get; set; }
private ICollection<Good> _goods;
public virtual ICollection<Good> Goods
{
get
{
if(_goods==null)
{
return new List<Good>();
}
return _goods;
}
set { _goods = value; }
}
}
which is inherited from a base entity class as :
public class BaseEntity
{
public int Id { get; set; }
public override string ToString()
{
return Id.ToString();
}
}
and this is my Add section of generic repository class:
public void Add(TEntity entity)
{
if (entity == null) return;
if (Context.Entry(entity).State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Set<TEntity>().Add(entity);
Context.SaveChanges();
}
before add a new record, max id is fetched from db and placed in IdTextBox and them add method of base form is called which calls aforementioned Add method of base repository. here is the problem, i get this error, "The property 'Id' is part of the object's key information and cannot be modified."
there is also a mapper class that maps every property to its corresponding control which does its job fine.
What is my problem?
Thanks in advance.
i figured out that this problem is occured because of auto detect changes enability which was true.
I've got the following requirement that works well in the OO space but I can't seem to get it to map back to the DB using ADO EF code first.
I have numrous products each will have different aspects (attributes but not in the sense of code attributes). For instance ring would have aspects such as mineral type = gold etc whilst a diamond would have an aspec of clarity = VVSI1.
As you can see the products very greatly in thier composition and I want a dynamic way of growing my system.
As such I've created a product class:
public class Product
{
public int id { get; set; }
public string Name { get; set; }
private List<ProductAspect> aspects = new List<ProductAspect>();
public List<ProductAspect> Aspects { get { return aspects; } set { aspects = value; } }
}
It has a list of ProductAspect which is the base class for all aspects moving forward:
public class ProductAspect
{
public int id { get; set; }
public string AspectName { get; set; }
}
I then inherit from the ProductAspect using a generic which alows me to be specific (strongly typed) about my Aspect Value:
public abstract class ProductAspect<T> : ProductAspect
{
public T AspectValue { get; set; }
}
I then create some Aspects that will allow me to decorate my product:
public class StringAspect : ProductAspect<string> { };
public class DecimalAspect : ProductAspect<decimal> { };
public class ImageAspect : ProductAspect<byte[]> { };
I then give the DbContext a try and have tried both TPH and TPC inheritance mappings.
Neither seem to work. The DB model that get's generated doesn't create a foriegn key to the StringAspect or DecimalAspect tables from the Aspect Table.
public class IxamDataContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductAspect> Aspects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
AspectMapping(modelBuilder);
}
private void AspectMapping(DbModelBuilder mb)
{
//TPH
//mb.Entity<ProductAspect>()
// .Map<StringAspect>(m => m.Requires("type").HasValue("sa"))
// .Map<DecimalAspect>(m => m.Requires("type").HasValue("da"));
//TPC
//mb.Entity<StringAspect>().ToTable("StringAspect");
//mb.Entity<DecimalAspect>().ToTable("DecimalAspect");
}
}
Resulting in the following exception for this Seeding code:
Product p = new Product();
p.Name = "Diamond";
p.Aspects.Add(new StringAspect() { AspectName = "History", AspectValue = "Old and long" });
p.Aspects.Add(new DecimalAspect() { AspectName = "Weight", AspectValue= 96.5M });
context.Products.Add(p);
context.SaveChanges();
Excpetion:
EntityType 'StringAspect' does not
exist in the EntitySet
'IxamDataContext.Aspects'. Parameter
name: entity
Any ideas from the EF code first pros out there?
Entity framework doesn't support intermediate non mapped types in inheritance hierarchy. It means that you can't have this inheritance: A (mapped) -> B (not mapped) -> C (mapped). EF also doesn't support mapping generic types. It means that you must remove your generic intermediate class from the hierarchy and move AspectValue to derived types with correct type.
Maybe it's to late, but I would offer you using ComplexType attribute it will allows you to extend your types as you wish.