xUnit testing using in-memory database for PUT method - entity-framework-core

I have been trying to do an X unit test for the repository using an in-memory database. I am unable to perform the update function. my test case is throwing an exception.
The code which I have written namespace for testing the repository
using AmazonAPI.Models;
using AmazonAPI.Repository;
using FakeItEasy;
using FluentAssertions;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
namespace AmazonAPITesting.Amazon_Repository
{
public class Amazon_Merchant_Repository
{
private async Task<AmazonContext> GetDatabaseContext()
{
var options = new DbContextOptionsBuilder<AmazonContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var databaseContext = new AmazonContext(options);
databaseContext.Database.EnsureCreated();
int temp = 1000;
if (await databaseContext.Merchants.CountAsync() <= 0)
{
for(int i = 0; i < 10; i++)
{
databaseContext.Merchants.Add(
new Merchant()
{
MerchantId = temp++,
MerchantEmail = "akhil"+i+"#gmail.com",
MerchantName = "akhil"+i,
MerchantPassword = "12345",
ConfirmPassword = "12345",
}
);
await databaseContext.SaveChangesAsync();
}
}
return databaseContext;
}
[Fact]
public async Task UpdateMerchant_Merchant()
{
//Arrange
var Id = 1001;
Merchant merchant = new Merchant
{
MerchantId = Id,
MerchantEmail = "akhil1#gmail.com",
MerchantName = "updatedAkhil",
MerchantPassword = "12345",
ConfirmPassword = "12345",
};
var dbContext = await GetDatabaseContext();
var merchantRepository = new MerchantRepository(dbContext);
//Act
var result = await merchantRepository.UpdateMerchant(Id,merchant);
//Assert
var name = result.MerchantName;
"updatedAkhil".Should().BeEquivalentTo(name);
}
}
}
The real method in repository
public async Task<Merchant> UpdateMerchant(int MerchantId, Merchant Merchant)
{
//Merchant _merchant = await _context.Merchants.FindAsync(MerchantId);
_context.Update(Merchant);
_context.SaveChanges();
return Merchant;
}
the merchant model
public class Merchant
{
[Key]
public int MerchantId { get; set; }
[Required(ErrorMessage = "Field can't be empty")]
[DataType(DataType.EmailAddress, ErrorMessage = "E-mail is not valid")]
public string? MerchantEmail { get; set; }
public string? MerchantName { get; set; }
[DataType(DataType.PhoneNumber)]
[Display(Name = "Phone Number")]
public string? MerchantPhoneNumber { get; set; }
[Display(Name = "Please enter password"), MaxLength(20)]
public string? MerchantPassword { get; set; }
[NotMapped]
[Display(Name = "ConfirmPassword")]
[Compare("MerchantPassword", ErrorMessage = "Passwords do not match")]
public string? ConfirmPassword { get; set; }
}
the exception which i got fron=m the test case
Message: 
System.InvalidOperationException : The instance of entity type 'Merchant' cannot be tracked because another instance with the same key value for {'MerchantId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Stack Trace: 
IdentityMap1.ThrowIdentityConflict(InternalEntityEntry entry) IdentityMap1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
IdentityMap1.Add(TKey key, InternalEntityEntry entry) IdentityMap1.Add(InternalEntityEntry entry)
StateManager.StartTracking(InternalEntityEntry entry)
InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties)
InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean modifyProperties, Nullable1 forceStateWhenUnknownKey) EntityGraphAttacher.PaintAction(EntityEntryGraphNode1 node)
EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode1 node, Func2 handleNode)
EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, Boolean forceStateWhenUnknownKey)
DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
DbContext.Update[TEntity](TEntity entity)
MerchantRepository.UpdateMerchant(Int32 MerchantId, Merchant Merchant) line 90
Amazon_Merchant_Repository.UpdateMerchant_Merchant() line 109
--- End of stack trace from previous location ---
this is my repository [link]https://github.com/Akhil1812007/AmazonAPI-Test , can someone help me to resolve this problem ,by helping me how to do put actions in the Xunit test

Related

.NET Core MongoDB. Find by guid returns null

