سؤال

أود أن تولد التالية select حيوي باستخدام أشجار التعبير:

var v = from c in Countries
        where c.City == "London"
        select new {c.Name, c.Population};

لقد عملت في كيفية توليد

var v = from c in Countries
        where c.City == "London"
        select new {c.Name};

ولكن أنا لا يمكن أن يبدو للعثور على منشئ/الزائد من شأنها السماح لي بتحديد خصائص متعددة في تحديد امدا.

هل كانت مفيدة؟

المحلول

ويمكن أن يتم ذلك، كما ذكرنا، مع مساعدة من انعكاس تنبعث منها وفئة المساعد لقد تضمنت أدناه. رمز أدناه هو التقدم في العمل، لذلك أعتبر لما يستحق ... "يعمل على بلدي مربع. يجب أن يلقى الطبقة طريقة SelectDynamic في فئة طريقة تمديد ثابتة.

وكما هو متوقع، فلن تحصل على أي التحسس لأنه لا يتم إنشاء نوع حتى وقت التشغيل. يعمل جيدا على عناصر تحكم البيانات منضم المتأخر.

public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<string> fieldNames)
{
    Dictionary<string, PropertyInfo> sourceProperties = fieldNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name));
    Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);

    ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t");
    IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();

    Expression selector = Expression.Lambda(Expression.MemberInit(
        Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

    return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType },
                 Expression.Constant(source), selector));
}



public static class LinqRuntimeTypeBuilder
{
    private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    private static AssemblyName assemblyName = new AssemblyName() { Name = "DynamicLinqTypes" };
    private static ModuleBuilder moduleBuilder = null;
    private static Dictionary<string, Type> builtTypes = new Dictionary<string, Type>();

    static LinqRuntimeTypeBuilder()
    {
        moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name);
    }

    private static string GetTypeKey(Dictionary<string, Type> fields)
    {
        //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter
        string key = string.Empty;
        foreach (var field in fields)
            key += field.Key + ";" + field.Value.Name + ";";

        return key;
    }

    public static Type GetDynamicType(Dictionary<string, Type> fields)
    {
        if (null == fields)
            throw new ArgumentNullException("fields");
        if (0 == fields.Count)
            throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition");

        try
        {
            Monitor.Enter(builtTypes);
            string className = GetTypeKey(fields);

            if (builtTypes.ContainsKey(className))
                return builtTypes[className];

            TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

            foreach (var field in fields)                    
                typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public);

            builtTypes[className] = typeBuilder.CreateType();

            return builtTypes[className];
        }
        catch (Exception ex)
        {
            log.Error(ex);
        }
        finally
        {
            Monitor.Exit(builtTypes);
        }

        return null;
    }


    private static string GetTypeKey(IEnumerable<PropertyInfo> fields)
    {
        return GetTypeKey(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }

    public static Type GetDynamicType(IEnumerable<PropertyInfo> fields)
    {
        return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }
}

نصائح أخرى

والجواب قبلت أمر مفيد للغاية، ولكن كنت بحاجة إلى شيء أقرب قليلا إلى نوع مجهول الحقيقي.

