避免批量分配的通用更新方法

时间:2014-02-15 09:58:30

标签: c# entity-framework generics linq-to-entities

如何在我的通用存储库中创建通用更新方法,通过指定要更新的属性来避免质量分配问题?

目前,我为每个“非通用”存储库提供了单独的更新方法,其中我基于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 };
    }

我想摆脱重复(每个存储库的单独更新方法)&在我的通用存储库中有一个更新方法。为了使这个通用,我需要

  1. 了解如何创建新的通用实体'copy'
  2. 使用字符串列表标记要更新的属性
  3. 我正在尝试做这样的事情(目前不起作用)。指导赞赏!

    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; }
    }
    

2 个答案:

答案 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);

请注意,如果您要将包含其导航属性的实体更新为相关实体,则此方法无效。