I have the following setup:
The document:
[BsonCollection("Users")] // I get the collection name with a custom extension
[BsonIgnoreExtraElements]
public class UserDocument
{
[BsonId]
public Guid Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public UserSettingsModel UserSettings { get; set; }
}
public class UserSettingsModel
{
// ...
}
The repository:
public class UserRepository
{
private readonly IMongoCollection<UserDocument> _collection;
private readonly ILogger<UserRepository> _logger;
public UserRepository(IMongoDatabase database, ILogger<UserRepository> logger)
{
// returns "Users"
var collectionName = typeof(UserDocument).GetCollectionName();
_collection = database.GetCollection<UserDocument>(collectionName);
}
// ...
public async Task<UserDocument> GetById(Guid id)
{
var filter = Builders<UserDocument>.Filter.Eq(x => x.Id, id);
var user = await _collection.FindAsync(filter);
// var user = await _collection.FindAsync(x => x.Id == id); - doesn't work either
var request = filter.Render(
_collection.DocumentSerializer,
_collection.Settings.SerializerRegistry).ToString();
_logger.LogDebug(request);
return user.FirstOrDefault();
}
}
And I initialize the client this way:
// ...
BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
var client = new MongoClient(connectionString);
var database = client.GetDatabase(dbName);
services.AddSingleton(c => database);
// convention pack and registries
// ...
// if moved here doesn't work either
// BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
The filter generated in GetById is still the following: { "_id" : CSUUID("459f165a-4a91-4f39-906c-dc7401ee2468") } when I expect it to be UUID instead of CSUUID.
So, the query doesn't find anything and returns null. In the database the document I'm searching for has _id: UUID('459f165a-4a91-4f39-906c-dc7401ee2468')
What am I doing wrong?
I was able to fixing by this trick:
var mongoConnectionUrl = new MongoUrl(connectionString);
var mongoClientSettings = MongoClientSettings.FromUrl(mongoConnectionUrl);
// before initializing client
mongoClientSettings.GuidRepresentation = GuidRepresentation.Standard;
However setting the GuidRepresentation in client settings is obsolete, which is quite confusing. Also, the query generated still has CSUUID instead of UUID. I was able to log the query the following way:
mongoClientSettings.ClusterConfigurator = cb =>
{
cb.Subscribe<CommandStartedEvent>(e => logger.LogDebug($"{e.CommandName} - {e.Command.ToJson()}"));
};
If anyone finds a better way and post it here it would be appreciated.

An error occurred while updating the entries

I'm strugglish with adding feature for my controller. While adding new item, receving the error like: "An error occurred while updating the entries. See the inner exception for details."
I debugged it, and understood ProductDetailIs is null and here is the issue. But, can not figure out how to mend the problem.
Here is the DTO models:
public class WishlistItemDto
{
public int Id { get; set; }
public string CustomerId { get; set; }
public ProductDetailsDtoWithPrimaryImage ProductDetails { get; set; }
public int Quantity { get; set; }
}
public class WishListItemCreationDto
{
public string CustomerId { get; set; }
public int ProductDetailId { get; set; }
public int Quantity { get; set; }
}
Controller:
[HttpPost]
public async Task<IActionResult> Add(WishListItemCreationDto wishListItemDto)
{
var itemAdd = _mapper.Map<WishlistItemDto>(wishListItemDto);
var itemCreated = await _wishListItemService.AddAsync(itemAdd);
return CreatedAtAction(nameof(GetId), new { id = itemCreated.Id }, wishListItemDto);
}
Service:
public async Task<WishlistItemDto> AddAsync(WishlistItemDto item)
{
var entity = _mapper.Map<WishlistItem>(item);
await _wishListItemRepository.AddAsync(entity);
return _mapper.Map<WishlistItemDto>(entity);
}
Repository:
public async Task<WishlistItem> AddAsync(WishlistItem item)
{
await _context.Set<WishlistItem>().AddAsync(item);
await _context.SaveChangesAsync();
return item;
}
This line here:
var itemAdd = _mapper.Map<WishlistItemDto>(wishListItemDto);
your "wishListItemDto" is passed in as a 'WishListItemCreationDto' which contains only a ProductDetailsId. Automapper will have no way of knowing how to convert that into a ProductDetailsDtoWithPrimaryImage.
Typically for something like this where you pass an reference ID you would compose your entity by either populating a FK or loading the referenced entity. Your existing service and repository patterns will complicate your final solution. From what I can see from your example I'd look at creating an AddAsync method that accepts the WishListItemCreationDto:
public async Task<WishlistItemCreationDto> AddAsync(WishlistItemCreationDto item)
{
var entity = _mapper.Map<WishlistItem>(item);
var productDetails = _productDetailsRepository.GetById(item.ProductDetailsId);
entity.ProductDetails = productDetails;
await _wishListItemRepository.AddAsync(entity);
return _mapper.Map<WishlistItemDto>(entity);
}
Without the added abstraction complexity of the Service and Repository the add operation can be a whole lot simpler:
[HttpPost]
public async Task<IActionResult> Add(WishListItemCreationDto wishListItemDto)
{
// or better, use an injected dependency to the Context...
// TODO: add applicable exception handling.
using(var context = new AppDbContext())
{
var item = _mapper.Map<WishlistItem>(wishListItemDto);
var productDetails = context.ProductDetails.Single(x => x.ProductDetailsId == wishListItemDto.ProductDetailsId);
item.ProductDetails = productDetails;
context.SaveChanges();
return CreatedAtAction(nameof(GetId), new { id = itemCreated.Id }, wishListItemDto);
}
}

