替换文本字符串时出现奇怪的C#错误

本文关键字:错误 文本 字符串 替换 | 更新日期: 2023-09-27 18:23:49

我们有一个注册类型的系统,完成后会发送一封确认电子邮件。该系统在几分钟内有大约3000个注册,我们注意到了一个错误。如果用户A在用户B注册后几毫秒注册,则用户A将通过电子邮件获取用户B的详细信息。我们确实解决了这个问题,我将其缩小到这段代码中,该代码从缓存中获取电子邮件模板,并在占位符上进行字符串替换。

private string ProcessEmailBody(MyRegistrationModel registration)
{
    var content = CacheHelper.GetContent("REGISTRATIONEMAIL");
    if (content != null)
    {
        content.Text = context.Text.Replace("@@FULL_NAME@@", registration.FullName);
        return content.Text;
    }
    else return null;
}

CacheHelper.GetContent()方法是静态的,我通过以下操作修复了这个"错误":

private string ProcessEmailBody(MyRegistrationModel registration)
{
    var content = CacheHelper.GetContent("REGISTRATIONEMAIL");
    if (content != null)
    {
        string body = content.Text;
        body = body.Replace("@@FULL_NAME@@", registration.FullName);
        return body;
    }
    else return null;
}

我一辈子都不明白为什么这能解决这个问题。有人能说明这件事吗?

EDIT:这是我的GetContent()方法(我知道签名与上面的不同,我只是简短地说)

public static Content GetContent(string key, int partnerSiteId, int? version, IContentRepository contentRepository, out string cacheKey)
{
    cacheKey = string.Format("{0}_{1}_{2}", key, partnerSiteId, version);
    var content = CacheManager.Get(cacheKey, () => contentRepository.GetContent(key, partnerSiteId, version), WebConfig.GetCacheDuration(CacheProfile.Short));
    return content;
}
private static DataCache _Cache = null; // DataCache is from AppFabric (Microsoft.ApplicationServer.Caching)
public static T Get<T>(string objectKey, Func<T> reloadItemExpresion, TimeSpan cacheDuration) where T : class
{
    if (_Cache == null)
    {
        if (reloadItemExpresion != null)
        {
            return reloadItemExpresion.Invoke();
        }
        return null;
    }
    object cachedObject = null;
    try
    {
        cachedObject = _Cache.Get(objectKey);
    }
    catch (Exception ex)
    {
        if (ex is FileNotFoundException)
        {
            _Cache.Remove(objectKey);
        }
    }
    if (cachedObject != null)
    {
        return cachedObject as T;
    }
    if (reloadItemExpresion != null && cacheDuration > TimeSpan.Zero)
    {
        T item = reloadItemExpresion.Invoke();
        if (item != null)
        {
            Insert(item, objectKey, cacheDuration);
        }
        return item;
    }
    return null;
}

contentRepository.GetContent只是转到数据库并取回实际内容。

替换文本字符串时出现奇怪的C#错误

第一次通过将context.Text中的"@@FULL_NAME@@"标记替换为第一个用户的详细信息。一旦你这样做了,它就再也不会回到"@@FULL_NAME@@",所以每个人都会得到那个家伙的详细信息,直到你的缓存被重置。您应该避免修改从缓存中获得的对象:

private string ProcessEmailBody(MyRegistrationModel registration) {
    var content = CacheHelper.GetContent("REGISTRATIONEMAIL");
    return content != null ? content.Replace("@@FULL_NAME@@", registration.FullName) : null;
}

问题是,您的content对象是每个访问它的人之间的共享对象。通过使用第一种方法content.Text = context.Text.Replace(),您将为同时访问它的每个人更改文本。

在第二种方法中,您不会更改共享对象中的文本,因此每个人都可以同时获得自己的文本。为了避免将来出现此问题,您应该考虑使content.Text属性对使用者只读(通过允许仅在构造函数中设置文本或仅提供具有只读访问权限的接口)。这样即使在编译时也可以避免这个错误。

如果不知道CacheHelper方法是如何工作的,或者content是什么类型,很难说。但它似乎没有返回字符串,而是通过引用返回某种Content对象。因此,如果两个线程同时运行,那么两个线程都可能使用CacheHelper返回的同一Content对象。

假设CacheHelper负责不在每次调用时创建一个全新的内容模板,那么您的原始代码就有缺陷了,因为每次Replace调用都会更改template,而不是从中派生的字符串

我猜这个代码也会起作用:

string body = content.Text.Replace("@@FULL_NAME@@", registration.FullName);
return body;

重要的一点不是将内容的Text读取到局部变量中,而是替换内容的Text属性,该属性显然是共享的。