0

I have three projects :

  1. App.Models: Contains object interfaces.
  2. App.WebAPIService: RESTFul API service using JSON.
  3. App.Client: WPF application that communicates with RESTful API Service.

I use Entity Framework to create interfaces and models.

App.Models is used in the other two projects to create objects.

public interface ISheet
{
    int ID { get; set; }
    string Name { get; set; }
    Nullable<System.DateTime> CreatedDate { get; set; }
    string CreatedUser { get; set; }

    ICollection<ICategory> Category { get; set; }
}

public interface ICategory
{
    int ID { get; set; }
    string Name { get; set; }
}

App.WebAPIService:

[DataContract(IsReference = true)]
[KnownType(typeof(Category))]
public partial class Sheet : ISheet
{
    #region Primitive Properties
    [DataMember]
    public int ID
    {
        get;
        set;
    }
    [DataMember]
    public Nullable<System.DateTime> CreatedDate
    {
        get;
        set;
    }
    [DataMember]
    public string CreatedUser
    {
        get;
        set;
    }
    [DataMember]
    public string Name
    {
        get;
        set;
    }
    #endregion
    #region Navigation Properties
    [DataMember]
    public ICollection<ICategory> Category
    {
        get
        {
            if (_category == null)
            {
                var newCollection = new FixupCollection<ICategory>();
                newCollection.CollectionChanged += FixupCategory;
                _category = newCollection;
            }
            return _category;
        }
        set
        {
            if (!ReferenceEquals(_category, value))
            {
                var previousValue = _category as FixupCollection<ICategory>;
                if (previousValue != null)
                {
                    previousValue.CollectionChanged -= FixupCategory;
                }
                _category = value;
                var newValue = value as FixupCollection<ICategory>;
                if (newValue != null)
                {
                    newValue.CollectionChanged += FixupCategory;
                }
            }
        }
    }
    private ICollection<ICategory> _category;

    #endregion
    #region Association Fixup
    private void FixupCategory(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (ICategory item in e.NewItems)
            {
                if (!item.Sheet.Contains(this))
                {
                    item.Sheet.Add(this);
                }
            }
        }

        if (e.OldItems != null)
        {
            foreach (ICategory item in e.OldItems)
            {
                if (item.Sheet.Contains(this))
                {
                    item.Sheet.Remove(this);
                }
            }
        }
    }

    #endregion
}

public class SheetController : ApiController
{
    private TORDBEntities db = new TORDBEntities();

    /// <summary>
    /// GET api/Sheet/5
    /// </summary>
    /// <param name="id">int</param>
    /// <returns>Sheet</returns>
    public Sheet GetSheet(int id)
    {
        Sheet sheet = db.Sheet.Include("Category").Single(s => s.ID == id);
        if (sheet == null)
        {
            throw new HttpResponseException
                (Request.CreateResponse(HttpStatusCode.NotFound));
        }

        return sheet;
    }
}

App.Client:

public class Sheet : ISheet
{
    public int ID { get; set; }
    public DateTime? CreatedDate { get; set; }
    public string CreatedUser { get; set; }
    public string Name { get; set; }

    public ICollection<ICategory> Category { get; set; }
}

class ServiceSheet
{
    public Sheet sheet = new Sheet();

    public Sheet GetSheet(int id)
    {
        string url = ConfigurationManager.AppSettings["UrlWebService"];
        url += @"api/sheet/" + id;
        HttpWebRequest requete = WebRequest.Create(url) as HttpWebRequest;
        requete.Method = WebRequestMethods.Http.Get;

        requete.BeginGetResponse(new AsyncCallback(this.GetSheetResponseCallback), 
            requete);

        return sheet;
    }

    private void GetSheetResponseCallback(IAsyncResult ar)
    {
        //Récupération de l'objet HttpWebRequest 
        HttpWebRequest requete = (HttpWebRequest)ar.AsyncState;

        try
        {
            using (HttpWebResponse reponse = requete.EndGetResponse(ar) 
                as HttpWebResponse)
            {
                using (StreamReader streamReader = new
                    StreamReader(reponse.GetResponseStream()))
                {
                    string Text = streamReader.ReadToEnd();
                    sheet = JsonConvert.DeserializeObject<Sheet>(Text);
                    //Newtonsoft.Json.JsonSerializer serializer = new 
                        Newtonsoft.Json.JsonSerializer();
                    //serializer.Converters.Add(new DTOJsonConverter());
                    //Sheet maSheet = serializer.Deserialize(streamReader);
                } 
            }
        }
        catch (WebException we)
        {
            if (we.Status == WebExceptionStatus.ProtocolError)
            {
                HttpWebResponse r = (HttpWebResponse)we.Response;
                if (r.StatusCode == HttpStatusCode.NotFound)

                    MessageBox.Show("Code d'erreur: " + r.StatusCode.ToString());
                r.Close();
            }
            else
                MessageBox.Show(we.Message + " " + we.Status.ToString());
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Erreur");
        }
    }
}

