question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

need custom Set() collection for Update()

See original GitHub issue

this is problem after i make custom FindQuery() https://github.com/linq2db/linq2db/issues/2793

var get = db.T_Table1.FirstOrDefault(x => x.RowId == 1 && x.Code == "code1");
if (get != null)
{
	if (true)
	{
		get.StringValue = "a";
		//db.FindQuery(get).Set(x => x.StringValue, get.StringValue).Update();
	}
	if (true)
	{
		get.IntValue = 5;
		//db.FindQuery(get).Set(x => x.IntValue, get.IntValue).Update();
	}
	if (true)
	{
		get.MoneyValue = 1000000;
		//db.FindQuery(get).Set(x => x.MoneyValue, get.MoneyValue).Update();
	}
	
	//db.FindQuery(get).Set(x => x.StringValue, get.StringValue)
	//	.Set(x => x.IntValue, get.IntValue)
	//	.Set(x => x.MoneyValue, get.MoneyValue)
	//	.Update();
}

since linq2db cannot track changes, i need to collect Changes before do Update()

public class MyUpdate<T> where T : class
    {
        private IQueryable<T> query { get; set; }
        private LinqToDB.Linq.IUpdatable<T> updatequery { get; set; }
        public MyUpdate(IQueryable<T> source)
        {
            query = source;
        }
        public void Set<TV>(System.Linq.Expressions.Expression<Func<T, TV>> extract, TV value)
        {
            if (updatequery == null)
            {
                updatequery = query.Set(extract, value);
            }
            else
            {
                updatequery = updatequery.Set(extract, value);
            }
        }
        public void ClearSet()
        {
	        updatequery = null;
        }
        public int Update()
        {
	        if (updatequery != null)
	        {
		        return updatequery.Update();
	        }
	        return 0;
        }
        
        public override string ToString()
        {
            if (updatequery == null)
            {
                return null;
            }
            else
            {
                return updatequery.ToString();
            }
        }
    }

with this i can update column i want by condition

var get = db.T_Table1.FirstOrDefault(x => x.RowId == 1 && x.Code == "code1");
if (get != null)
{
	var qupdate = new MyUpdate<T_Table1>(db.FindQuery(get));
	if (true)
	{
		get.StringValue = "a";
		qupdate.Set(x => x.StringValue, get.StringValue);
	}
	if (true)
	{
		get.IntValue = 5;
		qupdate.Set(x => x.IntValue, get.IntValue);
	}
	if (true)
	{
		get.MoneyValue = 1000000;
		qupdate.Set(x => x.MoneyValue, get.MoneyValue);
	}
	
	int affected = qupdate.Update();
}

i need feature something like this or more simple for future

Environment details

linq2db version: 3.2.3 Database: PostgreSQL 12.1 .NET Framework: 4.7.2

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:22 (12 by maintainers)

github_iconTop GitHub Comments

2reactions
sdanylivcommented, Jan 27, 2021

This is solution. Don’t even tell someone that I have helped in creating generic repository for linq2db. Worst idea ever. Usage is simple:

var affected = db.UpdateWithCompare(someObj);

public static class UpdateExtensions
{
	public static Func<IDataContext, T, int> CreateUpdateFunc<T>(IDataContext dc)
	{
		var dcParam     = Expression.Parameter(typeof(IDataContext), "dc");
		var objParam    = Expression.Parameter(typeof(T), "obj");

		var ed = dc.MappingSchema.GetEntityDescriptor(typeof(T));
		var pk = ed.Columns.Where(c => c.IsPrimaryKey).ToArray();

		if (pk.Length == 0)
			throw new InvalidOperationException($"Entity of type '{typeof(T).Name}' has no PrimaryKey.");

		var updatableColumns = ed.Columns.Where(c => !c.SkipOnUpdate && !c.IsPrimaryKey).ToArray();
		if (updatableColumns.Length == 0)
			throw new InvalidOperationException($"Entity of type '{typeof(T).Name}' has no updatable columns.");


		var queryExpr = Expression.Call(Methods.LinqToDB.GetTable.MakeGenericMethod(typeof(T)), dcParam);

		var entityParam = Expression.Parameter(typeof(T), "e");
		var predicateBody =
			pk.Select(k => (Expression)Expression.Equal(Expression.MakeMemberAccess(entityParam, k.MemberInfo),
				Expression.MakeMemberAccess(objParam, k.MemberInfo))).Aggregate(Expression.AndAlso);
		var predicate = Expression.Lambda(predicateBody, entityParam);

		queryExpr = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(typeof(T)), queryExpr, predicate);
		var firstOrDefaultExpr = Expression.Call(Methods.Queryable.FirstOrDefault.MakeGenericMethod(typeof(T)), queryExpr);

