链接到类/对象的通用方法

时间:2015-02-06 15:59:17

标签: c# generics lambda

我想编写一个通用扩展方法来链接两个类/对象

类:

public class Model1
{
    public Guid Model2ID { get; set; }
    public Model2 Model2 { get; set; }

    // .. other insignificant properties to this question
}

public class Model2
{
    public Guid ID { get; set; }
}

我正在尝试编写一个类似于:

的方法
public static class ObjectExtensions
{
    public static Void LinkTo<T,U>(
        this T m1,
        IEnumerable<U> m2,
        m1p // expression to select what property on m1 is populated
        Action<bool,T,IEnumerable<U>> expression)
    {
        // not sure about this part at all

        m1p = u2.FirstOrDefault(expression);
    }
}

用法:

var listOfModel2 = //....

Model1.LinkTo(listOfModel2, m => m.Model2, (m1,m2) m1.Model2Id == m2.ID);

3 个答案:

答案 0 :(得分:1)

正如我们在聊天中所讨论的,我推荐使用轻量级的EF Context(带反射)。这完全是自定义和动态的,您只需在模型上使用KeyAttributeForeignKeyAttribute,并将模型添加到此自定义Context。我只连接了Add,其余的你应该能够自己解决。

类:

public class Staff
{
    [Key]
    public Guid Id { get; set; }

    [ForeignKey("Contact")]
    public Guid ContactId { get; set; }
    public Contact Contact { get; set; }
}

public class Contact
{
    [Key]
    public Guid Id { get; set; }

    public string Name { get; set; }

    [ForeignKey("Dog")]
    public Guid DogId { get; set; }
    public Dog Dog { get; set; }
}

public class Dog
{
    [Key]
    public Guid Id { get; set; }
}

上下文:

public class Context
{
    //Add as many of these as you want.  Don't forget to make public properties for them!
    private ObservableCollection<Staff> _staffs = new ObservableCollection<Staff>();
    private ObservableCollection<Contact> _contacts = new ObservableCollection<Contact>();
    private ObservableCollection<Dog> _dogs = new ObservableCollection<Dog>();


    private List<IForeignKeyRelation> _relations = new List<IForeignKeyRelation>();

    public Context()
    {
        var observables = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
            .ToList();

        foreach(var observable in observables)
        {
            var notifyCollection = observable.GetValue(this) as INotifyCollectionChanged;
            if (notifyCollection != null)
            {
                notifyCollection.CollectionChanged += CollectionChanged;
                Type principalType = observable.FieldType.GetGenericArguments()[0];

                var relations = principalType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .ToList()
                    .Where(p => p.GetCustomAttribute(typeof(ForeignKeyAttribute)) as ForeignKeyAttribute != null)
                    .Select(p => new { PrincipalForeignKeyInfo = p, ForeignKey = p.GetCustomAttribute(typeof(ForeignKeyAttribute)) as ForeignKeyAttribute })
                    .Where(p => principalType.GetProperty(p.ForeignKey.Name) != null)
                    .Select(p => {
                        var principalForeignKeyInfo = p.PrincipalForeignKeyInfo;
                        var principalRelationInfo = principalType.GetProperty(p.ForeignKey.Name);
                        var dependantType = principalRelationInfo.PropertyType;
                        var dependantKeyProperties = dependantType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                            .ToList()
                            .Where(dp => dp.GetCustomAttribute(typeof(KeyAttribute)) as KeyAttribute != null)
                            .ToList();
                        var dependantKeyInfo = dependantKeyProperties.FirstOrDefault();

                        var isValid = (dependantKeyInfo != null)
                            // Don't allow INT to GUID comparisons
                            // Keys need to be of same type;
                            && (principalForeignKeyInfo.PropertyType == dependantKeyInfo.PropertyType);


                        return new {
                            IsValid = isValid,
                            PrincipalRelationInfo = principalRelationInfo,
                            DependantType = dependantType,
                            PrincipalCollection = observable.GetValue(this),
                            PrincipalForeignKeyInfo = principalForeignKeyInfo,
                            DependantKeyInfo =  dependantKeyInfo                            
                        };
                    })
                    .Where(r => r.IsValid)
                    .Select(r =>
                    {           
                        var relationType = typeof(ForeignKeyRelation<,>).MakeGenericType(principalType, r.DependantType);
                        var relation = Activator.CreateInstance(relationType) as IForeignKeyRelation;
                        relation.GetType().GetProperty("PrincipalCollection").SetValue(relation, observable.GetValue(this));
                        relation.DependantKeyInfo = r.DependantKeyInfo;
                        relation.PrincipalForeignKeyInfo = r.PrincipalForeignKeyInfo;
                        relation.PrincipalRelationInfo = r.PrincipalRelationInfo;

                        return relation;
                    })
                    .ToList();

                _relations.AddRange(relations);

            }
        }
    }

    // Makes storing Generic types easy when the
    // Generic type doesn't exist;
    private interface IForeignKeyRelation
    {
        PropertyInfo PrincipalForeignKeyInfo { get; set; } 
        PropertyInfo PrincipalRelationInfo { get; set; }
        PropertyInfo DependantKeyInfo { get; set; }
        void Add<T>(T value);
    }

    // Class to hold reflected values
    // Reflection 
    private class ForeignKeyRelation<P,D> : IForeignKeyRelation
    {
        // Staff.ContactId
        public PropertyInfo PrincipalForeignKeyInfo { get; set; } 
        public Collection<P> PrincipalCollection { get; set; }
        // Staff.Contact
        public PropertyInfo PrincipalRelationInfo { get; set; }
        // Contact.Id
        public PropertyInfo DependantKeyInfo { get; set; }

