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在这些方面的最佳实践,看起来我可能会选择一两个“正确”的东西,而这更适合“讨论”。我将更新问题以反映。