		var generator = new ExpressionGenerator();

		var serverObjVar = generator.AssignToVariable(firstOrDefaultExpr, "serverObj");
		var updatableVar = generator.AssignToVariable(
			Expression.Call(Methods.LinqToDB.Update.AsUpdatable.MakeGenericMethod(typeof(T)), queryExpr), "needsUpdate");
		var updatableSaveVar = generator.AssignToVariable(updatableVar, "updatableSave");
		var recordsAffectedVar = generator.AssignToVariable(Expression.Constant(0), "recordsAffected");

		var setObjParam = Expression.Parameter(typeof(T), "e");

		foreach (var column in updatableColumns)
		{
			var setExpr =
				Expression.Call(
					Methods.LinqToDB.Update.SetUpdatableExpression.MakeGenericMethod(typeof(T), column.MemberType),
					updatableVar,
					Expression.Lambda(Expression.MakeMemberAccess(setObjParam, column.MemberInfo), setObjParam),
					Expression.Lambda(Expression.MakeMemberAccess(serverObjVar, column.MemberInfo)));

			generator.IfThen(
				Expression.NotEqual(Expression.MakeMemberAccess(objParam, column.MemberInfo),
					Expression.MakeMemberAccess(serverObjVar, column.MemberInfo)),
				Expression.Assign(updatableVar, setExpr));
		}

		generator.IfThen(
			Expression.ReferenceNotEqual(updatableVar, updatableSaveVar),
			Expression.Assign(recordsAffectedVar,
				Expression.Call(Methods.LinqToDB.Update.UpdateUpdatable.MakeGenericMethod(typeof(T)),
					updatableVar)));


		generator.AddExpression(recordsAffectedVar);

		var lambda = Expression.Lambda<Func<IDataContext, T, int>>(generator.Build(), dcParam, objParam);
		return lambda.Compile();
	}

	static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());

	public static int UpdateWithCompare<T>(this IDataContext dc, T obj)
	{
		var key = (string)dc.MappingSchema.GetType().GetProperty("ConfigurationID", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dc.MappingSchema);

		var updateFunc = _cache.GetOrCreate(key, k => CreateUpdateFunc<T>(dc));

		return updateFunc(dc, obj);
	}
}
1reaction
sdanylivcommented, Jan 27, 2021
        public static int UpdateAudited<T>(this IUpdatable<T> updatable)
	{
		var queryable = (IQueryable)updatable.GetType().GetField("Query").GetValue(updatable);

		var updates = CollectUpdates(queryable.Expression, out var queryExpression);

		if (updates.Count > 0)
                    return updatable.Update();
		return 0;
	}
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to create a custom collection that extends on Sets In ...
I want to create a new custom Scala collection from existing Set Collection which I can later extend with some additional functions.
Read more >
Add data to Cloud Firestore - Firebase - Google
This guide explains how to use the set, add, or update individual documents in Cloud Firestore. If you want to write data in...
Read more >
Add to Update Set Utility Documentation and Custom...
You have custom configurations with dependencies that you wish to automate pushing into update sets. In the saveRecord() function of the ...
Read more >
Set custom metadata | Compute Engine Documentation
Click the instance for which you want to update metadata. Click the Edit button at the top of the page. Under Custom metadata,...
Read more >
Set up event parameters | Google Analytics 4 Properties
This guide shows you how to set up parameters for recommended events and custom events on your website so you can collect more...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found