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

时间:2014-02-07 14:38:13

标签: c# database entity-framework

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

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

我们有很多公共领域的表格,例如审计字段和#34; CreatedBy"," CreatedDate"," UpdatedBy"和" UpdatedDate"我们认为在这里使用ComplexType会很完美。

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

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

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

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

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

非常感谢StackOverflowians的时间!

- 编辑 -

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

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

1 个答案:

答案 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。