وهناك نوع مجهول، فقد كانت له خصائص، منشئ لملء كافة القيم، وتنفيذ يساوي / GetHashCode لمقارنة قيم كل الممتلكات، وToString تنفيذ يتضمن اسم / قيمة كل الممتلكات للقراءة فقط . (راجع https://msdn.microsoft.com/en-us/library/bb397696 .aspx اتصال للحصول على وصف كامل لأنواع مجهولة المصدر).

وبناء على هذا التعريف من فئات غير معروفة وأنا وضعت الطبقة التي تولد أنواع مجهولة الحيوية على جيثب على الموقع https://github.com/dotlattice/LatticeUtils/blob/master/LatticeUtils/AnonymousTypeUtils.cs . ويتضمن المشروع أيضا بعض الاختبارات وحدة للتأكد من أنواع مجهولة وهمية تتصرف وكأنها حقيقية.

وفيما يلي مثال بسيط جدا لكيفية استخدامه:

AnonymousTypeUtils.CreateObject(new Dictionary<string, object>
{
    { "a", 1 },
    { "b", 2 }
});

وأيضا، ملاحظة أخرى: لقد وجدت أنه عند استخدام نوع مجهول الديناميكي مع الكيان الإطار، يجب استدعاء المنشئ مع "أعضاء" مجموعة المعلمة. على سبيل المثال:

Expression.New(
    constructor: anonymousType.GetConstructors().Single(), 
    arguments: propertyExpressions,
    members: anonymousType.GetProperties().Cast<MemberInfo>().ToArray()
); 

إذا كنت تستخدم أحد إصدارات Expression.New لا يتضمن "الأعضاء" المعلمة، فإن إطار كيان لا يعترف بأنه منشئ من نوع مجهول. لذلك أفترض أن يعني أن التعبير منشئ نوع مجهول الحقيقي لتشمل أن "أعضاء" من المعلومات.

هل يمكن استخدام IQueryable إمتداد هنا، وهو implemantation من الحل وصفها "إيثان J. براون":

https://github.com/thiscode/DynamicSelectExtensions

وملحق يبني حيوي نوع مجهول.

وبعد ذلك يمكنك القيام بذلك:

var YourDynamicListOfFields = new List<string>(

    "field1",
    "field2",
    [...]

)
var query = query.SelectPartially(YourDynamicListOfFields);

وفي وقت متأخر ربما بعض الشيء ولكن قد تساعد على شخص ما.

ويمكنك توليد دينامية الاختيار عن طريق DynamicSelectGenerator الدعوة في اختيار من كيان.

public static Func<T, T> DynamicSelectGenerator<T>()
            {
                // get Properties of the T
                var fields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray();

            // input parameter "o"
            var xParameter = Expression.Parameter(typeof(T), "o");

            // new statement "new Data()"
            var xNew = Expression.New(typeof(T));

            // create initializers
            var bindings = fields.Select(o => o.Trim())
                .Select(o =>
                {

                    // property "Field1"
                    var mi = typeof(T).GetProperty(o);

                    // original value "o.Field1"
                    var xOriginal = Expression.Property(xParameter, mi);

                    // set value "Field1 = o.Field1"
                    return Expression.Bind(mi, xOriginal);
                }
            );

            // initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }"
            var xInit = Expression.MemberInit(xNew, bindings);

            // expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }"
            var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter);

            // compile to Func<Data, Data>
            return lambda.Compile();
        }

واستخدام هذا الرمز:

var result = dbContextInstancs.EntityClass.Select(DynamicSelectGenerator<EntityClass>());

أنا لا أعتقد أنك سوف تكون قادرة على تحقيق ذلك.على الرغم من أن عندما تفعل select new { c.Name, c.Population } يبدو أنك لا خلق فئة أنت عليه في الواقع.إذا كان لديك نظرة على تجميع الناتج في عاكس أو الخام IL سوف تكون قادرا على رؤية هذا.

سيكون لديك الدرجة التي سوف ننظر بشيء من هذا القبيل:

[CompilerGenerated]
private class <>c__Class {
  public string Name { get; set; }
  public int Population { get; set; }
}

(حسنا, لقد نظفت لمسة منذ الخاصية هي حقا مجرد get_Name() و set_Name(name) طريقة تعيين على أي حال)

ما كنت تحاول القيام به هو الصحيح الديناميكية فئة الخلق ، وهو ما لن يكون متاحا حتى .NET 4.0 يخرج (وحتى ذلك الحين أنا لست متأكدا مما إذا كان سوف تكون قادرة على تحقيق ما تريد).

أنت أفضل حل هو تعريف مختلف مجهول الطبقات ثم يكون نوعا من التحقق المنطقي لتحديد واحد الذي خلق ، خلق ذلك يمكنك استخدام كائن System.Linq.Expressions.NewExpression.

