2011-11-24 15 views
6

Tôi đang cố gắng để tạo ra một Html.ActionLink với viewmodel sau:tài sản trên mô hình Serialize IList khi truyền vào Html.ActionLink

public class SearchModel 
{ 
    public string KeyWords {get;set;} 
    public IList<string> Categories {get;set;} 
} 

Để tạo liên kết của tôi, tôi sử dụng các cuộc gọi sau đây:

@Html.ActionLink("Index", "Search", Model) 

đâu Model là một thể hiện của các SearchModel

các liên kết được tạo ra là một cái gì đó như thế này:

http://www.test.com/search/index?keywords=bla&categories=System.Collections.Generic.List

Vì rõ ràng là chỉ gọi phương thức ToString trên mọi thuộc tính.

Những gì tôi muốn thấy tạo ra là thế này:

http://www.test.com/search/index?keywords=bla&categories=Cat1&categories=Cat2

Có cách nào tôi có thể đạt được điều này bằng cách sử dụng Html.ActionLink

+0

Có vẻ như một bản sao: http://stackoverflow.com/q/1752721/25727. Xem câu trả lời đầu tiên cho một giải pháp với một HtmlHelper tùy chỉnh. – Jan

Trả lời

2

Trong MVC 3, bạn không may mắn vì các giá trị tuyến đường được lưu trữ trong một RouteValueDictionary như tên gọi ngụ ý sử dụng Dictionary nội bộ khiến không thể có nhiều giá trị được liên kết với một khóa duy nhất. Giá trị tuyến đường có thể được lưu trữ trong một NameValueCollection để hỗ trợ hành vi tương tự như chuỗi truy vấn.

Tuy nhiên, nếu bạn có thể áp đặt một số hạn chế về tên loại và bạn có thể hỗ trợ một chuỗi truy vấn trong các định dạng:

http://www.test.com/search/index?keywords=bla&categories=Cat1|Cat2 

sau đó bạn lý thuyết có thể cắm nó vào Html.ActionLink từ MVC sử dụng TypeDescriptor mà lần lượt có thể mở rộng khi chạy. Đoạn mã sau đây được trình bày để chứng minh nó có thể, nhưng tôi sẽ không khuyên bạn nên sử dụng nó, ít nhất là không cần tái cấu trúc thêm.

Có nói rằng, bạn sẽ cần phải bắt đầu bằng cách kết hợp một nhà cung cấp mô tả kiểu tùy chỉnh:

[TypeDescriptionProvider(typeof(SearchModelTypeDescriptionProvider))] 
public class SearchModel 
{ 
    public string KeyWords { get; set; } 
    public IList<string> Categories { get; set; } 
} 

Việc thực hiện cho nhà cung cấp và các mô tả tùy chỉnh đó sẽ ghi đè các mô tả tài sản cho Categories tài sản:

class SearchModelTypeDescriptionProvider : TypeDescriptionProvider 
{ 
    public override ICustomTypeDescriptor GetTypeDescriptor(
     Type objectType, object instance) 
    { 
     var searchModel = instance as SearchModel; 
     if (searchModel != null) 
     { 
      var properties = new List<PropertyDescriptor>(); 

      properties.Add(TypeDescriptor.CreateProperty(
       objectType, "KeyWords", typeof(string))); 
      properties.Add(new ListPropertyDescriptor("Categories")); 

      return new SearchModelTypeDescriptor(properties.ToArray()); 
     } 
     return base.GetTypeDescriptor(objectType, instance); 
    } 
} 
class SearchModelTypeDescriptor : CustomTypeDescriptor 
{ 
    public SearchModelTypeDescriptor(PropertyDescriptor[] properties) 
    { 
     this.Properties = properties; 
    } 
    public PropertyDescriptor[] Properties { get; set; } 
    public override PropertyDescriptorCollection GetProperties() 
    { 
     return new PropertyDescriptorCollection(this.Properties); 
    } 
} 

Sau đó, chúng tôi sẽ cần bộ mô tả thuộc tính tùy chỉnh để có thể trả lại giá trị tùy chỉnh trong GetValue được gọi nội bộ theo MVC:

class ListPropertyDescriptor : PropertyDescriptor 
{ 
    public ListPropertyDescriptor(string name) 
     : base(name, new Attribute[] { }) { } 

    public override bool CanResetValue(object component) 
    { 
     return false; 
    } 
    public override Type ComponentType 
    { 
     get { throw new NotImplementedException(); } 
    } 
    public override object GetValue(object component) 
    { 
     var property = component.GetType().GetProperty(this.Name); 
     var list = (IList<string>)property.GetValue(component, null); 
     return string.Join("|", list); 
    } 
    public override bool IsReadOnly { get { return false; } } 
    public override Type PropertyType 
    { 
     get { throw new NotImplementedException(); } 
    } 
    public override void ResetValue(object component) { } 
    public override void SetValue(object component, object value) { } 
    public override bool ShouldSerializeValue(object component) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Và cuối cùng để chứng minh rằng nó hoạt động một ứng dụng mẫu bắt chước MVC giá trị con đường sáng tạo:

static void Main(string[] args) 
{ 
    var model = new SearchModel { KeyWords = "overengineering" }; 

    model.Categories = new List<string> { "1", "2", "3" }; 

    var properties = TypeDescriptor.GetProperties(model); 

    var dictionary = new Dictionary<string, object>(); 
    foreach (PropertyDescriptor p in properties) 
    { 
     dictionary.Add(p.Name, p.GetValue(model)); 
    } 

    // Prints: KeyWords, Categories 
    Console.WriteLine(string.Join(", ", dictionary.Keys)); 
    // Prints: overengineering, 1|2|3 
    Console.WriteLine(string.Join(", ", dictionary.Values)); 
} 

Chết tiệt, đây có lẽ là câu trả lời dài nhất mà tôi từng đưa ra ở đây tại SO.

+0

Cảm ơn câu trả lời này. Nó được khá gần với nó để nhổ ra các giá trị đúng, nó chỉ là nếu bạn sử dụng | như delimiter bạn phải sử dụng một chất kết dính mô hình tùy chỉnh trên đường trở lại, vì vậy tôi đã thay đổi phiên bản của tôi để delimiter là string.Format ("& {0}", property.Name) ... hoạt động như một sự quyến rũ! – lomaxx

+2

từ "overengineering" thực sự có vị trí của nó ở đây :) đây là giải pháp phức tạp ridiculously cho một cái gì đó solvable với một dòng LINQ :) nhưng bằng chứng tốt đẹp của khái niệm .. – rouen

+0

@rouen nó không overengineering và nó không thực sự solvable bởi LINQ. Sự tham gia của chuỗi là, nhưng đó không phải là toàn bộ vấn đề, nó chỉ là một phần của vấn đề. Một phần khác là làm cho nó được sử dụng như một công dân hạng nhất trong mô hình định tuyến. Đó là những gì Joao đang cố gắng chứng minh – lomaxx

Các vấn đề liên quan