Integration Testing in .NET Core 3.1 with AutoMapper, WebApplicationFactory, Entity Framework, and DTOs

We have an API with about a dozen integration tests. All the tests passed until I added some DTOs and used AutoMapper. Now, all the tests that test methods that use AutoMapper and the DTOs are failing. I have provided all the code needed to understand one of the failing tests. Also, I read a lot about AutoMapper and the following StackOverflow posts:
Integration Testing with AutoMapper fails to initialise configuration
A kind of integration testing in ASP.NET Core, with EF and AutoMapper
Startup.cs
This is our Startup.ConfigureServices(). I have tried every code block commented out and/or marked "ATTEMPTED".
public void ConfigureServices(IServiceCollection services)
{
services
.AddDbContext<OurContext>(options =>
options.UseSqlServer(Configuration["ConnectionString"]))
.AddDbContext<OurContext>()
.AddRazorPages()
.AddMvcOptions(options => options.EnableEndpointRouting = false)
.AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services
.AddControllersWithViews();
//ATTEMPTED
//services
// .AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
//ATTEMPTED
//MapperConfiguration mapperConfiguration = new MapperConfiguration(mc =>
//{
// mc.AddProfile(new OurProfile());
//});
//IMapper mapper = mapperConfiguration.CreateMapper();
//services
// .AddSingleton(mapper);
//ATTEMPTED
//services
// .AddAutoMapper(typeof(Startup));
//ATTEMPTED
//var assembly = typeof(Program).GetTypeInfo().Assembly;
//services
// .AddAutoMapper(assembly);
//ATTEMPTED
var assembly = typeof(Program).GetTypeInfo().Assembly;
services.AddAutoMapper(cfg =>
{
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<OurModel, OurDto>()
.IgnoreAllPropertiesWithAnInaccessibleSetter();
}, assembly);
}
Controller
This is our controller.
[Route("api/[controller]")]
[ApiController]
public class OurController : ControllerBase
{
private readonly OurContext _context;
protected readonly ILogger<OurController> Logger;
private readonly IMapper _mapper;
public OurController(OurContext context, ILogger<OurController> logger,
IMapper mapper)
{
_context = context ??
throw new ArgumentNullException(nameof(context));
Logger = logger ??
throw new ArgumentNullException(nameof(logger));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
}
[HttpGet]
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IQueryable<OurDto> dtos =
_mapper.Map<IQueryable<OurDto>>(models);
return await dtos.ToListAsync();
}
}
Profile, Model, and DTO
Profile
public class OurProfile : Profile
{
public OurProfile()
{
CreateMap<OurModel, OurDto>();
}
}
Model
public partial class OurModel
{
public string Number { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Status { get; set; }
public DateTime? Date { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public string District { get; set; }
}
DTO
public class OurDto
{
public string Number { get; set; }
public string Name1 { get; set; }
public string Name2 { get; set; }
public string Status { get; set; }
public DateTime? Date { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
public string District { get; set; }
}
Test Fixture
This is our test fixture.
public abstract class ApiClientFixture : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
protected abstract string RelativeUrl { get; }
protected ApiClientFixture(WebApplicationFactory<Startup> factory)
{
_factory = factory;
}
protected HttpClient CreateClient()
{
HttpClient client;
var builder = new UriBuilder();
client = _factory.CreateClient();
builder.Host = client.BaseAddress.Host;
builder.Path = $"{RelativeUrl}";
client.BaseAddress = builder.Uri;
return client;
}
}
Test
This is our test class. The single test in this test class fails.
public class Tests : ApiClientFixture
{
public Tests(WebApplicationFactory<Startup> factory) : base(factory)
{
}
protected override string RelativeUrl => "api/OurController/";
[Fact]
public async void GetAllReturnsSomething()
{
var response = await CreateClient().GetAsync("");
Assert.True(response.IsSuccessStatusCode);
}
}
When I debug the test I see that a 500 status code is returned from the URL provided to the in-memory API.
Does anybody have some suggestions? More than half of our tests currently fail, and I suspect that AutoMapper is not configured properly for integration testing.
Creating a map for IQueryable<T> is not really a good solution. In your answer you are losing proper flow of asynchronous database querying. I wrote about IQueryable<T> in a comment because you were looking for a 500 error cause. Making it work it's a one thing, making it a good solution it's another thing, however.
I'd strongly suggest to use AutoMapper ProjectTo() extension which you can use directly on a IQueryable<T> sequence. It let's you combine mapping and querying in one go. More or less it does a Select() based on your mappings, so it not only gives you proper model right away with the query result, but it also reduces the amount of columns obtained from database, which can make the query run faster. But, there are of course limitations to it, e.g. you can't use custom type converters or conditional mapping. You can read more about Project() in the documentation.
Usage:
public async Task<ActionResult<List<OurDto>>> GetAll()
{
return await _context
.OurModel
.ProjectTo<OutDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
Thanks to #Prolog for his comment. I realized that I need to map each element of the IQueryable individually, so I rewrote my Controller method.
Also, side note: IList.AsQueryable().ToListAsync() does not work, so I wrote:
IQueryable<OurDto> dtosQueryable = dtos.AsQueryable();
return await Task.FromResult(dtosQueryable.ToList());
Old Controller Method
[HttpGet]
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IQueryable<OurDto> dtos =
_mapper.Map<IQueryable<OurDto>>(models);
return await dtos.ToListAsync();
}
New Controller Method
public async Task<ActionResult<IEnumerable<OurDto>>> GetAll()
{
IQueryable<OurModel> models = _context.OurModel;
IList<OurDto> dtos = new List<OurDto>();
foreach (OurModel model in models)
{
OurDto dto = _mapper.Map<OurDto>(model);
dtos.Add(dto);
}
IQueryable<OurDto> dtosQueryable = dtos.AsQueryable();
return await Task.FromResult(dtosQueryable.ToList());
}

AddOrUpdate violates unique index

I'm writing an MVC app in ASP.NET with the help of EF and I'm trying to seed my database. I have the following model:
public class Team
{
[ScaffoldColumn(false)]
public int Id { get; set; }
[ForeignKey("ParentTeam")]
public int? ParentTeamId { get; set; }
[Required(ErrorMessage = "Cannot create a Team without a name")]
[Index(IsUnique = true)]
[MaxLength(30)]
public string Name { get; set; }
public IEnumerable<string> Members { get; set; }
public virtual Team ParentTeam { get; set; }
public Team() { }
public Team(string name)
{
Name = name;
}
}
My migration says:
var team = new Team("Admin");
var team2 = new Team("Test Team");
var team3 = new Team("Test Team 2");
context.Teams.AddOrUpdate(t => t.Name, team, team2, team3);
context.SaveChanges();
And then, when I run Update-Database, I get:
System.Data.SqlClient.SqlException: Cannot insert duplicate key row in
object 'dbo.Teams' with unique index 'IX_Name'. The duplicate key
value is (Admin).
It's a little confusing - I thought I told AddOrUpdate to identify rows to update by their names, but this does not happen. I cannot add Name to Team's primary key, because it has a self-referencing foreign key (I could add ParentTeamName as a property, but I don't feel that it should be necessary). Am I misunderstanding the behaviour of AddOrUpdate? Did I specify the condition wrong?
I had the exact same reason. In my case, it was working fine, until I needed to use an Unique Index, when it broke.
My solution was to create a CustomAddOrUpdate method where I try to find the existing instance first based on a Where predicate. If I find it, I just update the properties and if not, it is added to the context.
However, before updating the instance, I had to copy the key values from the original instance to the new instance, to avoid an EF exception telling you cannot change key properties.
Here are the code snippets:
1) First the code in the context class
public void CustomAddOrUpdate<TEntity>(Expression<Func<TEntity, bool>> whereExpression, TEntity entity) where TEntity : class
{
var entitySet = this.EntitySet<TEntity>();
var foundInstance = entitySet.Where(whereExpression).FirstOrDefault();
if (foundInstance != null)
{
CopyKeyProperties<TEntity>(foundInstance, entity);
Entry(foundInstance).CurrentValues.SetValues(entity);
}
else
{
entitySet.Add(entity);
}
}
private void CopyKeyProperties<TEntity>(TEntity source, TEntity target) where TEntity : class
{
string[] keys = this.GetKeyNames<TEntity>();
foreach(var keyName in keys)
{
Entry(target).Property(keyName).CurrentValue = Entry(source).Property(keyName).CurrentValue;
}
}
2) Then on my seed code:
var entityList = new List<MyExempleEntity>()
{
new MyExampleEntity { Prop1 = "a p1", Prop2 = "a p2" },
new MyExampleEntity { Prop1 = "b p1", Prop2 = "b p2" },
new MyExampleEntity { Prop1 = "c p1", Prop2 = "c p2" },
}
foreach(var item in entityList)
{
context.CustomAddOrUpdate<MyExampleEntity>(x => x.Prop1 == item.Prop1 && x.Prop2 == item.Prop2, item);
}
context.SaveChanges()
3) And to wrap up, here you are the code to get the KeyProperties from an entity:
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace System.Data.Entity
{
public static class DbContextExtensions
{
public static string[] GetKeyNames<TEntity>(this DbContext context)
where TEntity : class
{
return context.GetKeyNames(typeof(TEntity));
}
public static string[] GetKeyNames(this DbContext context, Type entityType)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
MetadataWorkspace metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
// Get the mapping between CLR types and metadata OSpace
var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));
// Get metadata for given CLR type
var entityMetadata = metadata
.GetItems<EntityType>(DataSpace.OSpace)
.Single(e => objectItemCollection.GetClrType(e) == entityType);
return entityMetadata.KeyProperties.Select(p => p.Name).ToArray();
}
}
}
The above code was grabbed from this blog:
https://romiller.com/2014/10/07/ef6-1-getting-key-properties-for-an-entity/

