我数据库中的许多表都有公共字段,我称之为“审计”字段。它们的字段如 - UserUpdateId,UserCreateId,DateUpdated,DateCreated,DateDeleted,RowGUID,以及常见的“评论”表等。在数据库中,它们用于跟踪谁做了什么。此外,通过asp.net MVC 4视图,他们使用常见的显示模板(弹出窗口,鼠标悬停等)向用户显示这些属性。
目前,我将这些属性放入[Serializable()] CommonAttributesBase类中。然后我在所有应该继承这些属性的模型中初始化。不可否认,当我的CommonAttribute类调用存储库并且初始化看起来像是不必要的代码时,这有点笨拙且效率低下。
我很感激有关如何以最佳方式实施此建议的建议。
[Serializable()]
public class CommonAttributesBase
{
#region Notes
public Boolean AllowNotes { get; set; }
[UIHint("NoteIcon")]
public NoteCollection NoteCollection
{
get
{
if (!AllowNotes) return null;
INoteRepository noteRepository = new NoteRepository();
var notes = noteRepository.FindAssociatedNotes(RowGUID);
return new NoteCollection { ParentGuid = RowGUID, Notes = notes, AuditString = AuditTrail };
}
}
#region Audit Trail related
public void SetAuditProperties(Guid rowGuid, Guid insertUserGuid, Guid updateUserGuid, Guid? deleteUserGuid, DateTime updateDate, DateTime insertDate, DateTime? deleteDate)
{
RowGUID = rowGuid;
InsertUserGUID = insertUserGuid;
UpdateUserGUID = updateUserGuid;
DeleteUserGUID = deleteUserGuid;
UpdateDate = updateDate;
InsertDate = insertDate;
DeleteDate = deleteDate;
}
[UIHint("AuditTrail")]
public string AuditTrail
{
get
{
...code to produce readable user audit strings
return auditTrail;
}
}
...additional methods
}
在另一个班级
public partial class SomeModel
{
private CommonAttributesBase _common;
public CommonAttributesBase Common
{
get
{
if (_common == null)
{
_common = new CommonAttributesBase { AllowNotes = true, AllowAttachments = true, RowGUID = RowGUID };
_common.SetAuditProperties(RowGUID, InsertUserGUID, UpdateUserGUID, DeleteUserGUID, UpdateDate, InsertDate, DeleteDate);
}
return _common;
}
set
{
_common = value;
}
}
...rest of model
}
答案 0 :(得分:2)
对我来说,我更喜欢为每种类型(审计或注释)使用不同的接口,并使用装饰器来检索这些相关数据,而不是将它们嵌入到公共类中:
public class Note
{
//Node properties
}
public class AuditTrail
{
//Audit trail properties
}
public interface IAuditable
{
AuditTrail AuditTrail { get; set; }
}
public interface IHaveNotes
{
IList<Note> Notes { get; set; }
}
public class SomeModel : IAuditable, IHaveNotes
{
public IList<Note> Notes { get; set; }
public AuditTrail AuditTrail { get; set; }
public SomeModel()
{
Notes = new List<Note>();
}
}
public class AuditRepository : IRepository<T> where T : IAuditable
{
private IRepository<T> _decorated;
public AuditRepository(IRepository<T> decorated)
{
_decorated = decorated;
}
public T Find(int id)
{
var model = _decorated.Find(id);
model.Audit = //Access database to get audit
return model;
}
//Other methods
}
public class NoteRepository : IRepository<T> where T : IHaveNotes
{
private IRepository<T> _decorated;
public NoteRepository(IRepository<T> decorated)
{
_decorated = decorated;
}
public T Find(int id)
{
var model = _decorated.Find(id);
model.Notes = //Access database to get notes
return model;
}
//Other methods
}
优点是客户端将能够选择是否加载审计/注释,审计和注释的逻辑也与主实体存储库分开。
答案 1 :(得分:2)
你所做的基本上是作曲。正如其他人所说,有很多方法可以完成你所寻找的,有些方法比其他方法更好,但每种方法都取决于你的应用程序的需求,只有你可以与之交谈。
<强>组合物强>
组合涉及具有其他对象的对象。例如,如果您打算为汽车建模,可能会有以下内容:
public class Car
{
public Engine Engine { get; set; }
}
public class Engine
{
public int Horsepower { get; set; }
}
这种方法的好处是,您的Car
通过Horsepower
获得Engine
属性,但没有继承链。换句话说,您的Car
类可以从另一个类继承而不影响此属性或类似属性。这种方法的问题在于你必须涉及一个单独的对象,这通常不会太麻烦,但是当绑定到数据库时,你现在正在谈论有一个外键到另一个表,你必须加入才能获得所有课程。属性。
实体框架允许您通过使用它所调用的内容来减轻这种影响&#34;复杂类型&#34;。
[ComplexType]
public class Engine
{
...
}
复杂类型的属性映射到主类的表,因此不涉及连接。然而,正因为如此,复杂类型具有某些局限性。也就是说,它们不能包含导航属性 - 仅包含标量属性。此外,您需要注意实例化复杂类型,否则您可能会遇到问题。例如,模型绑定器不验证任何空的导航属性,但如果您的复杂类型上有属性(这会导致主类的属性不可为空),并且您在复杂类型属性为null时保存主类,您将从数据库中获得插入错误。为了安全起见,你应该总是这样做:
public class Car
{
public Car()
{
Engine = new Engine();
}
}
或者,
public class Car
{
private Engine engine;
public Engine Engine
{
get
{
if (engine == null)
{
engine = new Engine();
}
return engine;
}
set { engine = value; }
}
}
<强>继承强>
继承涉及从基类派生您的类,从而获取该基类的所有成员。这是最直接的方法,也是最有限的方法。这主要是因为所有.NET系列语言都只允许单继承。例如:
public class Flyer
{
public int WingSpan { get; set; }
}
public class Walker
{
public int NumberOfLegs { get; set; }
}
public class Swimmer
{
public bool HasFlippers { get; set; }
}
public class Duck : ????
{
...
}
这有点人为,但重点是Duck
是Flyer
,Walker
和Swimmer
,但它只能从一个继承这些。在仅允许单继承的语言中使用继承时必须要小心,以确保您继承的内容是可能的最完整的基类,因为您不能轻易地与此分开。
<强>接口强>
使用接口有点类似于继承,但是可以实现多个接口的额外好处。但是,缺点是实际的实现不是继承的。在前面的鸭子示例中,您可以这样做:
public class Duck : IFlyer, IWalker, ISwimmer
但是,您将负责手动在Duck
类上实现这些接口的所有成员,而继承它们只是通过基类。
接口和.NET能够扩展事物的巧妙技巧是你可以进行接口扩展。这些不会帮助你处理属性等事情,但是你可以放弃一些课程的实施。方法。例如:
public static class IFlyerExtensions
{
public static string Fly(this IFlyer flyer)
{
return "I'm flying";
}
}
然后,
var duck = new Duck();
Console.WriteLine(duck.Fly());
只需实施IFlyer
,Duck
即可获得Fly
方法,因为IFlyer
已使用该方法进行了扩展。同样,这并不能解决所有问题,但它确实允许接口更灵活。
答案 2 :(得分:1)
你可以通过几种不同的方式做这样的事情。我个人没有与EF合作,所以我不能谈论它将如何运作。
选项一:接口
public interface IAuditable
{
Guid RowGUID { get; }
Guid InsertUserGUID { get; }
Guid UpdateUserGUID { get; }
Guid DeleteUserGUID { get; }
DateTime UpdateDate { get; }
DateTime InsertDate { get; }
DateTime DeleteDate { get; }
}
当然,如果您的用例需要,可以将其更改为get
和set
。
选项二:超级/基类
public abstract class AuditableBase
{
// Feel free to modify the access modifiers of the get/set and even the properties themselves to fit your use case.
public Guid RowGUID { get; set;}
public Guid InsertUserGUID { get; set;}
public Guid UpdateUserGUID { get; set;}
public Guid DeleteUserGUID { get; set;}
public DateTime UpdateDate { get; set;}
public DateTime InsertDate { get; set;}
public DateTime DeleteDate { get; set;}
// Don't forget a protected constructor if you need it!
}
public class SomeModel : AuditableBase { } // This has all of the properties and methods of the AuditableBase class.
这个问题是如果你不能继承多个基类,但你可以实现多个接口。