        public void Add<T>(T value)
        {
            if (value.GetType() == typeof(D))
            {
                var dependantKey = DependantKeyInfo.GetValue(value);

                var principals = PrincipalCollection.Where(p => this.PrincipalForeignKeyInfo.GetValue(p).Equals(dependantKey))
                    .ToList();

                foreach(var principal in principals)
                {
                    PrincipalRelationInfo.SetValue(principal, value);
                }
            }
        }
    }

    private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs args) 
    {
        switch (args.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach(var relation in this._relations)
                {
                    foreach(var item in args.NewItems)
                    {
                        relation.Add(item);
                    }
                }
                break;
            case NotifyCollectionChangedAction.Move:
                break;
            case NotifyCollectionChangedAction.Remove:
                break;
            case NotifyCollectionChangedAction.Replace:
                break;
            case NotifyCollectionChangedAction.Reset:
                break;
            default:
                throw new NotImplementedException(args.Action.ToString());
        }
    }

    public IList<Staff> Staffs
    {
        get
        {
            return _staffs;
        }
    }
    public IList<Contact> Contacts
    {
        get
        {
            return _contacts;
        }
    }
    public IList<Dog> Dogs
    {
        get
        {
            return _dogs;
        }
    }
}

简单示例程序:

public static void Main()
{
    var context = new Context();
    var staff = new Staff() { Id = Guid.NewGuid() };

    var contact = new Contact();
    contact.Id = Guid.NewGuid();
    contact.Name = "Hello DotFiddle!";

    staff.ContactId = contact.Id;

    context.Staffs.Add(staff);

    Console.WriteLine("staff contact is null: " + (staff.Contact == null).ToString());

    context.Contacts.Add(contact);

    Console.WriteLine("staff contact is null: " + (staff.Contact == null).ToString());

    Console.WriteLine("Staff.Contact.Name: "+ staff.Contact.Name);
}

结果:

  

员工联系人为空:是

     

员工联系人为空:错误

     

Staff.Contact.Name:Hello DotFiddle!

This Entire Example on DotNetFiddle.net

答案 1 :(得分:1)

我不确切知道你的目的是什么,但其中一个完成了它:最后两个是'iffy',你可以看到所有的if语句。简单地说,编译器无法确保它们能够正常工作,因为您可以轻松传递错误的propertyExpression。

class Program
{
    static void Main(string[] args)
    {
        var guids = Enumerable.Range(0, 10).Select(i => Guid.NewGuid()).ToList();
        var m2s = guids.Select(g => new Model2 { ID = g }).ToList();
        var model1 = new Model1 { Model2ID = m2s[4].ID };
        model1.LinkTo(m2s, (m1, m2) => m1.Model2 = m2, (m1, m2) => m2.ID == m1.Model2ID);

        var model1a = new Model1 { Model2ID = m2s[4].ID };
        model1a.LinkTo(m2s, m1 => m1.Model2, m1 => m1.Model2ID, m2 => m2.ID);

        var model1b = new Model1 { Model2ID = m2s[4].ID };
        model1b.LinkTo(m2s, m1 => m1.Model2, (m1, m2) => m1.Model2ID == m2.ID);
    }
}

public static class ObjectExtensions
{
    public static void LinkTo<T, U>(this T m1, IEnumerable<U> m2s, Action<T, U> action, Func<T, U, bool> filter)
    {
        if (m2s.Any(m2 => filter(m1, m2)))
        {
            var x = m2s.First(m2 => filter(m1, m2));
            action(m1, x);
        }
    }

    public static void LinkTo<T, U>(this T m1, IEnumerable<U> m2s, Expression<Func<T, U>> propertyExpression, Func<T, U, bool> filter)
    {
        var results = m2s.Where(m2 => filter(m1, m2));

        if (!results.Any())
            return;

        var x = results.FirstOrDefault();

        if (x != null)
        {
            var me = (propertyExpression.Body as MemberExpression);
            if (me != null)
            {
                var pi = me.Member as PropertyInfo;
                if (pi != null)
                {
                    var setter = pi.GetSetMethod();
                    if (setter != null)
                    {
                        setter.Invoke(m1, new object[] { x });
                    }
                }
            }
        }
    }

    public static void LinkTo<T, U, Key>(this T m1, IEnumerable<U> m2s, Expression<Func<T, U>> propertyExpression, Func<T, Key> tKey, Func<U, Key> uKey)
    {
        var results = Enumerable.Repeat(m1, 1)
            .Join(m2s, tKey, uKey, (t, u) => u);

        if(!results.Any())
            return;

        var x = results
            .FirstOrDefault();

        if (x != null)
        {
            var me = (propertyExpression.Body as MemberExpression);
            if (me != null)
            {
                var pi = me.Member as PropertyInfo;
                if (pi != null)
                {
                    var setter = pi.GetSetMethod();
                    if (setter != null)
                    {
                        setter.Invoke(m1, new object[] { x });
                    }
                }
            }
        }
    }
}

答案 2 :(得分:0)

您应该阅读有关扩展方法的内容。它们被称为“来自”对象。 例如,您可以编写如下的通用扩展方法

class Program
{
    static void Main(string[] args)
    {
        var listOfModel2 = new Model1();

        //You can call it from "object"
        listOfModel2.MyExtensionMethod();

        //Or directly as it is declared
        ObjectExtensions.MyExtensionMethod(listOfModel2);
    }
}

public static class ObjectExtensions
{
    public static void MyExtensionMethod<T>(this T t)
    {
        //Do somthing
    }
}