Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何避免使用Dapper.Contrib在后续更新调用中更新列?_C#_Dapper_Dapper Contrib - Fatal编程技术网

C# 如何避免使用Dapper.Contrib在后续更新调用中更新列?

C# 如何避免使用Dapper.Contrib在后续更新调用中更新列?,c#,dapper,dapper-contrib,C#,Dapper,Dapper Contrib,我有一个类Media,它与我的DB表Media基本上是一对一的。我正在使用Dapper.Contrib的UpdateSqlConnection扩展方法更新表,方法是传入Media对象 表中的一列(以及相应的类属性)是OwnerID,只是它是第一个创建此媒体的用户的ID。它应该写在第一个insert上(这也是使用Dapper.Contrib的insertextension方法完成的),然后更新不应该改变它。有没有可能只用简洁的设计就可以做到这一点?如果可能的话,我不想在执行更新之前阅读OwnerI

我有一个类
Media
,它与我的DB表
Media
基本上是一对一的。我正在使用Dapper.Contrib的
Update
SqlConnection
扩展方法更新表,方法是传入
Media
对象

表中的一列(以及相应的类属性)是
OwnerID
,只是它是第一个创建此媒体的用户的ID。它应该写在第一个insert上(这也是使用Dapper.Contrib的
insert
extension方法完成的),然后更新不应该改变它。有没有可能只用简洁的设计就可以做到这一点?如果可能的话,我不想在执行更新之前阅读
OwnerID
列,只是为了确保
OwnerID
对象属性相同


[Computed]
属性似乎在
Update
Insert
中都忽略了此列。描述似乎表明此属性只应在更新时忽略对列的写入,这给了我希望,我只是不正确地使用了库。

不幸的是,衣冠楚楚,Contrib不能做你想做的事。要点是,根据扩展方法
Insert
Update
收集字段的机制完全相同,这将受到影响。基本上它看起来像:

// in Insert<T>
var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties)).ToList();

...

// in Update<T>
var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)).ToList();
以下是标记属性的属性,这些属性不能用于更新查询:

/// <summary>
/// Specifies that this is a not updateable column.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class NotUpdateableAttribute : Attribute
{
}

希望能有所帮助。

谢谢,我不会选择使用扩展方法,而是将此标记为答案,因为它解释了Dapper.Contrib无法实现的原因,并说明了如何通过编写自己的扩展方法来实现。
CREATE TABLE [Media]
(
    [Id]           INT IDENTITY (1, 1) NOT NULL,
    [OwnerId]      BIGINT              NOT NULL,    
    [Name]         VARCHAR(50)         NOT NULL
)
/// <summary>
/// Specifies that this is a not updateable column.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class NotUpdateableAttribute : Attribute
{
}
/// <summary>
/// My extensions for Dapper
/// </summary>
public static class TestSqlMapperExtensions
{
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ExplicitKeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> UpdatableProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> GetQueries = new ConcurrentDictionary<RuntimeTypeHandle, string>();
    private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeTableName = new ConcurrentDictionary<RuntimeTypeHandle, string>();

    private static readonly ISqlAdapter DefaultAdapter = new SqlServerAdapter();
    private static readonly Dictionary<string, ISqlAdapter> AdapterDictionary
        = new Dictionary<string, ISqlAdapter>
        {
            ["sqlconnection"] = new SqlServerAdapter(),
            ["sqlceconnection"] = new SqlCeServerAdapter(),
            ["npgsqlconnection"] = new PostgresAdapter(),
            ["sqliteconnection"] = new SQLiteAdapter(),
            ["mysqlconnection"] = new MySqlAdapter(),
            ["fbconnection"] = new FbAdapter()
        };

    private static List<PropertyInfo> ComputedPropertiesCache(Type type)
    {
        if (ComputedProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
        {
            return pi.ToList();
        }

        var computedProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ComputedAttribute)).ToList();