ولكن قد يكون (من الناحية النظرية على الأقل) يمكن أن تفعل ذلك إذا كنت الحصول على من الصعب حقا النواة حول الكامنة LINQ مزود.إذا كنت هي الكتابة الخاصة بك LINQ مزود يمكنك الكشف عن إذا كان حاليا-تحليل التعبير هو تحديد ، ثم تحديد CompilerGenerated فئة تعبر عن منشئ وخلق.

بتحد ليست مهمة بسيطة ، ولكن سيكون كيف LINQ to SQL, LINQ to XML, الخ تفعل كل ذلك.

هل يمكن استخدام فئة المعلمة بدلا من العمل مع نوع مجهول. في المثال الخاص بك يمكنك إنشاء فئة المعلمة مثل هذا:

public struct ParamClass {
    public string Name { get; set; };
    public int Population { get; set; };
}

... ووضعها الخاص بك حدد مثل هذا:

var v = from c in Countries
        where c.City == "London"
        select new ParamClass {c.Name, c.Population};

وما تحصل عليه من شيء من نوع IQueryable<ParamClass>.

وهذا يجمع، أنا دونو ما اذا كان يعمل ولكن ...

myEnumerable.Select((p) => { return new { Name = p.Name, Description = p.Description }; });

وإذا افترضنا ص هو ما المحولة الخاصة بك، وحدد البيان تعود نوع حالا، وذلك باستخدام تعريف الدالة لامدا ل.

وتحرير: أنا أيضا لا أعرف كيف كنت من شأنه أن يولد هذا بشكل حيوي. ولكن على الأقل فإنه يظهر لك كيفية استخدام حدد امدا لإرجاع نوع حالا مع قيم متعددة

وEdit2:

وأنت سيكون أيضا لالعارية في الاعتبار، أن ج # البرمجي في الواقع الطبقات ثابتة من نوع حالا. حتى نوع حالا لديه في الواقع نوع بعد وقت الترجمة. حتى إذا كان لديك توليد هذه الاستعلامات في وقت التشغيل (والتي أفترض أنك) قد تكون لديكم لبناء نوع باستخدام أساليب التفكير المختلفة (أعتقد أنك يمكن استخدامها لجعل أنواع على الطاير) تحميل أنواع التي تم إنشاؤها في سياق التنفيذ و استخدامها في الإخراج الذي تم إنشاؤه.

أعتقد أن معظم الأشياء التي أجبت - كما Slace قال: كنت بحاجة إلى بعض الفئة التي ستكون عاد من Select الأسلوب.مرة واحدة لديك الدرجة ، يمكنك استخدام System.Linq.Expressions.NewExpression طريقة لخلق التعبير.

إذا كنت تريد حقا أن تفعل هذا, يمكنك توليد الفئة في وقت التشغيل أيضا.انها أكثر قليلا من العمل ، لأنه لا يمكن أن يتم ذلك باستخدام LINQ أشجار التعبير, لكنه ممكن.يمكنك استخدام System.Reflection.Emit مساحة الاسم إلى فعل ذلك - أنا فقط عملت بحث سريع و هنا مقال يوضح هذا:

هل يمكن استخدام API التعبير الحيوي الذي يسمح لك لبناء حدد بيانكم مثل هذه حيوي:

 Select("new(<property1>,<property2>,...)");

وتحتاج ملف Dynamics.cs من العينات LINQ واللغة لبرنامج Visual Studio لهذا العمل، وترتبط سواء في الجزء السفلي من <لأ href = "http://weblogs.asp.net/rajbk/archive/ 2007/09/18 / أساس ديناميكية سلسلة-الاستعلامات في linq.aspx "يختلط =" نوفولو noreferrer "> هذه الصفحة . يمكنك أيضا رؤية مثال عمل يظهر في هذا العمل على في URL نفسه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top