如何在我的通用存储库中创建通用更新方法,通过指定要更新的属性来避免质量分配问题?
目前,我为每个“非通用”存储库提供了单独的更新方法,其中我基于id创建新实体,然后手动分配每个属性。例如,请参阅下面的代码。
public OperationStatus DoUpdateGame(Game game)
{
var gm = DataContext.Games.Where(g => g.Id == game.Id).FirstOrDefault();
if (gm != null)
{
//manually map all properties to avoid mass assignment security issues
gm.CreatorId = game.CreatorId;
gm.CurrentOrderPosition = game.CurrentOrderPosition;
gm.HasStarted = game.HasStarted;
gm.Name = game.Name;
gm.HasEnded = game.HasEnded;
gm.WinnerId = game.WinnerId;
}
return new OperationStatus { Status = true };
}
我想摆脱重复(每个存储库的单独更新方法)&在我的通用存储库中有一个更新方法。为了使这个通用,我需要
我正在尝试做这样的事情(目前不起作用)。指导赞赏!
public virtual OperationStatus Update(T entity, string idColName, params string[] propsToUpdate)
{
OperationStatus opStatus = new OperationStatus { Status = true };
// this line fails with 'LINQ to Entities does not recognize the method 'System.Reflection.PropertyInfo GetProperty(System.String)' method, and this method cannot be translated into a store expression.'
var current = DataContext.Set<T>().Where(g => g.GetType().GetProperty(idColName) == entity.GetType().GetProperty(idColName)).FirstOrDefault();
try
{
if (propsToUpdate != null && propsToUpdate.Length > 0)
{
//update specific properties
foreach (var propName in propsToUpdate)
{
TrySetProperty(current, propName, entity.GetType().GetProperty(propName));
}
}
opStatus.Status = true; // DataContext.SaveChanges() > 0;
}
catch (Exception exp)
{
// log
}
return opStatus;
}
private void TrySetProperty(object obj, string property, object value)
{
var prop = obj.GetType().GetProperty(property, BindingFlags.Public | BindingFlags.Instance);
if (prop != null && prop.CanWrite)
prop.SetValue(obj, value, null);
}
更新
我现在正在使用带有DTO对象的automapper来将原始实体映射到DTO。我现在得到'没有项目顺序错误'...下面的代码
public virtual OperationStatus Update(T entity, string idColName, params string[] propsToUpdate)
{
OperationStatus opStatus = new OperationStatus { Status = true };
var sourceType = entity.GetType();
// this line gives the 'no items in sequence error
var destinationType = Mapper.GetAllTypeMaps().
Where(map => map.SourceType == sourceType).
Single().DestinationType;
var current = Mapper.Map(entity, destinationType);
我的自动配置
public static class AutoMapperConfiguration
{
public static void Configure()
{
ConfigureMapping();
}
private static void ConfigureMapping()
{
Mapper.CreateMap<Game, GameUpdateDTO>();
}
}
global.asax中的行
AutoMapperConfiguration.Configure();
DTO模型
class GameUpdateDTO
{
public int Id { get; set; }
public int CreatorId { get; set; }
public string Name { get; set; }
public int CurrentOrderPosition { get; set; }
public bool HasStarted { get; set; }
public bool HasEnded { get; set; }
public int WinnerId { get; set; }
}
答案 0 :(得分:1)
您可以使用AutoMapper轻松解决此问题。这里有一些代码可能会给你一些想法。我发现链接非常有用。你可能想看看那里。
Mapper.CreateMap<Game, Game>();
Mapper.DynamicMap(newGame, oldGame);
GameRepository.Update(oldGame);
<强>更新强> 尝试这个示例用于泛型类型
internal class Program
{
private static void Main(string[] args)
{
var customerRepo = new Repository<Customer>(new Customer());
var userRepo = new Repository<User>(new User());
customerRepo.Update(new Customer
{
DateOfBirth = DateTime.Now,
FirstName = "Customer",
LastName = "Cust",
NumberOfOrders = 3
});
userRepo.Update(new User
{
DateOfBirth = DateTime.Now,
FirstName = "User",
LastName = "Us"
});
userRepo.Show();
customerRepo.Show();
Console.ReadKey();
}
public class Repository<T>
{
private T _entity;
public Repository(T entity)
{
_entity = entity;
}
public void Update(T data)
{
Mapper.CreateMap<T, T>();
Mapper.DynamicMap(data, _entity);
}
public void Show()
{
Console.WriteLine(_entity.ToString());
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public int NumberOfOrders { get; set; }
public override string ToString()
{
return GetType().Name + "= " + FirstName + " " + LastName + " " + DateOfBirth + " " + NumberOfOrders;
}
}
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public override string ToString()
{
return GetType().Name + "= " + FirstName + " " + LastName + " " + DateOfBirth;
}
}
}
答案 1 :(得分:1)
一旦有了DTO课程,就可以轻松地在Entity Framework中内置通用更新:
public void Update<T>(object dto, Expression<Func<T, bool>> currentEntityFilter)
where T : class
{
var current = DataContext.Set<T>().FirstOrDefault(currentEntityFilter);
DataContext.Entry(current).CurrentValues.SetValues(dto);
}
DTO属性与实体属性具有相同的名称非常重要。
您可以这样称呼它:
Update<Game>(gameUpdateDto, g => g.Id == gameUpdateDto.Id);
或者,如果您只想更新一些选定的属性,例如:
Update<Game>(new
{
gameUpdateDto.Name,
gameUpdateDto.HasStarted,
gameUpdateDto.HasEnded
}, g => g.Id == gameUpdateDto.Id);
如果您总是想通过其密钥更新实体,我更喜欢这个签名和实现:
public void Update<T>(object dto, params object[] keyValues)
where T : class
{
var current = DataContext.Set<T>().Find(keyValues);
DataContext.Entry(current).CurrentValues.SetValues(dto);
}
像这样被召唤:
Update<Game>(gameUpdateDto, gameUpdateDto.Id);
请注意,如果您要将包含其导航属性的实体更新为相关实体,则此方法无效。