EntityFramework首先使用数据库检测复杂类型

Min*_*Min 5 c# database entity-framework

我们正在使用EntityFramework并且已经使用Database First进行了长途旅行.我们有多个上下文和许多实体.虽然我们接受了首先进行代码项目的概念,但我们希望探索EF可用的其他可能性.

我们正在使用最新版本的EF,当前版本为6.0.2

我们有很多常用字段的表,例如审计字段"CreatedBy","CreatedDate","UpdatedBy"和"UpdatedDate",我们认为在这里使用ComplexType是完美的.

在使用这些字段的表中,当我们从数据库生成代码时,它会以raw格式提取字段.然后,我们在模型浏览器中删除它,添加复杂类型,然后将复杂属性映射到DB中的字段名称.

在这个实验中,我们运行"从模型生成数据库"而不映射字段以查看结果列名称是什么,以及任何约定或自动魔术是否允许双向行为(将复杂类型转换为列并将列识别为复杂的类型).

这导致列名称的复杂类型后缀为"_"和字段名称.我们进行了测试,当我们重新生成模型时,它没有从模型中的数据库中拉出复杂类型.

有没有一种正确的方法让EF先检测具有数据库的表中的复杂类型?我可以编写一些代码工厂,构建器,策略或模板吗?

我们的主要动机是我们有很多表可以支持,我们接受频繁的更改,我们希望阻止团队中的人忽略这一步并打破代码库.

非常感谢StackOverflowians的时间!

- 编辑 -

虽然这并没有解决使用复杂类型的自动检测的问题,但这解决了Developer在每次运行T4模板更新时破坏模型的问题.

http://www.ninjacrab.com/2015/02/07/update-entityframework-detecting-complex-type-with-database-first/

Eri*_*ips 1

我所做的是基于 EF 的通用半存储库模式。我还使用接口来允许识别和自动使用方法(如果它们匹配)。

可审计接口:

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; } 
}
Run Code Online (Sandbox Code Playgroud)

(我个人喜欢这样的想法:如果 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我将如何使用它:

// 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);
Run Code Online (Sandbox Code Playgroud)

这个示例应用程序非常小,因此我没有实现任何 UnitOfWork,但可以针对这种情况轻松修改它。此外,如果您有多个上下文,该类可以很容易地MyDb变成 a 。MyDb<T>我允许大量使用 DataAnnotations。