Yanıtlar:
İç özelliklere sahip anonim türler, bence kötü bir .NET çerçeve tasarımı kararıdır.
İşte anonim nesneyi hemen bir ExpandoObject'e dönüştürerek bu sorunu çözmek için hızlı ve güzel bir uzantı .
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
Kullanımı çok kolay :
return View("ViewName", someLinq.Select(new { x=1, y=2}.ToExpando());
Tabii ki sizce:
@foreach (var item in Model) {
<div>x = @item.x, y = @item.y</div>
}
Cevabı ilgili bir soruda buldum . Cevap David Ebbo'nun blog yazısında belirtildi MVC görünümlerine anonim nesneler geçirme ve onlara dinamik olarak erişme
Bunun nedeni, anonim türün denetleyicide dahili olarak geçirilmesidir, bu nedenle yalnızca bildirildiği derlemeden erişilebilir. Görünümler ayrı olarak derlendiğinden, dinamik bağlayıcı bu montaj sınırını aşamayacağından şikayet eder.
Ancak düşünürseniz, dinamik bağlayıcıdan kaynaklanan bu kısıtlama aslında oldukça yapaydır, çünkü özel yansımayı kullanırsanız, hiçbir şey sizi bu iç üyelere erişmenizi engellemez (evet, Orta güvende bile çalışır). Dolayısıyla, varsayılan dinamik bağlayıcı, CLR çalışma zamanının izin verdiği şeyi yapmanıza izin vermek yerine, C # derleme kurallarını (dahili üyelere erişemediğiniz yerlerde) uygulamak için kendi yolundan gidiyor.
ToExpando yöntemini kullanmak en iyi çözümdür.
System.Web derlemesi gerektirmeyen sürüm :
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject))
{
var obj = propertyDescriptor.GetValue(anonymousObject);
expando.Add(propertyDescriptor.Name, obj);
}
return (ExpandoObject)expando;
}
Anonim türden bir model oluşturmak ve anonim nesneyi buna benzer bir şekilde dönüştürmeye çalışmak yerine ExpandoObject
...
var model = new
{
Profile = profile,
Foo = foo
};
return View(model.ToExpando()); // not a framework method (see other answers)
ExpandoObject
Doğrudan doğrudan oluşturabilirsiniz :
dynamic model = new ExpandoObject();
model.Profile = profile;
model.Foo = foo;
return View(model);
Ardından görünümünüzde model türünü dinamik olarak ayarlarsınız @model dynamic
ve özelliklere doğrudan erişebilirsiniz:
@Model.Profile.Name
@Model.Foo
Normalde çoğu görünüm için kuvvetle yazılan görünüm modellerini öneririm, ancak bazen bu esneklik kullanışlıdır.
Anonim bir türü bir arabirime sarmak için framework doğaçlama arabirimini kullanabilirsiniz.
Sadece dönmek IEnumerable<IMadeUpInterface>
ve Linq sonunda .AllActLike<IMadeUpInterface>();
bu anonim türü bildirilen derleme bağlamıyla DLR kullanarak anonim özelliği çağırır çünkü bu işleri kullanın .
Bir konsol uygulaması yazdı ve referans olarak Mono.Cecil'i ekleyin (şimdi NuGet'ten ekleyebilirsiniz ), ardından kod parçasını yazın:
static void Main(string[] args)
{
var asmFile = args[0];
Console.WriteLine("Making anonymous types public for '{0}'.", asmFile);
var asmDef = AssemblyDefinition.ReadAssembly(asmFile, new ReaderParameters
{
ReadSymbols = true
});
var anonymousTypes = asmDef.Modules
.SelectMany(m => m.Types)
.Where(t => t.Name.Contains("<>f__AnonymousType"));
foreach (var type in anonymousTypes)
{
type.IsPublic = true;
}
asmDef.Write(asmFile, new WriterParameters
{
WriteSymbols = true
});
}
Yukarıdaki kod, derleme dosyasını giriş argümanlarından alır ve erişilebilirliği içten herkese değiştirmek için Mono.Cecil kullanır ve bu da sorunu çözer.
Programı web sitesinin Post Build etkinliğinde çalıştırabiliriz. Çince ile ilgili bir blog yazısı yazdım ama sadece kodu ve anlık görüntüleri okuyabileceğine inanıyorum. :)
Kabul edilen cevaba dayanarak, genel olarak ve sahne arkasında çalışmasını sağlamak için kontrolörde geçersiz kıldım.
İşte kod:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
//This is needed to allow the anonymous type as they are intenal to the assembly, while razor compiles .cshtml files into a seperate assembly
if (ViewData != null && ViewData.Model != null && ViewData.Model.GetType().IsNotPublic)
{
try
{
IDictionary<string, object> expando = new ExpandoObject();
(new RouteValueDictionary(ViewData.Model)).ToList().ForEach(item => expando.Add(item));
ViewData.Model = expando;
}
catch
{
throw new Exception("The model provided is not 'public' and therefore not avaialable to the view, and there was no way of handing it over");
}
}
}
Şimdi anonim bir nesneyi model olarak geçirebilirsiniz ve beklendiği gibi çalışacaktır.
Https://stackoverflow.com/a/7478600/37055 adresinden biraz çalmak yapacağım
Dynamitey paketini yüklerseniz bunu yapabilirsiniz:
return View(Build<ExpandoObject>.NewObject(RatingName: name, Comment: comment));
Ve köylüler sevinirler.
RuntimeBinderException nedeni tetiklendi, diğer yazılarda iyi bir cevap olduğunu düşünüyorum. Sadece nasıl çalıştığını açıklamaya odaklanıyorum.
ASP.NET MVC'de Anonim tür koleksiyonuyla answer @DotNetWise ve Bağlama görünümlerine bakın ,
İlk olarak, uzantı için statik bir sınıf oluşturun
public static class impFunctions
{
//converting the anonymous object into an ExpandoObject
public static ExpandoObject ToExpando(this object anonymousObject)
{
//IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
}
Denetleyicide
public ActionResult VisitCount()
{
dynamic Visitor = db.Visitors
.GroupBy(p => p.NRIC)
.Select(g => new { nric = g.Key, count = g.Count()})
.OrderByDescending(g => g.count)
.AsEnumerable() //important to convert to Enumerable
.Select(c => c.ToExpando()); //convert to ExpandoObject
return View(Visitor);
}
Görünümde, model IEnumerable (dinamik, model sınıfı değil), anonim tip nesnesini bağlayacağımız için bu çok önemlidir.
@model IEnumerable<dynamic>
@*@foreach (dynamic item in Model)*@
@foreach (var item in Model)
{
<div>x=@item.nric, y=@item.count</div>
}
Foreach yazın, ben var veya dinamik kullanarak hiçbir hata var .
Bu arada, yeni alanlarla eşleşen yeni bir ViewModel oluşturmak da sonucu görünüme geçirmenin yolu olabilir.
Şimdi yinelemeli lezzette
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
new RouteValueDictionary(obj).ForEach(o => expandoObject.Add(o.Key, o.Value == null || new[]
{
typeof (Enum),
typeof (String),
typeof (Char),
typeof (Guid),
typeof (Boolean),
typeof (Byte),
typeof (Int16),
typeof (Int32),
typeof (Int64),
typeof (Single),
typeof (Double),
typeof (Decimal),
typeof (SByte),
typeof (UInt16),
typeof (UInt32),
typeof (UInt64),
typeof (DateTime),
typeof (DateTimeOffset),
typeof (TimeSpan),
}.Any(oo => oo.IsInstanceOfType(o.Value))
? o.Value
: o.Value.ToExpando()));
return (ExpandoObject) expandoObject;
}
ExpandoObject Uzantısını kullanmak iç içe anonim nesneler kullanılırken çalışır ancak kesilir.
Gibi
var projectInfo = new {
Id = proj.Id,
UserName = user.Name
};
var workitem = WorkBL.Get(id);
return View(new
{
Project = projectInfo,
WorkItem = workitem
}.ToExpando());
Bunu başarmak için kullanıyorum.
public static class RazorDynamicExtension
{
/// <summary>
/// Dynamic object that we'll utilize to return anonymous type parameters in Views
/// </summary>
public class RazorDynamicObject : DynamicObject
{
internal object Model { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name.ToUpper() == "ANONVALUE")
{
result = Model;
return true;
}
else
{
PropertyInfo propInfo = Model.GetType().GetProperty(binder.Name);
if (propInfo == null)
{
throw new InvalidOperationException(binder.Name);
}
object returnObject = propInfo.GetValue(Model, null);
Type modelType = returnObject.GetType();
if (modelType != null
&& !modelType.IsPublic
&& modelType.BaseType == typeof(Object)
&& modelType.DeclaringType == null)
{
result = new RazorDynamicObject() { Model = returnObject };
}
else
{
result = returnObject;
}
return true;
}
}
}
public static RazorDynamicObject ToRazorDynamic(this object anonymousObject)
{
return new RazorDynamicObject() { Model = anonymousObject };
}
}
Denetleyicideki kullanım, ToExpando () yerine ToRazorDynamic () kullanmanız dışında aynıdır.
Anonim nesnenin tamamını almak için görünümünüzde sonuna ".AnonValue" ekleyin.
var project = @(Html.Raw(JsonConvert.SerializeObject(Model.Project.AnonValue)));
var projectName = @Model.Project.Name;
ExpandoObject denedim ama böyle bir iç içe anonim karmaşık türü ile işe yaramadı:
var model = new { value = 1, child = new { value = 2 } };
Benim çözümüm bir JObject to View modelini döndürmekti:
return View(JObject.FromObject(model));
ve .cshtml dosyasında dinamiğe dönüştürün:
@using Newtonsoft.Json.Linq;
@model JObject
@{
dynamic model = (dynamic)Model;
}
<span>Value of child is: @model.child.value</span>