I get an error for deserialization category. I have tried the following:

public class Sheet : ISheet
{
    public int ID { get; set; }
    public DateTime? CreatedDate { get; set; }
    public string CreatedUser { get; set; }
    public string Name { get; set; }

    [JsonConverter(typeof(ConcreteTypeConverter<Category>))]
    public ICollection<ICategory> Category { get; set; }
}
public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        //assume we can convert to anything for now
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
    {
        //explicitly specify the concrete type we want to create
        return serializer.Deserialize<TConcrete>(reader);
    }

    public override void WriteJson(JsonWriter writer, object value, 
        JsonSerializer serializer)
    {
        //use the default serialization - it works fine
        serializer.Serialize(writer, value);
    }
}

Error:

Unable to cast object of type 'System.Collections.Generic.List1[App.Client.Models.Category]' to type 'System.Collections.Generic.ICollection1[App.Models.ICategory]'.

And this:

public override object ReadJson(JsonReader reader, Type objectType, 
    object existingValue, JsonSerializer serializer)
    {
        //explicitly specify the concrete type we want to create
        List<TConcrete> liste = serializer.Deserialize<List<TConcrete>>(reader);
        ICollection<TConcrete> coll = new Collection<TConcrete>();
        liste.ForEach(delegate(TConcrete obj)
        {
            coll.Add(obj);
        });

        return coll;
    }

Error:

Unable to cast object of type 'System.Collections.Generic.Collection1[App.Client.Models.Category]' to type 'System.Collections.Generic.ICollection1[App.Models.ICategory]'.

I think the problem is just ICollection; have you ever encountered this problem, or do you have a better solution?

Answer :

OK, it's my fault !

I forgotten to add this part of code client side :

public class Sheet : ISheet
{
    public int ID { get; set; }
    public string Name { get; set; }

    public ICollection<ICategory> Category
    {
        get
        {
            if (_category == null)
            {
                var newCollection = new FixupCollection<ICategory>();
                newCollection.CollectionChanged += FixupCategory;
                _category = newCollection;
            }
            return _category;
        }
        set
        {
            if (!ReferenceEquals(_category, value))
            {
                var previousValue = _category as FixupCollection<ICategory>;
                if (previousValue != null)
                {
                    previousValue.CollectionChanged -= FixupCategory;
                }
                _category = value;
                var newValue = value as FixupCollection<ICategory>;
                if (newValue != null)
                {
                    newValue.CollectionChanged += FixupCategory;
                }
            }
        }
    }
    private ICollection<ICategory> _category;


    private void FixupCategory(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (ICategory item in e.NewItems)
            {
                if (!item.Sheet.Contains(this))
                {
                    item.Sheet.Add(this);
                }
            }
        }

        if (e.OldItems != null)
        {
            foreach (ICategory item in e.OldItems)
            {
                if (item.Sheet.Contains(this))
                {
                    item.Sheet.Remove(this);
                }
            }
        }
    }
}

public class FixupCollection<T> : ObservableCollection<T>
{
    protected override void ClearItems()
    {
        new List<T>(this).ForEach(t => Remove(t));
    }

    protected override void InsertItem(int index, T item)
    {
        if (!this.Contains(item))
        {
            base.InsertItem(index, item);
        }
    }
}

Without FixupCategory, it couldn't work.

Hope, this will help some people. Thanks to those who helped me.

Amandine
  • 1
  • 1
  • 1
    Are you sure that `App.Client.Models.Category` implements `App.Models.ICategory`? – Artyom Neustroev Jun 18 '14 at 10:17
  • Yes, here is my Category class : – Amandine Jun 18 '14 at 11:12
  • class Category : ICategory { public int ID { get; set; } public string Name { get; set; } } – Amandine Jun 18 '14 at 11:13
  • Which .Net version you are on? Covariance conversions ('You can assign an instance of IEnumerable to a variable of type IEnumerable.', http://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx) available only starting from .Net 4 – Lanorkin Jun 18 '14 at 11:28
  • App.Models and App.WebAPIService are .Net4, App.Client is .Net3.5 – Amandine Jun 18 '14 at 11:32
  • Ok, so easiest option will be move to .Net 4 for App.Client; otherwise you'll have to create somehow ICollection<**I**Category> instead of ICollection; you'll need to rewrite `ConcreteTypeConverter` to understand which interface to ude for each particular TConcrete – Lanorkin Jun 18 '14 at 11:58
  • I can't use .Net4 for App.Client because some user have Windows Vista. – Amandine Jun 18 '14 at 12:03
  • What do you mean about ICollection<**I**Category> ? – Amandine Jun 18 '14 at 12:08
  • @Amandine That should be `ICollection instead of ICollection`, with I and T in bold; markup just won't made it. So basically, if you cannot use covariant conversions, then you need to create List of correct type (with interface instead of class), so it won't be casted. – Lanorkin Jun 19 '14 at 11:43

0 Answers0