Entity Framework , how to only validate specify property

I have a demo class "User" like the following:
public partial class User {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[StringLength(30)]
[Required]
public string LoginName { get; set; }
[StringLength(120)]
[Required]
[DataType(DataType.Password)]
public string Pwd { get; set; }
[StringLength(50)]
public string Phone { get; set; }
[StringLength(100)]
public string WebSite { get; set; }
...
...
}
As you can see, "LoginName" and "Pwd" are "Required".
Some time , I only want to update user's "WebSite" , So I do like this:
public void UpdateUser(User user , params string[] properties) {
this.rep.DB.Users.Attach(user);
this.rep.DB.Configuration.ValidateOnSaveEnabled = false;
var entry = this.rep.DB.Entry(user);
foreach(var prop in properties) {
var entProp = entry.Property(prop);
//var vas = entProp.GetValidationErrors();
entProp.IsModified = true;
}
this.rep.DB.SaveChanges();
this.rep.DB.Configuration.ValidateOnSaveEnabled = true;
}
Parameter "user" like this:
new User(){
ID = 1,
WebSite = "http://www.stackoverflow.com"
}
Notice , I don't specify "LoginName" and "Pwd"
This function can work fine , but I wouldn't set ValidateOnSaveEnabled to false.
Is there any way only validate "WebSite" when ValidateOnSaveEnabled is true?
Thanks.
As I know validation executed in SaveChanges always validates the whole entity. The trick to get selective validation for property is commented in your code but it is not part of the SaveChanges operation.
I get a solution.
First define PartialValidationManager:
public class PartialValidationManager {
private IDictionary<DbEntityEntry , string[]> dics = new Dictionary<DbEntityEntry , string[]>();
public void Register(DbEntityEntry entry , string[] properties) {
if(dics.ContainsKey(entry)) {
dics[entry] = properties;
} else {
dics.Add(entry , properties);
}
}
public void Remove(DbEntityEntry entry) {
dics.Remove(entry);
}
public bool IsResponsibleFor(DbEntityEntry entry) {
return dics.ContainsKey(entry);
}
public void ValidateEntity(DbEntityValidationResult result) {
var entry = result.Entry;
foreach(var prop in dics[entry]){
var errs = entry.Property(prop).GetValidationErrors();
foreach(var err in errs) {
result.ValidationErrors.Add(err);
}
}
}
}
2, Add this Manager to My DbContext:
public class XmjDB : DbContext {
public Lazy<PartialValidationManager> PartialValidation = new Lazy<PartialValidationManager>();
protected override System.Data.Entity.Validation.DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry , IDictionary<object , object> items) {
if(this.PartialValidation.Value.IsResponsibleFor(entityEntry)) {
var result = new DbEntityValidationResult(entityEntry , new List<DbValidationError>());
this.PartialValidation.Value.ValidateEntity(result);
return result;
} else
return base.ValidateEntity(entityEntry , items);
}
...
...
Update Method :
public void UpateSpecifyProperties(T t, params string[] properties) {
this.DB.Set<T>().Attach(t);
var entry = this.DB.Entry<T>(t);
this.DB.PartialValidation.Value.Register(entry , properties);
foreach(var prop in properties) {
entry.Property(prop).IsModified = true;
}
this.DB.SaveChanges();
this.DB.PartialValidation.Value.Remove(entry);
}
Ok, it work fine.