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

C# 链接到类/对象的通用方法,c#,generics,lambda,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; } } publ

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

课程:

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 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 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);
    }
}

正如我们在聊天中所讨论的,我推荐一个轻量级的EF上下文(带有反射)。这是完全定制和动态的,您只需在模型上使用
KeyAttribute
ForeignKeyAttribute
,并将模型添加到您的This custom
上下文中即可。我只是连接了Add,其余的你应该可以自己解决

课程:

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 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;
        }
    }
}
结果:

员工联系人为空:True

员工联系人为空:False

Staff.Contact.Name:你好,Dotfidle


我不知道你到底想干什么,但其中一个已经做到了:从所有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 });
                    }
                }
            }
        }
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var guids=Enumerable.Range(0,10).Select(i=>Guid.NewGuid()).ToList();
var m2s=guids.Select(g=>newmodel2{ID=g}).ToList();
var model1=newmodel1{Model2ID=m2s[4].ID};
model1.LinkTo(m2s,(m1,m2)=>m1.Model2=m2,(m1,m2)=>m2.ID==m1.Model2ID);
var model1a=newmodel1{Model2ID=m2s[4].ID};
model1a.LinkTo(m2s,m1=>m1.Model2,m1=>m1.Model2ID,m2=>m2.ID);
var model1b=newmodel1{Model2ID=m2s[4].ID};
model1b.LinkTo(m2s,m1=>m1.Model2,(m1,m2)=>m1.Model2ID==m2.ID);
}
}
公共静态类对象扩展
{
公共静态无效链接到(此T m1、IEnumerable m2s、Action Action、Func筛选器)
{
if(m2s.Any(m2=>过滤器(m1,m2)))
{
变量x=m2s。首先(m2=>过滤器(m1,m2));
作用(m1,x);
}
}
公共静态void链接到(此T m1、IEnumerable m2s、表达式属性Expression、Func筛选器)
{
var结果=m2s,其中(m2=>过滤器(m1,m2));
如果(!results.Any())
返回;
var x=results.FirstOrDefault();
如果(x!=null)
{
var me=(propertyExpression.Body作为MemberExpression);
如果(me!=null)
{
var pi=me.Member作为PropertyInfo;
如果(pi!=null)
{
var setter=pi.GetSetMethod();
if(setter!=null)
{
Invoke(m1,新对象[]{x});
}
}
}
}
}
公共静态void链接到(此T m1、IEnumerable m2s、表达式属性Expression、Func tKey、Func uKey)
{
var结果=可枚举。重复(m1,1)
.Join(m2s,tKey,uKey,(t,u)=>u);
如果(!results.Any())
返回;
var x=结果
.FirstOrDefault();
如果(x!=null)
{
var me=(propertyExpression.Body作为MemberExpression);
如果(me!=null)
{
var pi=me.Member作为PropertyInfo;
如果(pi!=null)
{
var setter=pi.GetSetMethod();
if(setter!=null)
{
Invoke(m1,新对象[]{x});
}
}
}
}
}
}

您应该了解扩展方法。它们被称为“from”对象。 例如,您可以编写通用扩展方法,如下所示

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
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var listOfModel2=newmodel1();
//你可以从“对象”中调用它
listOfModel2.MyExtensionMethod();
//或者直接声明
MyExtensionMethod(listOfModel2);
}
}
公共静态类对象扩展
{
公共静态void MyExtensionMethod(此T)
{
//干坏事
}
}

我已经删除了您对MVC的所有评论,因为该框架与您的问题不相关。我的错误是,问题措辞错误-模型1和模型2来自完全不同的数据源。链接它们的唯一东西是Guid ID。我可能不应该提到JSON-巧合的是,一些数据源本身是可用的。哦,你是在尝试创建一个通用函数来完成所有模型的工作吗?正是这样,是的,这样我就可以在每个模型中存储链接数据,然后让控制器使用它。MVC不是关于链接模型。它只是模型(数据)、控制器(逻辑)、视图(表示)。如果这是webforms、wpf甚至winforms,那么问题和答案将完全相同。你想链接两个模型,即逻辑,它属于任何逻辑应该存在的地方(在MVC中,它不是模型,也不是视图)。这确实链接了它们-我的问题是我有很多复杂的链接,我更喜欢将数据存储在模型本身中,并让控制器基于它自动链接所有内容。这对我来说似乎更正确,因为它阻止控制器包含模型数据,而只是控制模型。这也使得我们更容易看到一个模型与另一个模型之间的联系。这正成为一个什么样的问题。有些人更喜欢MVC中的控制器(逻辑)编写所有没有逻辑的模型,并根据需要编写更多的模型。其他人则认为模型应该完全封装。对我来说,这取决于谁消费了这个物品。我倾向于总是使用DTO并将它们传递给数据层以创建域对象。(这并不能真正帮助回答这个问题)。很高兴知道——我并不完全熟悉MVC在这些方面的最佳实践,看起来我可能会选择一两个“正确”的东西,而这更适合“讨论”。我将更新问题以反映。