mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-26 17:40:30 +01:00
update item by name queries
This commit is contained in:
@@ -22,6 +22,7 @@ using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Persistence
|
||||
@@ -94,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
private IDbCommand _updateInheritedRatingCommand;
|
||||
private IDbCommand _updateInheritedTagsCommand;
|
||||
|
||||
public const int LatestSchemaVersion = 92;
|
||||
public const int LatestSchemaVersion = 95;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
|
||||
@@ -157,7 +158,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
"create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT, PRIMARY KEY (ItemId, UserDataKey))",
|
||||
"create index if not exists idx_UserDataKeys1 on UserDataKeys(ItemId)",
|
||||
|
||||
"create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT)",
|
||||
"create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT, CleanValue TEXT)",
|
||||
"create index if not exists idx_ItemValues on ItemValues(ItemId)",
|
||||
"create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)",
|
||||
|
||||
@@ -263,6 +264,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "SeriesName", "Text");
|
||||
|
||||
_connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
|
||||
_connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
|
||||
|
||||
string[] postQueries =
|
||||
{
|
||||
@@ -568,10 +570,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
_deleteItemValuesCommand.Parameters.Add(_deleteItemValuesCommand, "@Id");
|
||||
|
||||
_saveItemValuesCommand = _connection.CreateCommand();
|
||||
_saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value) values (@ItemId, @Type, @Value)";
|
||||
_saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)";
|
||||
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@ItemId");
|
||||
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Type");
|
||||
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Value");
|
||||
_saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@CleanValue");
|
||||
|
||||
// provider ids
|
||||
_deleteProviderIdsCommand = _connection.CreateCommand();
|
||||
@@ -905,7 +908,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
|
||||
UpdateImages(item.Id, item.ImageInfos, transaction);
|
||||
UpdateProviderIds(item.Id, item.ProviderIds, transaction);
|
||||
UpdateItemValues(item.Id, GetItemValues(item), transaction);
|
||||
UpdateItemValues(item.Id, GetItemValuesToSave(item), transaction);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
@@ -2019,7 +2022,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
}
|
||||
if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B"+ GetJoinUserDataText(query) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))", false);
|
||||
return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))", false);
|
||||
}
|
||||
|
||||
return new Tuple<string, bool>(name, false);
|
||||
@@ -2245,7 +2248,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd)
|
||||
private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, string paramSuffix = "")
|
||||
{
|
||||
var whereClauses = new List<string>();
|
||||
|
||||
@@ -2321,8 +2324,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
|
||||
if (includeTypes.Length == 1)
|
||||
{
|
||||
whereClauses.Add("type=@type");
|
||||
cmd.Parameters.Add(cmd, "@type", DbType.String).Value = includeTypes[0];
|
||||
whereClauses.Add("type=@type" + paramSuffix);
|
||||
cmd.Parameters.Add(cmd, "@type" + paramSuffix, DbType.String).Value = includeTypes[0];
|
||||
}
|
||||
else if (includeTypes.Length > 1)
|
||||
{
|
||||
@@ -2626,8 +2629,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
var index = 0;
|
||||
foreach (var artist in query.ArtistNames)
|
||||
{
|
||||
clauses.Add("@ArtistName" + index + " in (select value from itemvalues where ItemId=Guid and Type <= 1)");
|
||||
cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = artist;
|
||||
clauses.Add("@ArtistName" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)");
|
||||
cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = artist.RemoveDiacritics();
|
||||
index++;
|
||||
}
|
||||
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
|
||||
@@ -2640,8 +2643,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
var index = 0;
|
||||
foreach (var item in query.Genres)
|
||||
{
|
||||
clauses.Add("@Genre" + index + " in (select value from itemvalues where ItemId=Guid and Type=2)");
|
||||
cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = item;
|
||||
clauses.Add("@Genre" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
|
||||
cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = item.RemoveDiacritics();
|
||||
index++;
|
||||
}
|
||||
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
|
||||
@@ -2654,8 +2657,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
var index = 0;
|
||||
foreach (var item in query.Tags)
|
||||
{
|
||||
clauses.Add("@Tag" + index + " in (select value from itemvalues where ItemId=Guid and Type=4)");
|
||||
cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = item;
|
||||
clauses.Add("@Tag" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=4)");
|
||||
cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = item.RemoveDiacritics();
|
||||
index++;
|
||||
}
|
||||
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
|
||||
@@ -2668,8 +2671,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
var index = 0;
|
||||
foreach (var item in query.Studios)
|
||||
{
|
||||
clauses.Add("@Studio" + index + " in (select value from itemvalues where ItemId=Guid and Type=3)");
|
||||
cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = item;
|
||||
clauses.Add("@Studio" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
|
||||
cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = item.RemoveDiacritics();
|
||||
index++;
|
||||
}
|
||||
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
|
||||
@@ -2682,8 +2685,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
var index = 0;
|
||||
foreach (var item in query.Keywords)
|
||||
{
|
||||
clauses.Add("@Keyword" + index + " in (select value from itemvalues where ItemId=Guid and Type=5)");
|
||||
cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = item;
|
||||
clauses.Add("@Keyword" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=5)");
|
||||
cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = item.RemoveDiacritics();
|
||||
index++;
|
||||
}
|
||||
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
|
||||
@@ -2812,6 +2815,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
|
||||
whereClauses.Add("MediaType in (" + val + ")");
|
||||
}
|
||||
if (query.ItemIds.Length > 0)
|
||||
{
|
||||
var excludeIds = new List<string>();
|
||||
|
||||
var index = 0;
|
||||
foreach (var id in query.ItemIds)
|
||||
{
|
||||
excludeIds.Add("Guid = @IncludeId" + index);
|
||||
cmd.Parameters.Add(cmd, "@IncludeId" + index, DbType.Guid).Value = new Guid(id);
|
||||
index++;
|
||||
}
|
||||
|
||||
whereClauses.Add(string.Join(" OR ", excludeIds.ToArray()));
|
||||
}
|
||||
if (query.ExcludeItemIds.Length > 0)
|
||||
{
|
||||
var excludeIds = new List<string>();
|
||||
@@ -3478,7 +3495,292 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
}
|
||||
}
|
||||
|
||||
private List<Tuple<int, string>> GetItemValues(BaseItem item)
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, 0, typeof(MusicArtist).FullName);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, 1, typeof(MusicArtist).FullName);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, 3, typeof(Studio).FullName);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, 2, typeof(Genre).FullName);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, 2, typeof(GameGenre).FullName);
|
||||
}
|
||||
|
||||
public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
|
||||
{
|
||||
return GetItemValues(query, 2, typeof(MusicGenre).FullName);
|
||||
}
|
||||
|
||||
private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int itemValueType, string returnType)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException("query");
|
||||
}
|
||||
|
||||
if (!query.Limit.HasValue)
|
||||
{
|
||||
query.EnableTotalRecordCount = false;
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
var itemCountColumns = new List<Tuple<string, string>>();
|
||||
|
||||
var typesToCount = query.IncludeItemTypes.ToList();
|
||||
|
||||
if (typesToCount.Count == 0)
|
||||
{
|
||||
//typesToCount.Add("Item");
|
||||
}
|
||||
|
||||
foreach (var type in typesToCount)
|
||||
{
|
||||
var itemCountColumnQuery = "Select Count(Value) from ItemValues where ItemValues.CleanValue=CleanName AND Type=@ItemValueType AND ItemId in (";
|
||||
itemCountColumnQuery += "select guid" + GetFromText();
|
||||
|
||||
var typeSubQuery = new InternalItemsQuery(query.User)
|
||||
{
|
||||
ExcludeItemTypes = query.ExcludeItemTypes,
|
||||
MediaTypes = query.MediaTypes,
|
||||
AncestorIds = query.AncestorIds,
|
||||
ExcludeItemIds = query.ExcludeItemIds,
|
||||
ItemIds = query.ItemIds,
|
||||
TopParentIds = query.TopParentIds,
|
||||
ParentId = query.ParentId,
|
||||
IsPlayed = query.IsPlayed
|
||||
};
|
||||
if (string.Equals(type, "Item", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
typeSubQuery.IncludeItemTypes = query.IncludeItemTypes;
|
||||
}
|
||||
else
|
||||
{
|
||||
typeSubQuery.IncludeItemTypes = new[] { type };
|
||||
}
|
||||
var whereClauses = GetWhereClauses(typeSubQuery, cmd, type);
|
||||
|
||||
var typeWhereText = whereClauses.Count == 0 ?
|
||||
string.Empty :
|
||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
||||
|
||||
itemCountColumnQuery += typeWhereText;
|
||||
|
||||
itemCountColumnQuery += ")";
|
||||
|
||||
var columnName = type + "Count";
|
||||
|
||||
itemCountColumns.Add(new Tuple<string, string>(columnName, "(" + itemCountColumnQuery + ") as " + columnName));
|
||||
}
|
||||
|
||||
var columns = _retriveItemColumns.ToList();
|
||||
columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray());
|
||||
|
||||
cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray(), cmd)) + GetFromText();
|
||||
cmd.CommandText += GetJoinUserDataText(query);
|
||||
|
||||
var innerQuery = new InternalItemsQuery(query.User)
|
||||
{
|
||||
ExcludeItemTypes = query.ExcludeItemTypes,
|
||||
IncludeItemTypes = query.IncludeItemTypes,
|
||||
MediaTypes = query.MediaTypes,
|
||||
AncestorIds = query.AncestorIds,
|
||||
ExcludeItemIds = query.ExcludeItemIds,
|
||||
ItemIds = query.ItemIds,
|
||||
TopParentIds = query.TopParentIds,
|
||||
ParentId = query.ParentId,
|
||||
IsPlayed = query.IsPlayed
|
||||
};
|
||||
|
||||
var innerWhereClauses = GetWhereClauses(innerQuery, cmd);
|
||||
|
||||
var innerWhereText = innerWhereClauses.Count == 0 ?
|
||||
string.Empty :
|
||||
" where " + string.Join(" AND ", innerWhereClauses.ToArray());
|
||||
|
||||
var whereText = " where Type=@SelectType";
|
||||
whereText += " And CleanName In (Select CleanValue from ItemValues where Type=@ItemValueType AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
|
||||
cmd.CommandText += whereText;
|
||||
|
||||
var outerQuery = new InternalItemsQuery(query.User)
|
||||
{
|
||||
IsFavorite = query.IsFavorite,
|
||||
IsFavoriteOrLiked = query.IsFavoriteOrLiked,
|
||||
IsLiked = query.IsLiked,
|
||||
IsLocked = query.IsLocked,
|
||||
NameLessThan = query.NameLessThan,
|
||||
NameStartsWith = query.NameStartsWith,
|
||||
NameStartsWithOrGreater = query.NameStartsWithOrGreater,
|
||||
AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater,
|
||||
Tags = query.Tags,
|
||||
OfficialRatings = query.OfficialRatings,
|
||||
Genres = query.GenreIds,
|
||||
Years = query.Years
|
||||
};
|
||||
|
||||
var outerWhereClauses = GetWhereClauses(outerQuery, cmd);
|
||||
|
||||
var outerWhereText = outerWhereClauses.Count == 0 ?
|
||||
string.Empty :
|
||||
" AND " + string.Join(" AND ", outerWhereClauses.ToArray());
|
||||
cmd.CommandText += outerWhereText;
|
||||
|
||||
cmd.Parameters.Add(cmd, "@SelectType", DbType.String).Value = returnType;
|
||||
cmd.Parameters.Add(cmd, "@ItemValueType", DbType.Int32).Value = itemValueType;
|
||||
|
||||
if (EnableJoinUserData(query))
|
||||
{
|
||||
cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
|
||||
}
|
||||
|
||||
//cmd.CommandText += GetGroupBy(query);
|
||||
cmd.CommandText += " group by PresentationUniqueKey";
|
||||
|
||||
cmd.CommandText += " order by SortName";
|
||||
|
||||
if (query.Limit.HasValue || query.StartIndex.HasValue)
|
||||
{
|
||||
var limit = query.Limit ?? int.MaxValue;
|
||||
|
||||
cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (query.StartIndex.HasValue)
|
||||
{
|
||||
cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
cmd.CommandText += ";";
|
||||
|
||||
var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
|
||||
|
||||
if (isReturningZeroItems)
|
||||
{
|
||||
cmd.CommandText = "";
|
||||
}
|
||||
|
||||
if (query.EnableTotalRecordCount)
|
||||
{
|
||||
cmd.CommandText += "select count (guid)" + GetFromText();
|
||||
|
||||
cmd.CommandText += GetJoinUserDataText(query);
|
||||
cmd.CommandText += whereText;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.CommandText = cmd.CommandText.TrimEnd(';');
|
||||
}
|
||||
|
||||
var list = new List<Tuple<BaseItem, ItemCounts>>();
|
||||
var count = 0;
|
||||
|
||||
var commandBehavior = isReturningZeroItems || !query.EnableTotalRecordCount
|
||||
? (CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)
|
||||
: CommandBehavior.SequentialAccess;
|
||||
|
||||
using (var reader = cmd.ExecuteReader(commandBehavior))
|
||||
{
|
||||
LogQueryTime("GetItemValues", cmd, now);
|
||||
|
||||
if (isReturningZeroItems)
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
count = reader.GetInt32(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var item = GetItem(reader);
|
||||
if (item != null)
|
||||
{
|
||||
var countStartColumn = columns.Count - typesToCount.Count;
|
||||
|
||||
list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(reader, countStartColumn, typesToCount)));
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.NextResult() && reader.Read())
|
||||
{
|
||||
count = reader.GetInt32(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
count = list.Count;
|
||||
}
|
||||
|
||||
return new QueryResult<Tuple<BaseItem, ItemCounts>>
|
||||
{
|
||||
Items = list.ToArray(),
|
||||
TotalRecordCount = count
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private ItemCounts GetItemCounts(IDataReader reader, int countStartColumn, List<string> typesToCount)
|
||||
{
|
||||
var counts = new ItemCounts();
|
||||
|
||||
for (var i = 0; i < typesToCount.Count; i++)
|
||||
{
|
||||
var value = reader.GetInt32(countStartColumn + i);
|
||||
|
||||
var type = typesToCount[i];
|
||||
if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
counts.SeriesCount = value;
|
||||
}
|
||||
else if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
counts.EpisodeCount = value;
|
||||
}
|
||||
else if (string.Equals(type, "Movie", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
counts.MovieCount = value;
|
||||
}
|
||||
else if (string.Equals(type, "MusicAlbum", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
counts.AlbumCount = value;
|
||||
}
|
||||
else if (string.Equals(type, "Audio", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
counts.SongCount = value;
|
||||
}
|
||||
else if (string.Equals(type, "Game", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
counts.GameCount = value;
|
||||
}
|
||||
counts.ItemCount += value;
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item)
|
||||
{
|
||||
var list = new List<Tuple<int, string>>();
|
||||
|
||||
@@ -3604,6 +3906,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
_saveItemValuesCommand.GetParameter(0).Value = itemId;
|
||||
_saveItemValuesCommand.GetParameter(1).Value = pair.Item1;
|
||||
_saveItemValuesCommand.GetParameter(2).Value = pair.Item2;
|
||||
if (pair.Item2 == null)
|
||||
{
|
||||
_saveItemValuesCommand.GetParameter(3).Value = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveItemValuesCommand.GetParameter(3).Value = pair.Item2.RemoveDiacritics();
|
||||
}
|
||||
_saveItemValuesCommand.Transaction = transaction;
|
||||
|
||||
_saveItemValuesCommand.ExecuteNonQuery();
|
||||
|
||||
Reference in New Issue
Block a user