我们正在使用EntityFramework并且已经使用Database First进行了长途旅行。我们有多个上下文和许多实体。虽然我们首先接受了代码项目的概念,但我们希望探索EF可用的其他可能性。
我们正在使用最新版本的EF,当前版本为6.0.2
我们有很多公共领域的表格,例如审计字段和#34; CreatedBy"," CreatedDate"," UpdatedBy"和" UpdatedDate"我们认为在这里使用ComplexType会很完美。
在使用这些字段的表中,当我们从数据库生成代码时,它会以raw格式提取字段。然后我们在模型浏览器中删除它,添加复杂类型,然后将复杂属性映射到数据库中的字段名称。
在这个实验中,我们运行了#34;从模型中生成数据库"没有映射字段以查看结果列名称是什么,以及任何约定或自动魔术是否允许双向行为(将复杂类型转换为列并将列识别为复杂类型)。
这导致列名称的复杂类型后缀为' _'和字段名称。我们进行了测试,当我们重新生成模型时,它没有从模型中的数据库中拉回复杂类型。
是否有一种正确的方法让EF先检测具有数据库的表中的复杂类型? 我可以写一些代码工厂,构建器,策略或模板吗?
我们的主要动机是我们有很多表可以支持,我们接受频繁的更改,我们希望阻止团队成员忽视这一步并打破代码库。
非常感谢StackOverflowians的时间!
- 编辑 -
虽然这并没有使用复杂类型的自动检测解决问题,但这解决了开发人员每次运行T4模板更新时破坏模型的问题。
答案 0 :(得分:1)
我所做的是EF上的Generic 半存储库模式。我还使用了接口来识别和自动使用方法,如果匹配的话。
可审核界面:
public interface IDbAuditable : IDbEntity
{
Guid CreatedById { get; set; }
DateTime CreatedOn { get; set; }
Guid ModifiedById { get; set; }
DateTime ModifiedOn { get; set; }
Guid? DeletedById { get; set; }
DateTime? DeletedOn { get; set; }
}
(我个人喜欢这样的想法:如果CreatedOn == ModifiedOn那么它从未被修改过,有些人喜欢DateTime?,它们的用途并不重要。)
由于这个例子是在一个小项目中,我只是包装了EF Context并且没有使用任何IoC / DI。这是所有实际代码的一小部分(缺少某些方法,缺少某些接口,但它应该是完全合理的。)
public sealed class MyDb : IDisposable
{
private MyContext _context;
public MyDb(string connectionString, Lazy<Guid?> currentUserIdFunc)
{
this._context = new MyContext(connectionString);
Database.SetInitializer<MyContext>(new DatabaseInitializer());
this._context.Database.Initialize(true);
this._currentUserIdFunc = currentUserIdFunc;
}
public async Task<T> GetEntityAsync<T>(Func<IQueryable<T>, IQueryable<T>> entityQuery) where T : class, IDbEntity
{
var query = entityQuery(this._context.Set<T>());
if (typeof(T) is IDbAuditable)
{
query = query.Cast<IDbAuditable>()
.Where(a => !a.DeletedById.HasValue)
.Cast<T>();
}
return await query.FirstOrDefaultAsync();
}
public async Task<int> UpdateAsync<T>(T entity) where T : class, IDbEntity
{
if (entity is IDbDoNotModify)
{
throw new DoNotModifyException("Entity cannot be Modified (IDoNotModify).");
}
this._context.Set<T>().Attach(entity);
var entry = this._context.Entry<T>(entity);
entry.State = EntityState.Unchanged;
var entityType = entity.GetType();
var metadata = entityType.GetCustomAttributes(typeof(MetadataTypeAttribute)).FirstOrDefault() as MetadataTypeAttribute;
if (metadata != null)
{
var type = metadata.MetadataClassType;
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(p => new
{
Name = p.Name,
ScaffoldColumn = p.GetCustomAttributes(typeof(ScaffoldColumnAttribute), true).FirstOrDefault() as ScaffoldColumnAttribute,
Readonly = entityType.GetProperty(p.Name).GetCustomAttributes(typeof(ReadOnlyAttribute), true).FirstOrDefault() as ReadOnlyAttribute
})
.Where(p => (p.ScaffoldColumn == null || p.ScaffoldColumn.Scaffold)
&& (p.Readonly == null || !p.Readonly.IsReadOnly))
.ToList();
foreach (var property in properties)
{
entry.Property(property.Name).IsModified = true;
}
}
else
{
entry.State = EntityState.Modified;
}
var auditable = entity as IDbAuditable;
if (auditable != null)
{
this.Modified(auditable, this._currentUserIdFunc.Value);
entry.Property("ModifiedOn").IsModified = true;
entry.Property("ModifiedById").IsModified = true;
}
return await this._context.SaveChangesAsync();
}
private void Modified(IDbAuditable instance, Guid? currentUserId)
{
instance.ModifiedById = currentUserId.Value;
instance.ModifiedOn = DateTime.Now;
}
}
以下是我将如何使用它:
// returns the first car with a model of ford
var car = MyDb.EntityAsync<Car>((query) = query
.Where(c => c.Model.Equals("ford")
);
// returns the first dog with an ownerId of id
var car = MyDb.EntityAsync<Dog>((query) => query
.Where(d => d.OwnerId == Id)
);
UpdateAsync(car);
这个示例应用程序非常小,所以我没有实现任何UnitOfWork,但可以很容易地修改这种情况。此外,如果您很容易拥有多个上下文,MyDb
类可以转换为MyDb<T>
。我允许大量使用DataAnnotations。