동일한 키를 가진 이 유형의 다른 인스턴스가 이미 추적 중이므로 엔티티 유형의 인스턴스를 추적할 수 없습니다.
서비스 오브젝트가 있다Update
public bool Update(object original, object modified)
{
var originalClient = (Client)original;
var modifiedClient = (Client)modified;
_context.Clients.Update(originalClient); //<-- throws the error
_context.SaveChanges();
//Variance checking and logging of changes between the modified and original
}
여기서 이 메서드를 호출합니다.
public IActionResult Update(DetailViewModel vm)
{
var originalClient = (Client)_service.GetAsNoTracking(vm.ClientId);
var modifiedClient = (Client)_service.Fetch(vm.ClientId.ToString());
// Changing the modifiedClient here
_service.Update(originalClient, modifiedClient);
}
여기 있습니다GetAsNotTracking
방법:
public Client GetAsNoTracking(long id)
{
return GetClientQueryableObject(id).AsNoTracking().FirstOrDefault();
}
Fetch
방법:
public object Fetch(string id)
{
long fetchId;
long.TryParse(id, out fetchId);
return GetClientQueryableObject(fetchId).FirstOrDefault();
}
GetClientQueryableObject
:
private Microsoft.Data.Entity.Query.IIncludableQueryable<Client, ActivityType> GetClientQueryableObject(long searchId)
{
return _context.Clients
.Where(x => x.Id == searchId)
.Include(x => x.Opportunities)
.ThenInclude(x => x.BusinessUnit)
.Include(x => x.Opportunities)
.ThenInclude(x => x.Probability)
.Include(x => x.Industry)
.Include(x => x.Activities)
.ThenInclude(x => x.User)
.Include(x => x.Activities)
.ThenInclude(x => x.ActivityType);
}
좋은 생각 있어요?
아래 기사/토론을 살펴보았습니다.사용할 수 없음:ASP.NET GitHub 3839호
갱신:
다음은 에 대한 변경 사항입니다.GetAsNoTracking
:
public Client GetAsNoTracking(long id)
{
return GetClientQueryableObjectAsNoTracking(id).FirstOrDefault();
}
GetClientQueryableObjectAsNoTracking
:
private IQueryable<Client> GetClientQueryableObjectAsNoTracking(long searchId)
{
return _context.Clients
.Where(x => x.Id == searchId)
.Include(x => x.Opportunities)
.ThenInclude(x => x.BusinessUnit)
.AsNoTracking()
.Include(x => x.Opportunities)
.ThenInclude(x => x.Probability)
.AsNoTracking()
.Include(x => x.Industry)
.AsNoTracking()
.Include(x => x.Activities)
.ThenInclude(x => x.User)
.AsNoTracking()
.Include(x => x.Activities)
.ThenInclude(x => x.ActivityType)
.AsNoTracking();
}
EF 트랙 시스템을 재정의하지 않고 저장하기 전에 '로컬' 항목을 분리하고 업데이트된 항목을 첨부할 수도 있습니다.
//
var local = _context.Set<YourEntity>()
.Local
.FirstOrDefault(entry => entry.Id.Equals(entryId));
// check if local is not null
if (local != null)
{
// detach
_context.Entry(local).State = EntityState.Detached;
}
// set Modified flag in your entry
_context.Entry(entryToUpdate).State = EntityState.Modified;
// save
_context.SaveChanges();
업데이트: 코드 중복을 피하기 위해 확장 방법을 수행할 수 있습니다.
public static void DetachLocal<T>(this DbContext context, T t, string entryId)
where T : class, IIdentifier
{
var local = context.Set<T>()
.Local
.FirstOrDefault(entry => entry.Id.Equals(entryId));
if (!local.IsNull())
{
context.Entry(local).State = EntityState.Detached;
}
context.Entry(t).State = EntityState.Modified;
}
나의IIdentifier
인터페이스에는Id
string 속성.
엔티티에 관계없이 컨텍스트에서 다음 방법을 사용할 수 있습니다.
_context.DetachLocal(tmodel, id);
_context.SaveChanges();
public async Task<Product> GetValue(int id)
{
Product Products = await _context.Products
.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
return Products;
}
As No Tracking()
중요:사용.AsNoTracking()
많은 맥락에서 부적처럼 작용합니다.단, 다음과 같은 프레임워크에서 조롱 또는 스텁 전략을 사용하는 경우 유닛 테스트는 실패합니다.Moq
MSDN의 공식 설명:
하지만, 적절히 조롱하는 것은
DbSet
쿼리는 LINQ 연산자를 통해 표현되기 때문에 쿼리 기능을 사용할 수 없습니다.이것은 정적 확장 메서드 호출입니다.IQueryable
그 결과, '모킹'에 대해 말하는 사람이 있다.DbSet
"그들의 진정한 의미는 그들이 창조하는 것이DbSet
메모리 내 컬렉션에 의해 백업된 후 쿼리 연산자를 메모리 내 컬렉션과 비교하여 평가합니다.IEnumerable
실제 데이터베이스를 인메모리 컬렉션이 대체하는 일종의 가짜입니다.
모의고사 전략으로 유닛테스트를 시도할 때는 향후의 문제를 회피합니다.Microsoft 의 메뉴얼에 기재되어 있듯이, 레포지토리 패턴의 실장등의 유닛 테스트 방법이 있습니다.
나는 이것이 문제를 해결했다.업데이트 전에 이 코드 추가
_context.ChangeTracker.Clear()
Microsoft 매뉴얼에서
현재 추적되고 있는 모든 엔티티 추적을 중지합니다.
DbContext는 각 작업단위에 대해 새 인스턴스가 생성되는 짧은 수명을 갖도록 설계되었습니다.이 방법은 컨텍스트가 각 작업 유닛의 마지막에 폐기되면 모든 추적 대상 엔티티가 폐기됨을 의미합니다.단, 이 방법을 사용하여 추적 대상 엔티티를 모두 클리어하는 것은 새로운 컨텍스트인스턴스를 작성할 수 없는 경우에 도움이 될 수 있습니다.
이 방법은 항상 모든 추적 대상 엔티티를 분리하는 것보다 우선해야 합니다.엔티티를 분리하는 것은 느린 프로세스로 부작용이 발생할 수 있습니다.이 방법은 컨텍스트에서 모든 추적 대상 엔티티를 클리어하는 데 훨씬 효율적입니다.
엔티티가 개별적으로 분리되지 않으므로 이 메서드는 StateChanged 이벤트를 생성하지 않습니다.
갱신하다
이 경우 테이블의 id 열이 Identity 열로 설정되지 않았습니다.
xUnit 테스트 셋업 중에 같은 문제(EF Core)가 발생했습니다.테스트에서 시드 데이터를 설정한 후 변경 트래커 엔티티를 루핑하는 것을 '고정'했습니다.
- Seed AppDbContext() 메서드의 맨 아래에 있습니다.
테스트 모의 컨텍스트를 설정했습니다.
/// <summary>
/// Get an In memory version of the app db context with some seeded data
/// </summary>
public static AppDbContext GetAppDbContext(string dbName)
{
//set up the options to use for this dbcontext
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: dbName)
//.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.Options;
var dbContext = new AppDbContext(options);
dbContext.SeedAppDbContext();
return dbContext;
}
일부 시드 데이터를 추가하는 확장 방법:
- 엔티티를 분리하다
foreach
메서드 하단의 루프가 있습니다.
public static void SeedAppDbContext(this AppDbContext appDbContext)
{
// add companies
var c1 = new Company() { Id = 1, CompanyName = "Fake Company One", ContactPersonName = "Contact one", eMail = "one@caomp1.com", Phone = "0123456789", AdminUserId = "" };
c1.Address = new Address() { Id = 1, AddressL1 = "Field Farm", AddressL2 = "Some Lane", City = "some city", PostalCode = "AB12 3CD" };
appDbContext.CompanyRecords.Add(c1);
var nc1 = new Company() { Id = 2, CompanyName = "Test Company 2", ContactPersonName = "Contact two", eMail = "two@comp2.com", Phone = "0123456789", Address = new Address() { }, AdminUserId = "" };
nc1.Address = new Address() { Id = 2, AddressL1 = "The Barn", AddressL2 = "Some Lane", City = "some city", PostalCode = "AB12 3CD" };
appDbContext.CompanyRecords.Add(nc1);
//....and so on....
//last call to commit everything to the memory db
appDbContext.SaveChanges();
//and then to detach everything
foreach (var entity in appDbContext.ChangeTracker.Entries())
{
entity.State = EntityState.Detached;
}
}
컨트롤러 입력 방식
메서드는 ServiceStack에서 확장 메서드입니다.
[HttpPut]
public async Task<IActionResult> PutUpdateCompany(CompanyFullDto company)
{
if (0 == company.Id)
return BadRequest();
try
{
Company editEntity = company.ConvertTo<Company>();
//Prior to detaching an error thrown on line below (another instance with id)
var trackedEntity = _appDbContext.CompanyRecords.Update(editEntity);
await _appDbContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException dbError)
{
if (!CompanyExists(company.Id))
return NotFound();
else
return BadRequest(dbError);
}
catch (Exception Error)
{
return BadRequest(Error);
}
return Ok();
}
테스트:
[Fact]
public async Task PassWhenEditingCompany()
{
var _appDbContext = AppDbContextMocker.GetAppDbContext(nameof(CompaniesController));
var _controller = new CompaniesController(null, _appDbContext);
//Arrange
const string companyName = "Fake Company One";
const string contactPerson = "Contact one";
const string newCompanyName = "New Fake Company One";
const string newContactPersonName = "New Contact Person";
//Act
var getResult = _controller.GetCompanyById(1);
var getEntity = (getResult.Result.Result as OkObjectResult).Value;
var entityDto = getEntity as CompanyFullDto;
//Assert
Assert.Equal(companyName, entityDto.CompanyName);
Assert.Equal(contactPerson, entityDto.ContactPersonName);
Assert.Equal(1, entityDto.Id);
//Arrange
Company entity = entityDto.ConvertTo<Company>();
entity.CompanyName = newCompanyName;
entity.ContactPersonName = newContactPersonName;
CompanyFullDto entityDtoUpd = entity.ConvertTo<CompanyFullDto>();
//Act
var result = await _controller.PutUpdateCompany(entityDtoUpd) as StatusCodeResult;
//Assert
Assert.True(result.StatusCode == 200);
//Act
getResult = _controller.GetCompanyById(1);
getEntity = (getResult.Result.Result as OkObjectResult).Value;
entityDto = getEntity as CompanyFullDto;
//Assert
Assert.Equal(1, entityDto.Id); // didn't add a new record
Assert.Equal(newCompanyName, entityDto.CompanyName); //updated the name
Assert.Equal(newContactPersonName, entityDto.ContactPersonName); //updated the contact
//make sure to dispose of the _appDbContext otherwise running the full test will fail.
_appDbContext.Dispose();
}
실제로는 추적되지 않은 모델을 메모리에 저장하는 것이 아니라 모델에 대한 변경 사항을 추적하려는 것처럼 들립니다.문제를 완전히 제거할 다른 방법을 제안해도 될까요?
EF는 자동으로 변경 사항을 추적합니다.그 내장 논리를 활용하는 것은 어떻습니까?
「」를 .SaveChanges()
안에서DbContext
.
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<Client>())
{
if (entry.State == EntityState.Modified)
{
// Get the changed values.
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
}
return base.SaveChanges();
}
여기서 좋은 예를 찾을 수 있습니다.
MVC 및 엔티티 프레임워크를 사용한 감사 로그/변경 이력 구현
★★★★★★ Client
이치노 '우리'라고 합시다.ITrackableEntity
이렇게 하면 로직을 일원화하여 특정 인터페이스를 구현하는 모든 엔티티에 모든 변경을 자동으로 기록할 수 있습니다.인터페이스 자체에는 특정 속성이 없습니다.
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<ITrackableClient>())
{
if (entry.State == EntityState.Modified)
{
// Same code as example above.
}
}
return base.SaveChanges();
}
또한 실제로 Save Changes()를 덮어쓰지 않고 구독하는 eranga의 훌륭한 제안도 살펴보세요.
EF 코어 - 또한 외부 키와 외부 키 탐색 속성을 모두 설정하지 마십시오.키와 속성을 모두 설정했을 때 이 오류가 발생했습니다.
예.
new VerificationAccount()
{
Account = konto_1630,
VerificationRowType = VerificationRowType.Template,
// REMOVED THE LINE BELOW AND THE ERROR WENT AWAY
//VerificationAccount = verificationAccounts.First(x => x.Account == konto_1630),
VerificationId = verificationId
}
이런 문제도 있었고..저장하기 전에 먼저 오래된 엔티티의 추적을 풀어서 해결했습니다.
public async Task<int> Update<T>(T entity) where T : BaseEntity
{
entity.UpdatedAt = DateTime.UtcNow;
//Untrack previous entity version
var trackedEntity = this.context.Set<T>().SingleOrDefaultAsync(e => e.Id == entity.Id);
this.context.Entry<T>(await trackedEntity).State = EntityState.Detached;
//Track new version
this.context.Set<T>().Attach(entity);
this.context.Entry<T>(entity).State = EntityState.Modified;
await this.context.SaveChangesAsync();
return entity.Id;
}
다음과 같이 엔티티를 저장 후 분리하도록 설정할 수 있습니다.
public async Task<T> Update(int id, T entity)
{
entity.Id = id;
_ctx.Set<T>().Update(entity);
await _ctx.SaveChangesAsync();
_ctx.Entry(entity).State = EntityState.Detached; //detach saved entity
return entity;
}
저는 Auto Mapper 및 를 사용하는 동안 이 문제가 발생했습니다.NET 6. 이 문제를 해결하기 위해 코드를 다음과 같이 변경했습니다.
DbItem? result = await _dbContext.DbItems.FirstOrDefaultAsync(t => t.Id == id);
if (result == null)
{
return null;
}
DbItem mappedItem = _mapper.Map<DbItem>(dto); //problematic line
var updatedItem = _dbContext.DbItems.Update(mappedItem);
수신인:
DbItem? result = await _dbContext.DbItems.FirstOrDefaultAsync(t => t.Id == id);
if (result == null)
{
return null;
}
_mapper.Map(dto, result); //the fix
var updatedItem = _dbContext.DbItems.Update(result);
문제가 있는 행으로 인해 동일한 키 값을 가진 새 DbItem이 생성되어 문제가 발생하였습니다.수정 행은 DTO의 필드를 원래 DbItem에 매핑합니다.
이 에러는 백그라운드 서비스에서 받았습니다.나는 새로운 범위를 만드는 것을 해결했다.
using (var scope = serviceProvider.CreateScope())
{
// Process
}
'Id' 또는 열 이름이 같은 두 개 이상의 테이블을 설정한 경우 컨텍스트 클래스에서 OnModelCreating 메서드를 변경하는 것이 가장 쉬운 방법입니다.
이 경우 'Id'를 'AbandonedCartId'로 변경하고 오브젝트의 열 이름이 'Id'임을 엔티티에 알려야 합니다.
entity.Property(e => e.AbandonedCartId).HasColumnName("Id");
예
public partial class AbandonedCart
{
public int AbandonedCartId { get; set; }
public double? CheckoutId { get; set; }
public int? AppId { get; set; }
public double? CustomerId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AbandonedCart>(entity =>
{
entity.Property(e => e.AbandonedCartId).HasColumnName("Id");
entity.Property(e => e.CreatedAt).HasColumnType("datetime");
entity.HasOne(d => d.App)
.WithMany(p => p.AbandonedCart)
.HasForeignKey(d => d.AppId)
.HasConstraintName("FK_AbandonedCart_Apps");
});
}
에러 메세지에 나타나 있듯이, 2개의 엔티티가 추적되고 있습니다.업데이트 작업을 수행하는 경우:
- 엔티티를 취득하다
- 엔티티의 속성을 설정하여 엔티티를 업데이트합니다.해당 엔티티에 대해 새로 작업을 수행하지 마십시오.그러면 두 개의 엔티티가 생성됩니다.
아, 이게 저를 곤란하게 했고, 트러블 슈팅에 많은 시간을 할애했습니다.문제는 Parellel(XUnit의 디폴트)에서 테스트가 실행되고 있었다는 것입니다.
테스트를 순차적으로 실행하기 위해 각 클래스를 다음 속성으로 장식했습니다.
[Collection("Sequential")]
내가 생각한 방법은 이렇다. (병행이 아닌) 순차적으로 유닛 테스트 실행
GenFu를 사용하여 EF In Memory 컨텍스트를 시뮬레이션합니다.
private void CreateTestData(TheContext dbContext)
{
GenFu.GenFu.Configure<Employee>()
.Fill(q => q.EmployeeId, 3);
var employee = GenFu.GenFu.ListOf<Employee>(1);
var id = 1;
GenFu.GenFu.Configure<Team>()
.Fill(p => p.TeamId, () => id++).Fill(q => q.CreatedById, 3).Fill(q => q.ModifiedById, 3);
var Teams = GenFu.GenFu.ListOf<Team>(20);
dbContext.Team.AddRange(Teams);
dbContext.SaveChanges();
}
테스트 데이터를 작성할 때, 내가 차감할 수 있는 범위에서 테스트 데이터는 두 가지 범위에서 활성화되어 있었습니다(팀 테스트 실행 중 직원 테스트에서 한 번).
public void Team_Index_should_return_valid_model()
{
using (var context = new TheContext(CreateNewContextOptions()))
{
//Arrange
CreateTestData(context);
var controller = new TeamController(context);
//Act
var actionResult = controller.Index();
//Assert
Assert.NotNull(actionResult);
Assert.True(actionResult.Result is ViewResult);
var model = ModelFromActionResult<List<Team>>((ActionResult)actionResult.Result);
Assert.Equal(20, model.Count);
}
}
두 테스트 클래스를 이 순차적 컬렉션 특성으로 래핑하면 명백한 충돌이 지워졌습니다.
[Collection("Sequential")]
기타 참고 자료:
https://github.com/aspnet/EntityFrameworkCore/issues/7340
EF Core 2.1 메모리 DB가 레코드를 업데이트하지 않음
http://www.jerriepelser.com/blog/unit-testing-aspnet5-entityframework7-inmemory-database/
http://gunnarpeipman.com/2017/04/aspnet-core-ef-inmemory/
https://github.com/aspnet/EntityFrameworkCore/issues/12459
유닛 테스트에서 EF Core SqlLite 사용 시 트래킹 문제 방지
저도 같은 문제에 직면했지만, 그 문제는 매우 어리석었습니다. 실수로 두 아이디 사이의 관계를 잘못 부여했습니다.
public static void DetachEntity<T>(this DbContext dbContext, T entity, string propertyName) where T: class, new()
{
try
{
var dbEntity = dbContext.Find<T>(entity.GetProperty(propertyName));
if (dbEntity != null)
dbContext.Entry(dbEntity).State = EntityState.Detached;
dbContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception)
{
throw;
}
}
public static object GetProperty<T>(this T entity, string propertyName) where T : class, new()
{
try
{
Type type = entity.GetType();
PropertyInfo propertyInfo = type.GetProperty(propertyName);
object value = propertyInfo.GetValue(entity);
return value;
}
catch (Exception)
{
throw;
}
}
이 두 가지 연장 방법을 만들었는데, 정말 잘 되고 있어요.
DB 행을 업데이트할 수 없습니다.나도 같은 오류에 직면해 있었다.현재 다음 코드로 작업 중입니다.
_context.Entry(_SendGridSetting).CurrentValues.SetValues(vm);
await _context.SaveChangesAsync();
이 에러 메시지는, 엔티티가 중복해, SaveChanges() 를 실행하고 있는 경우에 발생할 가능성이 있습니다.
에도 같은 , 했습니다.AddDbContext
AddDbContextFactory
이치노
이 문제를 해결한 방법은 다음과 같습니다.
록록ing inginging AddDbContext
AddDbContextFactory
신음음하다
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<YourApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("YourDatabaseConnectionString")));
}
중요한 주의사항:
등록 안 함AddDbContext
★★★★★★★★★★★★★★★★★」AddDbContextFactory
하다System.AggregateException: 'Some services are not able to be constructed...'
exception
을 사용하다AddDbContextFactory
★★★★★★ 。
의 ★★★★★★★★★★★★★★★★★.ApplicationDbContext
는 공공 .DbContextOptions<YourApplicationDbContext>
츠키다
public class YourApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<YourApplicationDbContext> options): base(options){}
}
DbContextFactory
그런 다음 다음과 같이 컨스트럭터 주입을 통해 공장을 사용할 수 있습니다.
private readonly IDbContextFactory<YourApplicationDbContext> dbContextFactory;
public YourConstructor(IDbContextFactory<YourApplicationDbContext> dbContextFactory)
{
dbContextFactory = dbContextFactory;
}
또는
public YourController(IDbContextFactory<YourApplicationDbContext> dbContextFactory)
{
dbContextFactory = dbContextFactory;
}
그런 다음 주입된 공장을 사용하여DbContext
다음과 같이 서비스 코드에 인스턴스가 포함되어 있습니다.
using (var context = dbContextFactory.CreateDbContext())
{
// your database CRUD code comes in here... for example:
context.DatabaseTable.Update(suppliedModel);
await context.SaveChangesAsync();
}
이 옵션을 고려할 수 있는 경우: 컨텍스트 유형을 직접 등록하는 대신 팩토리를 등록하면 새 DbContext 인스턴스를 쉽게 만들 수 있습니다.Blazor 어플리케이션에도 추천합니다.
나는 이것이 이 문제에 직면한 누군가에게 도움이 되기를 바란다.건배!
저도 이런 문제가 있었어요.Entity Framework는 데이터베이스에 삽입하는 모든 개체를 추적합니다.따라서 몇 개의 필드가 변경된 동일한 개체의 중복 레코드를 삽입하면 EF가 이 오류를 발생시킵니다.다시 삽입하려는 개체를 자세히 복제하여 복구했습니다.
public static T DeepClone<T>(this T a)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, a);
stream.Position = 0;
return (T)formatter.Deserialize(stream);
}
}
그 후, 다음과 같이 합니다.
var cloned = objectYouAreTryingToReinsert.deepClone();
context.objects.add(cloned);
await context.SaveChangesAsync();
임의의 키 유형에 대해 엔티티를 분리하는 확장 메서드입니다.
public static void Detach<TEntry, TId>(this DbContext context, Func<TEntry, TId> idReader, TId id)
where TEntry : class
where TId : IEquatable<TId>
{
var local = context.Set<TEntry>()
.Local
.FirstOrDefault(entry => idReader(entry).Equals(id));
if (local != null)
{
context.Entry(local).State = EntityState.Detached;
}
}
GUID 키 사용:
dbContext.Detach<EntryType, Guid>(e => e.Id, myEntity.Id);
dbContext.Attach(myEntity);
저는 내비게이션 속성에서 동일한 엔티티를 가지고 있습니다.
예.
class Entity1
{
public ICollection<Entity2> Entities2 { get; set; } // <-- problem
}
class Entity2
{
public Entity1 Ent { get; set; } // <-- problem
}
솔루션
Entity1 e = ...
e.Entities2 = null;
db.SaveChanges();
데이터가 매번 변경되면 표를 추적하지 않아도 됩니다.예를 들어 tigger를 사용하여 테이블 업데이트 ID([key])를 지정합니다.트레이스를 하면, 같은 ID가 취득되어 문제가 발생합니다.
언급URL : https://stackoverflow.com/questions/36856073/the-instance-of-entity-type-cannot-be-tracked-because-another-instance-of-this-t
'programing' 카테고리의 다른 글
Git serve : 그렇게 심플하게 하고 싶다 (0) | 2023.04.22 |
---|---|
뮤텍스와 크리티컬 섹션의 차이점은 무엇입니까? (0) | 2023.04.22 |
Git 별칭 나열 (0) | 2023.04.22 |
윈도우즈 10에서 환경 변수가 너무 큽니다. (0) | 2023.04.22 |
WPF MVVM: 창을 닫는 방법 (0) | 2023.04.22 |