Bishnu Rawal
Bishnu Rawal

Reputation: 1387

Cannot implicitly convert anonymous type #1[] to anonymous type #2[]

I have following scenario:

public JsonResult ChangeFilterList(int option)
{
    var data = new[] { new { Text = "Unknown option", Value = -1 } };
    switch (option)
    {
        case 2: data = _departmentnameRepository.All.Select(x => new { Text = x.DeptName, Value = x.Id }).ToArray();
            break;
        case 3: data = Session["projectid"] == null
                ? _assetSequenceRepository.All.Select(x => new { Text = x.AssetShotName, Value = x.Id }).ToArray()
                : _assetSequenceRepository.FindBy(p => p.ProjectId == (int)Session["projectid"]).Select(x => new { Text = x.AssetShotName, Value = x.Id }).ToArray();
            break;
        default: data = _userRepository.All.Select(x => new { Text = x.DisplayName, Value = x.UserID }).ToArray();
            break;
    }            

    return Json(data, JsonRequestBehavior.AllowGet);
}

case2 and default looks great but complains on case 3 (conditional) saying: Cannot implicitly convert type 'AnonymousType#1[]' to 'AnonymousType#2[]'. Shouldn't ?: be able to decide the type since i already have provided the blueprint for the anonymous as var data = new[] { new { Text = "Unknown option", Value = -1 } };.

Solution:

@Darin Dimitrov answer is great but i want to have some test with anonymous types (Simple cases always need it). As @Douglas suspects : My assetSequenceRepository is supplying id as long and anonymous Value goes in favor of int not long. since C# compiler does not implicitly cast long to int, i got the error. Compiling snippet is:

public JsonResult ChangeFilterList(int option = 3)
        {
            var data = new[] { new { Text = "Unknown option", Value = long.MaxValue } };
            switch (option)
            {
                case 2: data = _departmentnameRepository.All.Select(x => new { Text = x.DeptName, Value = (long)x.Id }).ToArray();
                    break;
                case 3: data = Session["projectid"] == null
                        ? _assetSequenceRepository.All.Select(x => new { Text = x.AssetShotName, Value = x.Id }).ToArray()
                        : _assetSequenceRepository.FindBy(p => p.ProjectId == (int)Session["projectid"]).Select(x => new { Text = x.AssetShotName, Value = x.Id }).ToArray();
                    break;
                default: data = _userRepository.All.Select(x => new { Text = x.DisplayName, Value = (long)x.UserID }).ToArray();
                    break;
            }            

            return Json(data, JsonRequestBehavior.AllowGet);
        }

Upvotes: 3

Views: 8778

Answers (3)

Douglas
Douglas

Reputation: 54917

My guess is that your FindBy method returns objects whose properties have different types from the ones you're expecting (e.g. int? instead of int). Try specifying a type cast to ensure that your anonymous type has the correct definition:

case 3: data = Session["projectid"] == null
             ? _assetSequenceRepository.All.Select(x => new { Text = x.AssetShotName, Value = x.Id }).ToArray()
             : _assetSequenceRepository.FindBy(p => p.ProjectId == (int)Session["projectid"]).Select(x => new { Text = (string)x.AssetShotName, Value = (int)x.Id }).ToArray();
        break;

The key change is:

new { Text = (string)x.AssetShotName, Value = (int)x.Id })
                  ↖    explicit type casts    ↗

Upvotes: 4

MarcinJuraszek
MarcinJuraszek

Reputation: 125660

Following test code compiles just fine:

public void Test(int option, string parameter)
{
    var data = new[] { new { Text = "Unknown option", Value = -1 } };

    switch(option)
    {
        case 2:
            data = Enumerable.Range(1, 4)
                             .Select(x => new { Text = x.ToString(), Value = x })
                             .ToArray();
            break;
        case 3:
            data = (new Random()).Next(2) % 2 == 1
                ? Enumerable.Range(1, 6)
                            .Select(x => new { Text = x.ToString(), Value = x })
                            .ToArray()
                : Enumerable.Range(1, 2)
                            .Select(x => new { Text = x.ToString(), Value = x })
                            .ToArray();
            break;
        default:
            data = Enumerable.Range(1, 3)
                             .Select(x => new { Text = x.ToString(), Value = x })
                             .ToArray();
            break;
    }
}

I only changed your repository calls to Enumerable.Range() and Select lambda to get proper string/int properties values.

I would guess, that you've shown use not exactly the code you're trying to compile. And in your real code you have one property with non-matching name (e.g. incorrect casing) or incorrect type.

You can try to figure it out by hovering your mouse pointer over ToArray() call. There will be a is new { string Text, int Value } printed on the tooltip.

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1039438

You are putting the compiler to a real test here. Just write a view model to end its suffering and make things more explicit:

public class MyViewModel
{
    public int Value { get; set; }
    public string Text { get; set; }
}

and then project your LINQ queries to this view model to avoid any possible ambiguity that might arise from the use of the conditional operator and anonymous types:

public ActionResult ChangeFilterList(int option)
{
    var data = new[] 
    { 
        new MyViewModel { Text = "Unknown option", Value = -1 } 
    };

    switch (option)
    {
        case 2: data = _departmentnameRepository
            .All
            .Select(x => new MyViewModel { Text = x.DeptName, Value = x.Id })
            .ToArray();
            break;
        case 3: data = Session["projectid"] == null
                ? _assetSequenceRepository
                    .All
                    .Select(x => new MyViewModel { Text = x.AssetShotName, Value = x.Id })
                    .ToArray()
                : _assetSequenceRepository
                    .FindBy(p => p.ProjectId == (int)Session["projectid"])
                    .Select(x => new MyViewModel { Text = x.AssetShotName, Value = x.Id })
                    .ToArray();
            break;
        default: data = _userRepository
            .All
            .Select(x => new MyViewModel { Text = x.DisplayName, Value = x.UserID })
            .ToArray();
            break;
    }            

    return Json(data, JsonRequestBehavior.AllowGet);
}

Upvotes: 5

Related Questions