Jcl*_*Jcl 9 c# entity-framework ef-migrations
这可能是一个简单的问题,但我是Code First和Migrations的新手,所以请耐心等待.我会将示例代码保持在最低限度以显示问题:
我有一个BaseAuditableEntity
包括这个(除其他外,但让我们简化):
public abstract class BaseAuditableEntity : BaseEntity, IAuditableEntity
{
public DateTime CreatedOn { get; set; }
public DateTime LastModified { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
现在(例如)User
POCO继承自它:
public class User : BaseAuditableEntity
{
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public bool Active { get; set; }
public DateTime? LastLogin { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我在上下文的SaveChanges
方法中有这个,填写CreatedOn
和LastModified
日期(简化):
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditableEntity>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(p => p.State != EntityState.Unchanged))
{
var now = DateTime.UtcNow;
if (entry.State == EntityState.Added)
entry.Entity.CreatedOn = now;
entry.Entity.LastModified = now;
}
}
return base.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
现在我有一个移植到位,为一些用户提供种子,如下所示:
protected override void Seed(MyContext context)
{
context.Users.AddOrUpdate(
p => p.UserName,
new User { Active = true,
FullName = "My user name",
UserName = "ThisUser",
PasswordHash = "",
Email = "my@email",
LastLogin = null,
}
// etc.
);
}
Run Code Online (Sandbox Code Playgroud)
现在我有一个关于AddOrUpdate
迁移后播种的问题.当实体是新的(它被添加)时,CreatedOn
正确填充并且一切都按预期工作.但是,当实体被修改(它已经存在于数据库中并UserName
匹配)时,它会尝试使用我正在创建的新实体更新它...这会因为CreatedOn
无效DateTime
(在这种情况下DateTime.MinValue
)而失败.
有没有办法使用该AddOrUpdate
方法,以便它实际从数据库中检索匹配的实体,只更新非默认字段?或者也许某种方式告诉它哪些字段不更新?对于这个特定情况,我希望该CreatedOn
字段保持不变,但是可以理解通用解决方案.
也许我应该做我自己的AddOrUpdate
方法,其中包含一个带有我想要更改的字段的谓词,而不是将它传递给一个全新的实体?
这是EF 6.1
我知道我可以轻松地解决这个问题CreatedOn
,这是我目前正在为这个具体案例做的事情:
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
var now = DateTime.UtcNow;
if (entry.State == EntityState.Added)
{
entry.Entity.CreatedOn = now;
}
else
{
if (entry.Property(p => p.CreatedOn).CurrentValue == DateTime.MinValue)
{
var original = entry.Property(p => p.CreatedOn).OriginalValue;
entry.Property(p => p.CreatedOn).CurrentValue = original != SqlDateTime.MinValue ? original : now;
entry.Property(p => p.CreatedOn).IsModified = true;
}
}
entry.Entity.LastModified = now;
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找更通用的解决方案
Yul*_*dra 11
实行AddOrUpdate
用途CurrentValues.SetValues
,使所有的标量的属性将被修改.
我已经扩展了功能,以接受在更新时要修改的属性,否则它只是一个创建,只需使用DbSet<T>::Add
.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class SeedExtension
{
public static void Upsert<T>(this DbContext db, Expression<Func<T, object>> identifierExpression, Expression<Func<T, object>> updatingExpression, params T[] entities)
where T : class
{
if (updatingExpression == null)
{
db.Set<T>().AddOrUpdate(identifierExpression, entities);
return;
}
var identifyingProperties = GetProperties<T>(identifierExpression).ToList();
Debug.Assert(identifyingProperties.Count != 0);
var updatingProperties = GetProperties<T>(updatingExpression).Where(pi => IsModifiedable(pi.PropertyType)).ToList();
Debug.Assert(updatingProperties.Count != 0);
var parameter = Expression.Parameter(typeof(T));
foreach (var entity in entities)
{
var matches = identifyingProperties.Select(pi => Expression.Equal(Expression.Property(parameter, pi.Name), Expression.Constant(pi.GetValue(entity, null))));
var matchExpression = matches.Aggregate<BinaryExpression, Expression>(null, (agg, v) => (agg == null) ? v : Expression.AndAlso(agg, v));
var predicate = Expression.Lambda<Func<T, bool>>(matchExpression, new[] { parameter });
var existing = db.Set<T>().SingleOrDefault(predicate);
if (existing == null)
{
// New.
db.Set<T>().Add(entity);
continue;
}
// Update.
foreach (var prop in updatingProperties)
{
var oldValue = prop.GetValue(existing, null);
var newValue = prop.GetValue(entity, null);
if (Equals(oldValue, newValue)) continue;
db.Entry(existing).Property(prop.Name).IsModified = true;
prop.SetValue(existing, newValue);
}
}
}
private static bool IsModifiedable(Type type)
{
return type.IsPrimitive || type.IsValueType || type == typeof(string);
}
private static IEnumerable<PropertyInfo> GetProperties<T>(Expression<Func<T, object>> exp) where T : class
{
Debug.Assert(exp != null);
Debug.Assert(exp.Body != null);
Debug.Assert(exp.Parameters.Count == 1);
var type = typeof(T);
var properties = new List<PropertyInfo>();
if (exp.Body.NodeType == ExpressionType.MemberAccess)
{
var memExp = exp.Body as MemberExpression;
if (memExp != null && memExp.Member != null)
properties.Add(type.GetProperty(memExp.Member.Name));
}
else if (exp.Body.NodeType == ExpressionType.Convert)
{
var unaryExp = exp.Body as UnaryExpression;
if (unaryExp != null)
{
var propExp = unaryExp.Operand as MemberExpression;
if (propExp != null && propExp.Member != null)
properties.Add(type.GetProperty(propExp.Member.Name));
}
}
else if (exp.Body.NodeType == ExpressionType.New)
{
var newExp = exp.Body as NewExpression;
if (newExp != null)
properties.AddRange(newExp.Members.Select(x => type.GetProperty(x.Name)));
}
return properties.OfType<PropertyInfo>();
}
}
Run Code Online (Sandbox Code Playgroud)
用法.
context.Upsert(
p => p.UserName,
p => new { p.Active, p.FullName, p.Email },
new User
{
Active = true,
FullName = "My user name",
UserName = "ThisUser",
Email = "my@email",
}
);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3904 次 |
最近记录: |