2

I am working on ASP.NET MVC project with EF6 with Database First. I am trying to use Redis server to cache frequently used objects.

But i am getting problem in saving related entities (parent-child). For example following Author and Author_Book classes are parent-child and referencing to each other (Foreign-Key constraint in RDBMS)

public partial class Author
{
    public int Id { get; set; }
    public string Name { get; set; }  
    public virtual ICollection<Author_Book> Author_Book { get; set; }
}

public partial class Author_Book
{
    public int Id { get; set; }
    public int AuthorId { get; set; }
    public string Title { get; set; }
    public virtual Author Author { get; set; }
}

public partial class Customer
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

Querying and trying to store query result to Redis server as below

using (EFTestContext db = new EFTestContext())
{
    var data = db.Authors.ToList();
    redisClient.Set<List<Author>>("author", data);
}

Above line redisClient.Set.. resulting following Exception

An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

However if i store Customer entity (which doesn't have child entities) into Redis server it work fine

using (EFTestContext db = new EFTestContext())
{
    var customers = db.Customers.ToList();
    redisClient.Set<List<Customer>>("customers", customers);
}

So my question is how to store complete entity (with childs) into redis server?

4
  • Try db.Configuration.ProxyCreationEnabled = false; Commented Feb 27, 2014 at 20:06
  • Could it be a problem with an Author having a collection of Author_Book and each Author_Book also referencing the Author? I don´t know how that redis client works, but it could be trying to serialize the object graph and then hitting the stack overflow exception. Commented Feb 27, 2014 at 21:02
  • @Daniel i also think that it could be due to that circular reference. But it is generated from EF database-first. If i remove circular reference, EF starts complaining. Commented Feb 28, 2014 at 3:02
  • possible duplicate of Preventing StackOverflowException while serializing EF object graph into Json Commented Feb 28, 2014 at 3:32

1 Answer 1

1

I followed [DataContract] / [DataMember] approach (As @oferzelig mentioned in comment). It is working fine and no longer raising that exception. I am describing it here so it could help someone else.

EF Database-first by default does not add [DataContract] / [DataMember] attributes, we need to modify T4 template for it. I did following modifications in Model template Model1.tt.

Added [DataContract] attribute before the line

<#=codeStringGenerator.EntityClassOpening(entity)#>

now it looks like

[DataContract]
<#=codeStringGenerator.EntityClassOpening(entity)#>

Added [DataMember] attribute before line

<#=codeStringGenerator.Property(edmProperty)#>

and it looks like

[DataMember]
<#=codeStringGenerator.Property(edmProperty)#>

We also need to generate [DataMember] attribute for one-to-many relationships (e.g. public virtual ICollection<Author_Book> Author_Book { get; set; } in question) but NOT for one-to-one (e.g. public virtual Author Author { get; set; } in question). To achieve it i added a new function in CodeStringGenerator class

public string NavigationProperty_NeedDataMember(NavigationProperty navProp)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0}",
        navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("[DataMember]") : ""
        );
}

and called it just before the line

<#=codeStringGenerator.NavigationProperty(navigationProperty)#>

as below

<#=codeStringGenerator.NavigationProperty_NeedDataMember(navigationProperty)#>
<#=codeStringGenerator.NavigationProperty(navigationProperty)#>

And finally modified UsingDirectives procedure to add System.Runtime.Serialization, as below

public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
    return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
        ? string.Format(
            CultureInfo.InvariantCulture,
            "{0}using System;{1}" +
            "{2}" +
            "{3}",
            inHeader ? Environment.NewLine : "",
            includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
            includeCollections ? (Environment.NewLine + "using System.Runtime.Serialization;") : "",
            inHeader ? "" : Environment.NewLine)
        : "";
}

That all.

Now it is generating following classes and i no need to edit classes manually after each update.

[DataContract]
public partial class Author
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public virtual ICollection<Author_Book> Author_Book { get; set; }
}

[DataContract]
public partial class Author_Book
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public int AuthorId { get; set; }
    [DataMember]
    public string Title { get; set; }


    public virtual Author Author { get; set; }
}

Hope it will help someone else.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.