Cast exception when i am trying to insert an entity in Entity Framework (using code-f

Hello,

I get an cast exception when i am trying to insert an entity in Entity Framework (using code-first).

From this code :

public virtual T Insert(T entity)
{
return Context.Set<T>().Add(entity);
}

The cast exception is like “impossible to cast …Collection’1(Entity) to type (Entity)”

I can’t figure out why. I am pretty sure ive done everything right.

Post entity

public class Post
{
public long PostId { get; private set; }
public DateTime date { get; set; }
[Required]
public string Subject { get; set; }
public User User { get; set; }
public Category Category { get; set; }
[Required]
public string Body { get; set; }

    public virtual ICollection&lt;Tag&gt; Tags { get; private set; }

    public Post()
    {
        Category = new Category();
        if (Tags == null) 
            Tags = new Collection&lt;Tag&gt;();
    }

    public void AttachTag(string name, User user)
    {
        if (Tags.Count(x =&gt; x.Name == name) == 0)
            Tags.Add(new Tag { 
                Name = name, 
                User = user 
            });
        else
            throw new Exception("Tag with specified name is already attached to this post.");
    }

    public Tag DeleteTag(string name)
    {
        Tag tag = Tags.Single(x =&gt; x.Name == name);
        Tags.Remove(tag);

        return tag;
    }

    public bool HasTags()
    {
        return (Tags.Count &gt; 0);
    }
}

Tag entity

public class Tag { public long TagId { get; private set; } public string Name { get; set; }

// Qui a ajouté le tag ?
public User User { get; set; }

}

Mapping

public class PostMap: EntityTypeConfiguration<Post>
{
public PostMap()
{
ToTable(“Posts”);
HasKey(x => x.PostId);
Property(x => x.Subject)
.HasColumnType(“varchar”)
.HasMaxLength(256)
.IsRequired();
Property(x => x.Body)
.HasColumnType(“text”)
.IsRequired();
HasMany(x => x.Tags);
HasOptional(x => x.Tags);
}
}

class TagMap : EntityTypeConfiguration<Tag>
{
public TagMap()
{
ToTable(“Tags”);
HasKey(x => x.TagId);
Property(x => x.Name)
.HasColumnType(“varchar”)
.HasMaxLength(256)
.IsRequired();
HasRequired(x => x.User);

}
}

Thanks a lots.

You are returning the collection, not the entity. Fix your method signature or send back the entity directly.

Well i haven’t intended to sent a collection, i had no control on this. Entity Framework tried to insert a collection (and i dunno why).

I found solution.

I dunno what is causing this. But it wasn’t my repository or the entities the problem. It was definitivly the mapping.

This fellowing mapping was wrong …

public PostMap()
{
ToTable(“Posts”);
HasKey(x => x.PostId);
Property(x => x.Subject)
.HasColumnType(“varchar”)
.HasMaxLength(256)
.IsRequired();
Property(x => x.Body)
.HasColumnType(“text”)
.IsRequired();
HasMany(x => x.Tags);
HasOptional(x => x.Tags);
}

HasMany(x => x.Tags);
HasOptional(x => x.Tags);

should be

HasMany(x => x.Tags).WithOptional();
and HasOption must be removed.

Geez, this is the replacement to SQL? EF is far harder then it has to be. EF, join TDD and DDD as the dumbest ideas ever. T-SQL owns this over-complicated, unnecessary crap.

With all due respect Patriot, that isn’t exactly a fair statement to make.

The reasons this kind of abstraction was developed is so that domain objects can become persistence ignorant, and can be mapped to any storage mechanism you want, which isn’t limited to an sql database.

If one was to know for absolute certain that a given application will always use a t-sql enabled storage medium, then I might agree, and just use sql (I have on several occasions). Obviously he’s entertaining the thought that his app might not always use a database, in which case sql is meaningless.

I will agree with you on one issue though. EF’s mapping implementation is not real great. It is far too generic, especially in the conventions area, making it harder to author custom conventions (such as how to handle many to many relationships).

I agree with Serenarules. Well its not because you think this is crap that anyone think so. I mean everyone got his techniques and methods and starting a debate won’t do anything else than wasting time.

I understand EF is not mature enough yet but still an option to consider and i am actually like it a lots.

Personally, I would run with fluent nhibernate over the EF stuff at the moment.

That said, I think turning your back on DDD or ORMs is kind of like putting your head in the sand.

Back to the problem at hand – I think the following should work:


public virtual T Insert(T entity)
{
   Context.Set<T>().Add(entity);
   return entity;
}

I’m guessing Add() returns the collection here for some reason.

Your right (Edited because i didnt’t read correctly your sentence). Ive actually put lots of research time into DDD and i have to say this is a very good way to develop a system sure its not intuitive but sometime intuition isnt good it just make things harder on the developement side. You just need to be prepared. Also its not a requirement to use an ORM. I could if i want make an SQL persistance system that attach to my project anytime. About Webforms (which i use for like 1 year) its cool for simple systems but when you get into complex stuff this is no more for me.

About the ORM part, i am more familiar to EF4 and ive heard stories where it go really well. Its not mature yet because there are part of it that need to be more developped. But it do his job so far. The real reason i am not going with hibernate is the timeline. I am on a new project and decided to bring some new technologies on the side to help us.

Who care if one way is better than the other… the goal is to like what were doing. If someone think SQL is the way to go then go otherwise you should be open to others options.

Anyway that just an opinion.

About the problem now… well i still think the behaviour was strange of returning a collection. This may be the mapping that work this way if badly written.

Rushino,

I’ll address your code sample, and go over a few tips, as well as point out some of the flaws for future considerations toward using NHibernate.

First, most persistence engines today use proxy classes to work with your entities. Entity Framework is no exception. While basic operations do not require all properties to be marked virtual, in order to take better advantage of change-tracking, you should do this.

Second, the DbSet class exposes a method called Create<T>() so you should incorporate this into your base repository in order to provide a built-in factory method.

Third, exposing a collection as anything but IEnumerable opens the possibility for arbitrary Add’s and Remove’s. Unfortunately, EF-CTP cannot yet map public properties to private members. It is impossible to hold the collection internally, exposing it as IEnumerable only. NHibernate can. For now, you can help secure this a bit by using ISet<T> and HashSet<T>. This disallows adding the same element more than once.

Fourth, the way in which you are adding tags is a bit detrimental to your system. Consider the two operations: post1.AddTag(“coding”) and post2.AddTag(“coding”). If two posts declare a tag with the same name, the result wil be two records in the database with the same value. This is because you are using a OneToMany relationship. You might want to consider using a ManyToMany relationship so that existing tags can be reused without bloating the table.

Fifth, with the way that EF works, using AddThis() AddThat() methods is little overkill, since we cannot explicitly deny direct Add() and Remove() on the collections. I know this is a good DDD practice, but with EF it’s pointless.

Sixth, drop the use of the [Required] attribute. If you really need to guarentee a value in a given field, make it protected set and add a setter method to the entity. This was you can protect the field exactly as you wish, including throwing custom exceptions.

Your classes and mapping should probably look more like the following. I have tested these and they work. For simplicities sake, I left out the User entity:

Category.cs

public class Category
{
 
    // key property
    public virtual long CategoryId { get; protected set; }
 
    // scalar properties
    public virtual string Name { get; set; }
 
}

Post.cs

public class Post
{
 
    // key property
    public virtual long PostId { get; protected set; }
 
    // scalar properties
    public virtual DateTime PostedOn { get; set; }
    public virtual string Subject { get; set; }
    public virtual string Body { get; set; }
 
    // navigational properties
    public virtual Category Category { get; set; }
    public virtual ISet<Tag> Tags { get; set; }
 
    // class constructors
    public Post() { Tags = new HashSet<Tag>(); }
 
}

Tag.cs

public class Tag
{

    // key property
    public virtual long TagId { get; protected set; }

    // scalar properties
    public virtual string Name { get; set; }
 
    // navigational properties
    public virtual ISet<Post> Posts { get; set; }
 
    // class constructors
    public Tag() { Posts = new HashSet<Post>(); }
 
}

CategoryMap.cs

public class CategoryMap : EntityTypeConfiguration<Category>
{
 
    public CategoryMap()
    {
 
        // map key property
        HasKey(category => category.CategoryId);
 
        // map scalars properties
        Property(category => category.Name)
            .HasColumnType("varchar")
            .HasMaxLength(256)
            .IsRequired();
 
    }
 
}

PostMap.cs

public class PostMap : EntityTypeConfiguration<Post>
{
 
    public PostMap()
    {
 
        // map key property
        HasKey(post => post.PostId);
 
        // map scalars properties
        Property(post => post.PostedOn)
            .IsRequired();
        Property(post => post.Subject)
            .HasColumnType("varchar")
            .HasMaxLength(256)
            .IsRequired();
        Property(post => post.Body)
            .HasColumnType("text")
            .IsRequired();
 
        // map navigational properties
        HasRequired(post => post.Category);
        HasMany(post => post.Tags)
            .WithMany(tag => tag.Posts);
 
    }
 
}

TagMap.cs

public class TagMap : EntityTypeConfiguration<Tag>
{
 
    public TagMap()
    {
 
        // map key property
        HasKey(tag => tag.TagId);
 
        // map scalars properties
        Property(tag => tag.Name)
            .HasColumnType("varchar")
            .HasMaxLength(256)
            .IsRequired();
 
        // map navigational properties
        HasMany(tag => tag.Posts)
            .WithMany(post => post.Tags);
 
    }
 
}

DomainContext.cs

public class DomainContext : DbContext
{
 
    public DbSet<Category> Categories { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }
 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CategoryMap());
        modelBuilder.Configurations.Add(new PostMap());
        modelBuilder.Configurations.Add(new TagMap());
        base.OnModelCreating(modelBuilder);
    }
 
}

P.S. Using the above, the following will, in fact work.

public T Insert(T entity)
{
return context.Set<T>().Add(entity);
}