        ComputedProperties[type.TypeHandle] = computedProperties;
        return computedProperties;
    }

    private static List<PropertyInfo> NotUpdateablePropertiesCache(Type type)
    {
        if (UpdatableProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
        {
            return pi.ToList();
        }

        var notUpdateableProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is NotUpdateableAttribute)).ToList();

        UpdatableProperties[type.TypeHandle] = notUpdateableProperties;
        return notUpdateableProperties;
    }

    private static List<PropertyInfo> ExplicitKeyPropertiesCache(Type type)
    {
        if (ExplicitKeyProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
        {
            return pi.ToList();
        }

        var explicitKeyProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute)).ToList();

        ExplicitKeyProperties[type.TypeHandle] = explicitKeyProperties;
        return explicitKeyProperties;
    }

    private static List<PropertyInfo> KeyPropertiesCache(Type type)
    {
        if (KeyProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pi))
        {
            return pi.ToList();
        }

        var allProperties = TypePropertiesCache(type);
        var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();

        if (keyProperties.Count == 0)
        {
            var idProp = allProperties.Find(p => string.Equals(p.Name, "id", StringComparison.CurrentCultureIgnoreCase));
            if (idProp != null && !idProp.GetCustomAttributes(true).Any(a => a is ExplicitKeyAttribute))
            {
                keyProperties.Add(idProp);
            }
        }

        KeyProperties[type.TypeHandle] = keyProperties;
        return keyProperties;
    }

    private static List<PropertyInfo> TypePropertiesCache(Type type)
    {
        if (TypeProperties.TryGetValue(type.TypeHandle, out IEnumerable<PropertyInfo> pis))
        {
            return pis.ToList();
        }

        var properties = type.GetProperties().Where(IsWriteable).ToArray();
        TypeProperties[type.TypeHandle] = properties;
        return properties.ToList();
    }

    private static bool IsWriteable(PropertyInfo pi)
    {
        var attributes = pi.GetCustomAttributes(typeof(WriteAttribute), false).AsList();
        if (attributes.Count != 1) return true;

        var writeAttribute = (WriteAttribute)attributes[0];
        return writeAttribute.Write;
    }



    private static string GetTableName(Type type)
    {
        if (TypeTableName.TryGetValue(type.TypeHandle, out string name)) return name;

        if (SqlMapperExtensions.TableNameMapper != null)
        {
            name = SqlMapperExtensions.TableNameMapper(type);
        }
        else
        {
            var info = type;
            //NOTE: This as dynamic trick falls back to handle both our own Table-attribute as well as the one in EntityFramework 
            var tableAttrName =
                info.GetCustomAttribute<TableAttribute>(false)?.Name
                ?? (info.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic)?.Name;

            if (tableAttrName != null)
            {
                name = tableAttrName;
            }
            else
            {
                name = type.Name + "s";
                if (type.IsInterface && name.StartsWith("I"))
                    name = name.Substring(1);
            }
        }

        TypeTableName[type.TypeHandle] = name;
        return name;
    }

    /// <summary>
    /// Updates entity in table "Ts", checks if the entity is modified if the entity is tracked by the Get() extension.
    /// </summary>
    /// <typeparam name="T">Type to be updated</typeparam>
    /// <param name="connection">Open SqlConnection</param>
    /// <param name="entityToUpdate">Entity to be updated</param>
    /// <param name="transaction">The transaction to run under, null (the default) if none</param>
    /// <param name="commandTimeout">Number of seconds before command execution timeout</param>
    /// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
    public static bool MyUpdate<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
    {
        if (entityToUpdate is Dapper.Contrib.Extensions.SqlMapperExtensions.IProxy proxy && !proxy.IsDirty)
        {
            return false;
        }

        var type = typeof(T);

        if (type.IsArray)
        {
            type = type.GetElementType();
        }
        else if (type.IsGenericType)
        {
            var typeInfo = type.GetTypeInfo();
            bool implementsGenericIEnumerableOrIsGenericIEnumerable =
                typeInfo.ImplementedInterfaces.Any(ti => ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) ||
                typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>);

            if (implementsGenericIEnumerableOrIsGenericIEnumerable)
            {
                type = type.GetGenericArguments()[0];
            }
        }

        var keyProperties = KeyPropertiesCache(type).ToList();  //added ToList() due to issue #418, must work on a list copy
        var explicitKeyProperties = ExplicitKeyPropertiesCache(type);
        if (keyProperties.Count == 0 && explicitKeyProperties.Count == 0)
            throw new ArgumentException("Entity must have at least one [Key] or [ExplicitKey] property");

        var name = GetTableName(type);

        var sb = new StringBuilder();
        sb.AppendFormat("update {0} set ", name);

        var allProperties = TypePropertiesCache(type);
        keyProperties.AddRange(explicitKeyProperties);
        var computedProperties = ComputedPropertiesCache(type);

        // Exclude not updateable fields
        var notUpdateableProperties = NotUpdateablePropertiesCache(type);
        var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties).Union(notUpdateableProperties)).ToList();

        var adapter = GetFormatter(connection);

        for (var i = 0; i < nonIdProps.Count; i++)
        {
            var property = nonIdProps[i];
            adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336
            if (i < nonIdProps.Count - 1)
                sb.Append(", ");
        }
        sb.Append(" where ");
        for (var i = 0; i < keyProperties.Count; i++)
        {
            var property = keyProperties[i];
            adapter.AppendColumnNameEqualsValue(sb, property.Name);  //fix for issue #336
            if (i < keyProperties.Count - 1)
                sb.Append(" and ");
        }
        var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);
        return updated > 0;
    }

    private static ISqlAdapter GetFormatter(IDbConnection connection)
    {
        var name = SqlMapperExtensions.GetDatabaseType?.Invoke(connection).ToLower()
                   ?? connection.GetType().Name.ToLower();

        return !AdapterDictionary.ContainsKey(name)
            ? DefaultAdapter
            : AdapterDictionary[name];
    }


}