我想编写一个通用扩展方法来链接两个类/对象
类:
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);
答案 0 :(得分:1)
正如我们在聊天中所讨论的,我推荐使用轻量级的EF Context(带反射)。这完全是自定义和动态的,您只需在模型上使用KeyAttribute
和ForeignKeyAttribute
,并将模型添加到此自定义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!
答案 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
}
}