Giter Club home page Giter Club logo

entityframework.triggers's Introduction

EntityFramework.Triggers

Add triggers to your entities with insert, update, and delete events. There are three events for each: before, after, and upon failure.

This repo contains the code for both the EntityFramework and EntityFrameworkCore projects, as well as the ASP.NET Core support projects.

Nuget packages for triggers

EF version .NET support NuGet package
>= 6.1.3 >= Framework 4.6.1 NuGet Status
>= Core 2.0 >= Framework 4.6.1 || >= Standard 2.0 NuGet Status

Nuget packages for ASP.NET Core dependency injection methods

EF version .NET support NuGet package
>= 6.1.3 >= Framework 4.6.1 NuGet Status
>= Core 2.0 >= Framework 4.6.1 || >= Standard 2.0 NuGet Status

Basic usage with a global singleton

To use triggers on your entities, simply have your DbContext inherit from DbContextWithTriggers. If you can't change your DbContext inheritance chain, you simply need to override your SaveChanges... as demonstrated below

public abstract class Trackable {
	public DateTime Inserted { get; private set; }
	public DateTime Updated { get; private set; }

	static Trackable() {
		Triggers<Trackable>.Inserting += entry => entry.Entity.Inserted = entry.Entity.Updated = DateTime.UtcNow;
		Triggers<Trackable>.Updating += entry => entry.Entity.Updated = DateTime.UtcNow;
	}
}

public class Person : Trackable {
	public Int64 Id { get; private set; }
	public String Name { get; set; }
}

public class Context : DbContextWithTriggers {
	public DbSet<Person> People { get; set; }
}

As you may have guessed, what we're doing above is enabling automatic insert and update stamps for any entity that inherits Trackable. Events are raised from the base class/interfaces, up to the events specified on the entity class being used. It's just as easy to set up soft deletes (the Deleting, Updating, and Inserting events are cancellable from within a handler, logging, auditing, and more!).

Usage with dependency injection

This library fully supports dependency injection. The two features are:

  1. Injecting the triggers and handler registrations to avoid the global singleton in previous versions
serviceCollection
	.AddSingleton(typeof(ITriggers<,>), typeof(Triggers<,>))
	.AddSingleton(typeof(ITriggers<>), typeof(Triggers<>))
	.AddSingleton(typeof(ITriggers), typeof(Triggers));
  1. Using injected services right inside your global handlers
Triggers<Person, Context>().GlobalInserted.Add<IServiceBus>(
	entry => entry.Service.Broadcast("Inserted", entry.Entity)
);

Triggers<Person, Context>().GlobalInserted.Add<(IServiceBus Bus, IServiceX X)>(
	entry => {
		entry.Service.Bus.Broadcast("Inserted", entry.Entity);
		entry.Service.X.DoSomething();
	}
);
  1. Using injected services right inside your injected handlers
public class Startup
{
	public void ConfigureServices(IServiceCollection services)
	{
		...
		services.AddDbContext<Context>();
		services.AddTriggers();
	}

	public void Configure(IApplicationBuilder app, IHostingEnvironment env)
	{
		...
		app.UseTriggers(builder =>
		{
			builder.Triggers().Inserted.Add(
				entry => Debug.WriteLine(entry.Entity.ToString())
			);
			builder.Triggers<Person, Context>().Inserted.Add(
				entry => Debug.WriteLine(entry.Entity.FirstName)
			);

			// receive injected services inside your handler, either with just a single service type or with a value tuple of services
			builder.Triggers<Person, Context>().GlobalInserted.Add<IServiceBus>(
				entry => entry.Service.Broadcast("Inserted", entry.Entity)
			);
			builder.Triggers<Person, Context>().GlobalInserted.Add<(IServiceBus Bus, IServiceX X)>(
				entry => {
					entry.Service.Bus.Broadcast("Inserted", entry.Entity);
					entry.Service.X.DoSomething();
				}
			);
		});
	}
}

How to enable triggers if you can't derive from DbContextWithTriggers

If you can't easily change what your DbContext class inherits from (ASP.NET Identity users, for example), you can override your SaveChanges... methods to call the SaveChangesWithTriggers... extension methods. Alternatively, you can call SaveChangesWithTriggers... directly instead of SaveChanges... if, for example, you want to control which changes cause triggers to be fired.

class YourContext : DbContext {
	// Your usual DbSet<> properties

	#region If you're targeting EF 6
	public override Int32 SaveChanges() {
		return this.SaveChangesWithTriggers(base.SaveChanges);
	}
	public override Task<Int32> SaveChangesAsync(CancellationToken cancellationToken) {
		return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, cancellationToken);
	}
	#endregion

	#region If you're targeting EF Core
	public override Int32 SaveChanges() {
		return this.SaveChangesWithTriggers(base.SaveChanges, acceptAllChangesOnSuccess: true);
	}
	public override Int32 SaveChanges(Boolean acceptAllChangesOnSuccess) {
		return this.SaveChangesWithTriggers(base.SaveChanges, acceptAllChangesOnSuccess);
	}
	public override Task<Int32> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) {
		return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, acceptAllChangesOnSuccess: true, cancellationToken: cancellationToken);
	}
	public override Task<Int32> SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)) {
		return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, acceptAllChangesOnSuccess, cancellationToken);
	}
	#endregion
}

#region If you didn't/can't override `SaveChanges...`, you can (not recommended) call 
dbContext.SaveChangesWithTriggers(dbContext.SaveChanges);
dbContext.SaveChangesWithTriggersAsync(dbContext.SaveChangesAsync);
#endregion

Longer example (targeting EF6 for now)

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EntityFramework.Triggers;

namespace Example {
	public class Program {
		public abstract class Trackable {
			public virtual DateTime Inserted { get; private set; }
			public virtual DateTime Updated { get; private set; }

			static Trackable() {
				Triggers<Trackable>.Inserting += entry => entry.Entity.Inserted = entry.Entity.Updated = DateTime.UtcNow;
				Triggers<Trackable>.Updating += entry => entry.Entity.Updated = DateTime.UtcNow;
			}
		}

		public abstract class SoftDeletable : Trackable {
			public virtual DateTime? Deleted { get; private set; }

			public Boolean IsSoftDeleted => Deleted != null;
			public void SoftDelete() => Deleted = DateTime.UtcNow;
			public void SoftRestore() => Deleted = null;

			static SoftDeletable() {
				Triggers<SoftDeletable>.Deleting += entry => {
					entry.Entity.SoftDelete();
					entry.Cancel = true; // Cancels the deletion, but will persist changes with the same effects as EntityState.Modified
				};
			}
		}

		public class Person : SoftDeletable {
			public virtual Int64 Id { get; private set; }
			public virtual String FirstName { get; set; }
			public virtual String LastName { get; set; }
		}

		public class LogEntry {
			public virtual Int64 Id { get; private set; }
			public virtual String Message { get; set; }
		}

		public class Context : DbContextWithTriggers {
			public virtual DbSet<Person> People { get; set; }
			public virtual DbSet<LogEntry> Log { get; set; }
		}
		internal sealed class Configuration : DbMigrationsConfiguration<Context> {
			public Configuration() {
				AutomaticMigrationsEnabled = true;
			}
		}

		static Program() {
			Triggers<Person, Context>.Inserting += e => {
				e.Context.Log.Add(new LogEntry { Message = "Insert trigger fired for " + e.Entity.FirstName });
				Console.WriteLine("Inserting " + e.Entity.FirstName);
			};
			Triggers<Person>.Updating += e => Console.WriteLine($"Updating {e.Original.FirstName} to {e.Entity.FirstName}");
			Triggers<Person>.Deleting += e => Console.WriteLine("Deleting " + e.Entity.FirstName);
			Triggers<Person>.Inserted += e => Console.WriteLine("Inserted " + e.Entity.FirstName);
			Triggers<Person>.Updated += e => Console.WriteLine("Updated " + e.Entity.FirstName);
			Triggers<Person>.Deleted += e => Console.WriteLine("Deleted " + e.Entity.FirstName);
		}
		
		private static void Main(String[] args) => Task.WaitAll(MainAsync(args));

		private static async Task MainAsync(String[] args) {
			using (var context = new Context()) {
				context.Database.Delete();
				context.Database.Create();

				var log = context.Log.ToList();
				var nickStrupat = new Person {
					FirstName = "Nick",
					LastName = "Strupat"
				};

				context.People.Add(nickStrupat);
				await context.SaveChangesAsync();

				nickStrupat.FirstName = "Nicholas";
				context.SaveChanges();
				context.People.Remove(nickStrupat);
				await context.SaveChangesAsync();
			}
		}
	}
}

See also

Contributing

  1. Create an issue
  2. Let's find some point of agreement on your suggestion.
  3. Fork it!
  4. Create your feature branch: git checkout -b my-new-feature
  5. Commit your changes: git commit -am 'Add some feature'
  6. Push to the branch: git push origin my-new-feature
  7. Submit a pull request :D

History

Commit history

License

MIT License

entityframework.triggers's People

Contributors

nickstrupat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

entityframework.triggers's Issues

Set order of Triggers?

Is it currently possible to set the calling order of triggers? I have a basic example that looks similar to this, minus the actual logic implemented.

public abstact class Updatable
{
    static Updatable()
    {
        Triggers<Updatable>.Updating += entry => {...};
    }
}

public class ModelA : Updatable
{
    static ModelA()
    {
        Triggers<ModelA>.Updating += entry => {...};
    }
}

The goal in my use case is to call the Updating trigger in ModelA before the generic Updating trigger in Updatable is called. Is it currently possible to do that, or am I approaching triggers in the wrong way? Any help would be greatly appreciated.

EF Core 2.0.2 Add-Migration Problems

Hi there,

I'm currently setting up a new project with Entity Framework Core 2.0.2 (target framework is net461 and platform x64) and tried to install EntityFramework.Triggers, but I have two problems getting the Add-Migration command to work again (like it did before I installed the package).

The first try was only installing the package and inheriting from DbContextWithTriggers. The result was Add-Migration did not recognize my DbContext any more ("No DbContext named '...' was found").
Is this quite normal?

So, next try was with the alternative way of overwriting SaveChanges-Methods. This time Add-Migration worked as before. But there's no trigger defined yet.
Next step was to add a test trigger in my user entity:

static User
{
    Triggers<User>.Inserted += o => {};
}

and run Add-Migration again, resulting in a FileLoadException on trying to load "Microsoft.EntityFrameworkCore, Version 2.0.0.0" (instead of 2.0.2.0 which is used everywhere)

System.TypeInitializationException: Der Typeninitialisierer fรผr "EntityModel.User" hat eine Ausnahme verursacht. ---> System.IO.FileLoadException: Die Datei oder Assembly "Microsoft.EntityFrameworkCore, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" oder eine Abhรคngigkeit davon wurde nicht gefunden. Die gefundene Manifestdefinition der Assembly stimmt nicht mit dem Assemblyverweis รผberein. (Ausnahme von HRESULT: 0x80131040)
bei EntityModel.User..cctor()

and the exceptions's FusionLog property states it's called by "EntityFrameworkCore.Triggers, Version=1.1.1.0":

I can't any reference to version 2.0.0.0 anywhere und don't have any further ideas how to solve this for the moment.
Hope you can help me.

Double log entry on InsertFailed

I am trying to create a trigger on insertFailed but when I try to save the Log for the failed action it actually refires the failed event so I am getting two log entries.
I am logging to a separate database so I created a "saveLog" in the other project that I can use to perform the Save.

My Trigger:

Triggers<Unit>.InsertFailed += t =>
            {
                AuditLog log = new AuditLog();
                log.Action = "Insert";
                log.ModifyingCode = "Unit";
                log.WhatHappened = "Insert of new user failed." + t.Exception.InnerException.Message.ToString();
                
                AuditLogService.SaveAuditLog(log);
            };

In the SaveAuditLog

public static void SaveAuditLog(AuditLog log)
        {
           LoggingDBContext context = new LoggingDBContext(builder.Options);

            context.Add(log);
            context.SaveChanges();
        }

Am I missing something to keep it from calling into the InserFailed trigger twice? The SaveAuditLog does not produce an exception.

Problems in loading the right CoContra assembly in mixed application (.netstandard2.0 and .net462 projects)

While upgrading our application to .netstandard 2.0 library projects and using those projects as nuget packages in a .net framework 4.6.2 web application project we get the following exeption on runtime.

exception_cocontra

the Nuget package of EntityFrameworkCore.Triggers was reference in the .netstandard2.0 library

I think there are two issues here:

  1. The application tries to load Version 1.0.1 of CoContra. I referenced the newest package of EntityFramework.Triggers 1.1.0 which actually references Cocontra 1.0.2.

  2. The .net461 web application tries to load the CoContra.net45.dll assembyl but actually needs the .netstandard1.0.dll. Before upgrading it worked to explicitly load the .netstandard1.0 assembly in csproj file.

At the moment we canยดt use the official EntityFrameworCore.Triggers package anymore

Can't save changes in a trigger

I have 2 DbSet, one is Product another is ProductRecord.
And I want to record every changes of Product into ProductRecord.
here is my code.

  Triggers<Product>.Inserted += entry =>
  {
      ProductRecord.AddRecord(entry.Entity, ProductRecordOperationType.Insert);
  }
  Triggers<Product>.Updated += entry =>
  {
      ProductRecord.AddRecord(entry.Entity, ProductRecordOperationType.Update);
  }

  public static void AddRecord(Product P, ProductRecordOperationType opt)
  {
      using(var db = new ApplicationDbContext())
      {
          var rcd = ProductRecord.FromProduct(P);
          rcd.Operation = opt;
          rcd.Id = Guid.NewGuid().ToString();
          db.PushProductRecord.Add(rcd);
          
          db.SaveChanges(); // ERROR HAPPENS HERE
      }
  }

Error Message says that I'm tring to add an entity with Id=null

And I also tried add an Inserting Trigger to ProductRecord to ensure every record has ID, still dosent work.

EntityFramework.Triggers handles Transactions with DbContext SaveChanges() ?

Hello

In the documentation I did not see anything that explains how the library works with the atomicity between the commands that originated the activation of the trigger to the commands executed in the trigger itself.

Is there any native library mechanism?

If not, any tips on how to work on this subject?

Victor Perez

Multiple Updated Events Fired

Hello,

I'm currently hitting an issue where I save an entity with my DbContext and that triggers multiple Updated events.

If I have something like...

public MyEntity()
{
   Triggers<MyEntity>.Updated += _Updated;
}

void _Updated(IAfterChangeEntry<MyEntity> entry)
{
   var eventEntity = _GetEventEntity(entry.Entity); // Assume this already exists
   entry.Context.Set<Event>().Add(eventEntity);

   // Do I call SaveChanges here?
   entry.Context.SaveChanges();
}

Where exactly do I call SaveChanges?

trigger fires twice EFCore 2.2.0-preview3-35497

The trigger is triggered 2 even though it has been saved only once:

public partial class TblDatei { static TblDatei() => Triggers<TblDatei>.Updated += e => { Console.WriteLine("Updated " + e.Entity.Guid); }; }

Saving:

`
var dat = db.TblDatei.Where(s => s.Guid == "997F352E860C4D54B5CBBD2D7344FD06").FirstOrDefault();

            dat.Erstelldatum = "123456Y8";

            db.SaveChanges();

`

can't call Updating.Original

when call

Triggers<Order>.Updating += x =>
{
       var a = x.Original;
}

have error => Member 'OriginalValue' cannot be called for property 'Details' on entity of type 'Order' because the property is not part of the Entity Data Model.

Share trigger events across multple instances

I would like to use EntityFramework.Triggers across multiple instances, would it be possible to raise an event and another instance to capture this? Similar to what a service broker would do.

Abstract Class byte[] instead?

In your example of the abstract class Trackable, why would you not use something like

[Rowversion]
public byte[] Inserted {get ; private set } 

Is this because only 1 rowversion tag can be on an object, therefore we wouldn't have a rowversion for updated?

How to use with DDD

Hello,

Im using a ddd structure, in this case dont want to add more references in my Domain project.
We have a way to make a class ITriggerable from my data project?

Thank you

GlobalInserted or GlobalUpdate not work

Hi,
i'm trying to use your library but i don't understant why GlobalInserted or GlobalUpdate does'nt work, events is not fired when SaveChanges is called

How to Get Logged User in Trigger?

I want to save the information about the user that inserted and/or modified the entry. This is being a pain, since I need to use the static constructor to get this information, and ASP.NET Core uses a lot of IoC/DI patterns that make the task almost impossible.

The idea was to use a class like this to get the user:

    public class CurrentUserService
    {
        private IHttpContextAccessor _contextAccessor;
        private HttpContext _context { get { return _contextAccessor.HttpContext; } }

        public CurrentUserService(IHttpContextAccessor contextAccessor)
        {
            _contextAccessor = contextAccessor;
        }

        public string UserName
        {
            get
            {
                var userName = "SystemGenerated";

                if (_context != null)
                {
                    if (_context.User != null)
                    {
                        var identity = _context.User.Identity;

                        if (identity != null && identity.IsAuthenticated)
                        {
                            userName = identity.Name;
                        }
                    }
                }

                return userName;
            }
        }
    }

But I think including this as an entry property can be better.

The inherited properties are not saved in EF Core

Hello,

I created the example project. When I make new migration: dotnet ef migration new Initialize

  • The migration creates new Question table with 3 columns Id, Title, Description. Columns InsertedAt, UpdatedAt are not inclued into the migration.
  • When I add missing columns into the database manually...
    The EF saves only 3 columns (Id, Title, Description) into the database. Columns **InsertedAt, UpdatedAt are not saved. Triggers Inserting and Updating are called correctly.
  • The EF loads only 3 columns (Id, Title, Description) from the database. Columns InsertedAt, UpdatedAt are not loaded.

Could you help me where the problem is?
Thank you Myth.

    public abstract class Trackable
    {
        public DateTime InsertedAt { get; private set; }
        public DateTime UpdatedAt { get; private set; }
    
        static Trackable()
        {
            Triggers<Trackable>.Inserting += (entry) => { entry.Entity.InsertedAt = entry.Entity.UpdatedAt = DateTime.UtcNow; };
            Triggers<Trackable>.Updating += (entry) => { entry.Entity.UpdatedAt = DateTime.UtcNow; };
        }
    }

    public class Question : Trackable
    {
        public long Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
    }
   
    public class ApplicationDbContext : DbContextWithTriggers
    {
        public DbSet<Question> Questions { get; set; }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }

Triggers Twice

I am using EFCore 2.1.8; AutoFac 4.9.2 w/ AutoFac.DependencyInjection 4.4.0 & AspNetCore.App 2.1.1.

I added your package EntityFrameworkCore.Triggers.AspNetCore 1.0.0 to use IApplicationBuilder#UseTriggers() and IServiceCollection#AddTriggers() and declare my own triggers.

Here's how I declare'em:

app.UseTriggers(builder =>
            {
                 #region Declare Triggers for Actors               
                Triggers<MercuryActor, MessageBrokerContext>.Inserted += entry =>
                {
                    MercuryActor actor= entry.Entity;
                    Logger.LogInformation("{0} Inserted! w/ Context", actor.Id);
                };
                #endregion

                Logger.LogInformation("Triggers declared");
            } );

            MessageBrokerContext ctx = app.ApplicationServices.GetRequiredService<MessageBrokerContext>();
            ctx.Actors.Add(new MercuryActor("Trigger Testing"));
            ctx.SaveChangesAsync();

I do have two issues on that.
This is a microservice hosted working with an API which listens and inserts entities.
When I run this code above, it triggers the event (when I insert that entity right after of the declaration), but when I use the API which is hosted independently of that service, it doesn't.

Next, I that case, when it triggers, it does twice.

Any idea?
Thanks.

Original values on update

Hi Nick,

This is a nice utility that IMO has many applications.

One of the things that I would find useful is the ability to have access to the original values when updating to make decisions and if the triggers are integrating things like SignalR or Hangfire, be able to better control the integration points.

This is just an idea and I wanted to see how you felt about it.

I realize that I can get the original values as a property bag using:

var originalValues = entry.Context.Entry(entry).OriginalValues;
var originalUpdateTime = originalValues.GetValue<DateTime>("UpdateDateTime");

What if the Entry interface was changed to say:

public interface IEntry<out TTriggerable> where TTriggerable : class, ITriggerable {
        TTriggerable Current { get; }
        TTriggerable Original { get; }
        DbContext Context { get; }
}

I could also see a lazy loaded projection of the Original property bag into a concrete object for the model (TTriggerable) so we would have a strongly typed object for Original.

Not working with Tracker Enabled DBContext

Hi Great Package

I was hoping you could shed some light as to why your package does't work when trying to use it with
Tracker Enabled DBContext
https://github.com/bilal-fazlani/tracker-enabled-dbcontext

TEDB uses attributes where yours inherits from Trackable

where Trackable does initialize but the events never fire.

 static Trackable()
        {
            Triggers<Trackable>.Inserting += entry => entry.Entity.Inserted = entry.Entity.Updated = DateTime.Now;
            Triggers<Trackable>.Updating += entry => entry.Entity.Updated = DateTime.Now;
        }

Any help will be appreciated.

Bahavior for Cascading deletes?

Are triggers capable of handling the cascading delete that occurs if you remove records from a navigation collection where cascading deletes are set up? No explicit remove from the context is done, but the records missing a mandatory owner will be removed.

I am trying to catch those deletes in a Deleted trigger, but it doesn't seem that i get there. (trying to keep an in-memory cache syncronized with the DB)

Instead, it seems like the entities are showing up in the Updated trigger with the parent navigation property set to null, but this is much more complicated to handle in a generic way. (I am doing this for a number of different entitytypes, so the code in that case would have to figure out if the navigation is in fact cascading and which field it should test for null)

Running Triggers 1.1.1 with EF Core 2.2.1

Trigger on Junction table in many-to-many relationship

Hi Nick.
Thank you again for your great library!

I have a question and I need your advice.

I need to catch event of updating relationships between two models which are related as many-to many relationship.

For example I have first model:

    {
        public virtual ICollection<Group> Groups { get; set; }
    }

and second model:

    public class Group : BaseEntity
    {
        public virtual ICollection<User> Users { get; set; }
    }

I need to have event when an user adds to a group. I know that we can add junction model:

    public class UserGroup : BaseEntity
    {
        public string UserId { get; set; }
        public virtual User User { get; set; }
        public string GroupId { get; set; }
        public virtual Group Group{ get; set; }
    }

And then add triggers to this model
But it is not great solution in my case. So I would like to know other possible ways to do it.

Thank for your attention!
Igor.

Cannot access a disposed object

I'm getting this error where it's telling me that my context is already disposed so I cant access it anymore. I've this trigger implemented in my Order.cs class, which basically updates the order status,

static Order()
{
    Triggers<Bill>.Inserted += async e =>
    {
        await PaymentStatusChangeNotifierAsync(e);
    };
}

Following is the PaymentStatusChangeNotifierAsync code,

private static async Task<int> PaymentStatusChangeNotifierAsync(IEntry<Bill, Microsoft.EntityFrameworkCore.DbContext> e)
{
    Task.Factory.StartNew(() =>
    {
        var orderedItems = e.Context.Entry(e.Entity.Order)
                                .Collection(o => o.OrderItems).Query()
                                .Where(wi => wi.OrderId == e.Entity.OrderId);

        var paidAmountSumation = e.Entity.Order.Bills.Sum(b => b.PaidAmount);

        var orderedItemAmountSumation = orderedItems.Sum(oi => oi.Quantity * (oi.UnitPrice - oi.Discount));

        if (paidAmountSumation == 0)
            e.Entity.Order.PaymentStatus = PaymentStatus.Unpaid;
        else if (paidAmountSumation == orderedItemAmountSumation)
            e.Entity.Order.PaymentStatus = PaymentStatus.Paid;
        else
            e.Entity.Order.PaymentStatus = PaymentStatus.PartialPaid;
    }).Wait();
    
    return await e.Context.SaveChangesAsync();
}

The await PaymentStatusChangeNotifierAsync(e); occasionally gives me the following exception.

Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application.

Is there any problems with my trigger implementation?

EntityFrameworkCore.Triggers works once, then twice then 3 times...

I'm attempting to use EntityFrameworkCore.Triggers to log changes to Transactions. When I run my app and change a transaction, it logs it once. Then when I change another transaction, it logs it twice. The next time I make a change, it logs it 3 times, etc. Any idea why this is happening?

Here is my TriggersContext...

using Microsoft.EntityFrameworkCore;
using System;
using System.Threading;
using System.Threading.Tasks;
using TriggersPOC.Models;
using EntityFrameworkCore.Triggers;

namespace TriggersPOC.Data
{
    public class TriggersContext : DbContext
    {
        public TriggersContext(DbContextOptions<TriggersContext> options) : base(options)
        {
        }

        public DbSet<Transaction> Transactions { get; set; }
        public DbSet<TransactionFundingStatusChange> TransactionFundingStatusHistory { get; set; }

        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {

            Triggers<Transaction, TriggersContext>.Inserting +=
                entry => entry.Context.TransactionFundingStatusHistory.Add(new TransactionFundingStatusChange { Date = DateTime.Now, Transaction = entry.Entity, Status = entry.Entity.FundingStatus });

            Triggers<Transaction, TriggersContext>.Updating +=
                entry => entry.Context.TransactionFundingStatusHistory.Add(new TransactionFundingStatusChange { Date = DateTime.Now, Transaction = entry.Entity, Status = entry.Entity.FundingStatus });

            return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, cancellationToken);
        }
    }
}

Could you please provide a sample for EF Core 2?

Could you please provide a sample for adding a trigger, Condition is given it that I can't change your DbContext inheritance chain. If not possible then at least provide steps to create/alter/delete trigger, add migration and update database.

Further technical details:

Database engine: SQL Server

Visual Studio or SSMS version: Visual Studio 2017 15.5, SSMS 17.4

Example Syntax Not Working

Using Asp Net Core 2.2 with NickStrupat.EntityFrameworkCore.Triggers.AspNetCore 1.0.2

I'm trying to follow the examples on the github main page, but they are not working for me.

I have a DbContext derived from DbContextWithTriggers.
services.AddTriggers(); is called in Startup.ConfigureServices.

If I have the following in the Configure method nothing happens:

            app.UseTriggers(builder =>
            {
                builder.Triggers().Inserted.Add(  entry => logger.LogWarning("Please Log")  );
            });

This almost exactly matches the documentation but does not work.

The follow does however work:

            app.UseTriggers(builder =>
            {
                Triggers<EventNote>.Inserted += e => logger.LogWarning("I work");
                Triggers.Inserted += e => logger.LogWarning("me too");
            });

What am I missing? What I am really looking for is to use the dependency injection syntax to pass the modified entry to a service. Something like this for a single entity:

            app.UseTriggers(builder =>
            {
                builder.Triggers<EventNote, EntityContext>().Inserted.Add<TransactionLog>(entry =>
                {
                    entry.Service.LogEventNote(entry.Entity as EventNote );
                });
            });

Or preferably this for all entities:

            app.UseTriggers(builder =>
            {
                builder.Triggers().Inserted.Add<TransactionLog>(entry =>
                {
                    entry.Service.LogEntity(entry.Entity);
                });
            });

Also, are async triggers supported? Something like:

            app.UseTriggers(builder =>
            {
                builder.Triggers().Inserted.Add<TransactionLog>(async entry =>
                {
                    await entry.Service.LogEntity(entry.Entity);
                });
            });

How to user this EntityFramework.Triggers with Repository pattern

I am having hard time integration this library in my app, it has UnitOfWork and Repository pattern. I also want to pass logged in user info who performed the action from asp.net core to data layer. It seems this library can easily solve a complex issue in my app where I am logging in each controller. Thank you

Exception when accessing Original property

The following code does not work, when Entity is a proxy:

           Triggers<EntityBase>.Updating += entity => {
                if (entity.Original.Version != entity.Entity.Version) { <== Exception
                    entity.Cancel = true;
                    // ReSharper disable once PossibleNullReferenceException
                    var entityName = entity.Entity.GetType().BaseType != null 
                        && entity.Entity.GetType().Name.StartsWith(entity.Entity.GetType().BaseType.Name)
                        // ReSharper disable once PossibleNullReferenceException
                        ? entity.Entity.GetType().BaseType.Name
                        : entity.Entity.GetType().Name;

                    throw new DbUpdateConcurrencyException(
                        $"Validation failed for entitiy {entityName} with id {entity.Entity.Id}. Version in Database in different from version in Entity.");
                }
                entity.Entity.Updated = DateTime.UtcNow;
                entity.Entity.Version += 1;
            };

Exception is:

System.TypeLoadException
  HResult=0x80131522
  Message=Could not load type 'Hotel_FEB49A97C767CB2D75BD11061E9CD3A5AC5E68203057224FA38195FCC1D93530__OriginalValuesWrapper' from assembly 'Hotel_FEB49A97C767CB2D75BD11061E9CD3A5AC5E68203057224FA38195FCC1D93530__OriginalValuesWrapperAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because the parent type is sealed.
  Source=<Cannot evaluate the exception source>
  StackTrace:
<Cannot evaluate the exception stack trace>

I could solve the problem by changing the line

if (entity.Original.Version != entity.Entity.Version) {

to

if (entity.Context.Entry(entity.Entity).OriginalValues.ToObject() is EntityBase org && org.Version != entity.Entity.Version) {

It may be a problem of EntityFramework.TypedOriginalValues...

TypeInitializationException on accessing Original property - already added virtual.. please help

Hello -
I know its an old issue but the answer on previos posts doesn't solve the probleme for me.. what i mean is I've already added the virtual keyword to all attributes.

Here is the example code of entity which prodeces that error.

public class ApplicationMember 
{
	static ApplicationMember()
	{
		Triggers<ApplicationMember>.Updating += entry =>
		{
			ApplicationMember V1 = entry.Entity;
			ApplicationMember V2 = entry.Original;

			if(V1.Email != V2.Email)
			{

			}
		};
	}

	public virtual Int32 ID { get; set; }

	[DataType(DataType.EmailAddress)]
	public virtual String Email { get; set; }

	[DataType(DataType.Password)]
	public virtual String Password { get; set; }

	public virtual ICollection<ApplicationMember> ApplicationMembers { get; set; }
}

If I remove public virtual ICollection<ApplicationMember> ApplicationMembers { get; set; } I'm able to access the original property but with that line its not possible and thow that exception.

System.TypeInitializationException: "Der Typeninitialisierer fรผr "EntityFramework.TypedOriginalValues.OriginalValuesWrapper`1" hat eine Ausnahme verursacht."

Would be thanksfull for every help :)

BTW: It's a really awesome libary :)

Greetz
PowerOfGit

Exception when Triggers added on both Parent and Child class

Trying to add Triggers in both a parent (CreatedEntity) and child (ModifiedCreatedEntity) class, but I keep hitting an InvalidCastException (details below) on context.SaveChanges()

Moving both the triggers to either the parent or child class resolves the problem, but isn't as nice design-wise, since I would like some entity classes to inherit from the parent (with its properties and associated triggers) and other entity classes to inherit from the child (with both the parent and child properties and triggers). Any help or advice would be greatly appreciated.

Sample Code:

using System;
using System.Data.Entity;
using EntityFramework.Triggers;

class CreatedEntity : ITriggerable
{
    public CreatedEntity()
    {
        this.Triggers().Inserting += entry => { entry.Entity.InsertedDateTime = DateTime.Now; };
    }
    public DateTime InsertedDateTime { get; set; }

}

class ModifiedCreatedEntity : CreatedEntity
{
    public ModifiedCreatedEntity()
    {
        //this.Triggers().Inserting += entry => { entry.Entity.InsertedDateTime = DateTime.Now; };
        this.Triggers().Inserting += entry => { entry.Entity.ModifiedDateTime = DateTime.Now; };
        this.Triggers().Updating += entry => { entry.Entity.ModifiedDateTime = DateTime.Now; };
    }
    public Int64 Id { get; protected set; }
    public DateTime ModifiedDateTime { get; set; }

}

class Context : DbContext
{
    public DbSet<ModifiedCreatedEntity> Entities { get; set; }

    public override Int32 SaveChanges() { return this.SaveChangesWithTriggers(base.SaveChanges); }
}

class Program
{


    static void Main(string[] args)
    {
        using (var context = new Context())
        {
            try
            {
                var entity = new ModifiedCreatedEntity();
                context.Entities.Add(entity);
                context.SaveChanges();
            }
            finally
            {
                context.Database.Delete();
            }
        }
    }
}

Exception:

System.InvalidCastException was unhandled
  HResult=-2147467262
  Message=Unable to cast object of type 'EntityFramework.Triggers.Triggers`1[CreatedEntity]' to type 'EntityFramework.Triggers.Triggers`1[ModifiedCreatedEntity]'.
  Source=EntityFramework.Triggers
  StackTrace:
       at EntityFramework.Triggers.Extensions.Triggers[TTriggerable](TTriggerable triggerable) in c:\Development\EntityFramework.Triggers\EntityFramework.Triggers\Extensions.cs:line 27
       at ModifiedCreatedEntity..ctor() in c:\Users\user\Documents\Visual Studio 2013\Projects\TriggerTestApp\TriggerTestApp\Program.cs:line 20
       at Program.Main(String[] args) in c:\Users\user\Documents\Visual Studio 2013\Projects\TriggerTestApp\TriggerTestApp\Program.cs:line 45
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

Old reference to Microsoft.EntityFrameworkCore

Hi,

I am getting issues building your project together with a test-project testing a library referencing Microsoft.EntityFrameworkCore, because of a conflict between Microsoft.EntityFrameworkCore 1.1.1 and Microsoft.EntityFrameworkCore 1.1.0, causing an HRESULT: 0x80131040 - error.

Is it possible to upgrade to Microsoft.EntityFrameworkCore 1.1.1? I tried doing this myself, but I can't get the solution to build in Visual Studio 2017, as it doesn't use project.json anymore and something seems to go wrong while converting the solution.

Thanks & best regards,
Compu

Missing Original property

Is it possible that this property is broken? @NickStrupat

I've treid your "Longer example (targeting EF6 for now)" - Example and got error that the property Original is missing.

Or does it just changed? I'm using the latest version 6.1.1 and also tried different one but im still miss this prop.

Thanks

SaveChangesWithTriggersAsync swallows exception

We have overridden SaveChangesAsync like so:

try
            {
                return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, cancellationToken);
            }
            catch (DbEntityValidationException ex)
            {
                // Handle ...
            }

But we're not getting the full errors and the catch block is never run. How can we solve this issue?

Insert null after update

Hi Nick

I hope you are well.

I have a problem here where when I insert a record the Insert and Update Columns gets set and all is fine
When I do an update the Update Column is set but the Insert column get sets to null

`public abstract class Trackable
{
public virtual DateTime? Inserted { get; private set; }
public virtual DateTime? Updated { get; private set; }

    static Trackable()
    {
        Triggers<Trackable>.Inserting += entry => entry.Entity.Inserted = entry.Entity.Updated = DateTime.Now;
        Triggers<Trackable>.Updating += entry => entry.Entity.Updated = DateTime.Now;
    }
}`
#region If you're targeting EF 6
        public override int SaveChanges()
        {
            return this.SaveChangesWithTriggers(base.SaveChanges);
        }
        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
        {
             return await this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, cancellationToken);
        }

        #endregion
public class PerformanceReviewMaster: Trackable
   {
       [Required,Key]
       public Guid PerformanceReviewMasterId { get; set; }
       [Required]
       public string Description { get; set; }
       [Required]
       [DataType(DataType.Date)]
       public DateTime StartDate { get; set; }
       [Required]
       [DataType(DataType.Date)]
       public DateTime EndDate { get; set; }
     
   }

Query database during trigger on same DbContext?

Question:

We have a BinaryObject entity that just contains the ID and data (byte[]) and a BinaryObjectHeader that contains details. The BinaryObject is created first and the data is streamed. The header is created next. I recently added a Size property to the header and would like to automatically set it on Insert trigger to the length of the data.

Can I use the DbContext from the trigger to executable DbContext.Database.SqlQuery<>() during the trigger? The SQL does not affect the data in the context ("SELECT LEN(Data) FROM BinaryObjects WHERE BinaryObjectId = @p0")

It is possible activate the triggers per interface

Hi,

I would like to know if it is possible to activate triggers for a kind of interface. I already tried this approach but without success.

For example:

public interface someInterface
{
      BeforeCreate();
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
            Triggers<SomeInterface>.Updating += entry => entry.Entity...
            
            return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, acceptAllChangesOnSuccess: true, cancellationToken: cancellationToken);
}

Updated field is not getting saved to the Database

I have a core project set up just like the sample.

The trigger fires and sets the Updated field with the current date/time.

But it doesn't get persisted to the database.

Triggers<TrackableEntity>.Updating += entry =>
{
	entry.Entity.Updated = DateTime.Now;
};

I don't see what I am doing wrong.

Trigger Updating: entry.Original exception

Hello Nick.

Thank you for the great library. It is really very useful and flexible library.

I have issue with trigger on event "Updating"

My base entity:

    public class BaseEntity : ITriggerable
    {
        static BaseEntity()
        {
            Triggers<BaseEntity>.Inserting += entry => entry.Entity.DateCreated = entry.Entity.DateModified = DateTime.UtcNow;
            Triggers<BaseEntity>.Updating += entry => entry.Entity.DateModified = DateTime.UtcNow;
        }

        public BaseEntity()
        {
            Id = Guid.NewGuid().ToString("N");
        }

        public void SetId(string id)
        {
            Id = id;
        }

        [Key]
        public string Id { get; private set; }

        public DateTime DateCreated { get; set; }

        public DateTime DateModified { get; set; }

    }

My main model:

public class RequestInfo : BaseEntity
    {
        static RequestInfo()
        {
            // change status when reviewer was assigned
            Triggers<RequestInfo>.Updating += entry =>
            {
                if (!string.IsNullOrEmpty(entry.Original.AssignedToId) && !string.IsNullOrEmpty(entry.Entity.AssignedToId) && entry.Entity.Status == RequestStatus.New)
                    entry.Entity.Status = RequestStatus.InProgress;
            };
        }


        public RequestStatus Status { get; set; }

        public string AssignedToId { get; set; }
   }

But when event of trigger is hired I see error
image

My dbContext is inherited from DbContextWithTriggers and I have several events on Updating for this model

Maybe I did something wrong. Please let me know my mistake or suggestion to fix it.

Soft deletes do not trigger Update events

When IDeletingEntry.Cancel is set true, it seems logical that the Updating/Updated events should then fire for that entry.

Is this the expected behavior?

Sample:

static bool SoftDeleteEnabled = false;

void Main()
{
	TestContext.Migrate();

	var db = new TestContext();
	var widget = db.Widgets.Create();
	db.Widgets.Add(widget);

	widget.Value = DateTime.Now.ToString();
	db.SaveChanges(); // Inserting, Inserted

	widget.Value = DateTime.Today.ToString();
	db.SaveChanges(); // Updating, Updated

	db.Widgets.Remove(widget);
	db.SaveChanges(); // Deleting, Deleted

	db.Widgets.Add(widget);
	db.SaveChanges(); // Inserting, Inserted

	SoftDeleteEnabled = true;
	db.Widgets.Remove(widget);
	db.SaveChanges(); // Deleting

}

public class Widget
{
	public int Id { get; set; }
	public string Value { get; set; }
	public DateTime? Deleted { get; set; }

	static Widget()
	{
		Triggers<Widget>.Inserting += entry => Console.WriteLine("Inserting");
		Triggers<Widget>.InsertFailed += entry => Console.WriteLine("InsertFailed");
		Triggers<Widget>.Inserted += entry => Console.WriteLine("Inserted");

		Triggers<Widget>.Updating += entry => Console.WriteLine("Updating");
		Triggers<Widget>.UpdateFailed += entry => Console.WriteLine("UpdateFailed");
		Triggers<Widget>.Updated += entry => Console.WriteLine("Updated");

		Triggers<Widget>.Deleting += entry => Console.WriteLine("Deleting");
		Triggers<Widget>.DeleteFailed += entry => Console.WriteLine("DeleteFailed");
		Triggers<Widget>.Deleted += entry => Console.WriteLine("Deleted");

		Triggers<Widget>.Deleting += entry =>
		{
			if (SoftDeleteEnabled)
			{
				entry.Cancel = true;
				entry.Entity.Deleted = DateTime.Now;
			}
		};
	}
}


public class TestContext : DbContextWithTriggers
{
	public TestContext()
	{
		Database.Log = Util.SqlOutputWriter.Write;
	}
	
	public DbSet<Widget> Widgets { get; set; }


	public static void Migrate()
	{
		using (var db = new TestContext())
		{
			var m = new DropCreateDatabaseIfModelChanges<TestContext>();
			m.InitializeDatabase(db);
		}
	}
}

Disable Triggers On Related Entities

I have a question about disabling triggers on related models.

Lets say I have ModelA and that has many ModelBs. I have a trigger on both for Updating.

However, my trigger in ModelA is updating its related ModelBs which causes the updating trigger to be called for ModelB.

Is there any way to disable triggers on ModelB if it's being updated from ModelAs trigger?

Properties are not being persisted inside Updating event

I'm having this strange bug.

Let's say I have this entity:

`
public class Subscription
{
public long Id {get;set;}
public decimal Amount {get;set;}
public bool IsPaid {get;set;}
public DateTime? PaidOnUtc {get;set;}
}

when I update, like this,
Triggers.Updating += context => {
context.Entity.Amount = 30.0M;
};

using (var myTriggerContext = new MyTriggerContext()) {
var existingSub = myTriggerContext.Subscriptions.FirstOrDefault(x => x.Id == 30);
existingSub.PaidOnUtc = DateTime.UtcNow;
await myTriggerContext.SaveChangesAsync();
}
`

Amount prop is never getting persisted. It persists only when following:

  1. When I set Amount property explicity like: existingSub.Amount = 10M;
  2. or when I call context.ChangeTracker.DetectChanges() method inside Updating event handler.

It seems to me that when we change entities inside Updating event handler, change tracker's DetectChanges method is not being called automatically in the base context's SaveChanges method.

Could you clarify the issue?

Thanks

EF.Triggers and .Net Core transactions

Hello I've a question about using your EF.Triggers library together with Transactions and transactions scopes.

We're using transactions when modifying several database entries, so that if one operation fails/ is rejected, the whole transaction rolls back. However, since there are multiple context.SaveChanges() calls inside the transactions, the Triggers.Updated and .TriggersInserted triggers are activated, and if the transaction is rolled back, I have not found a way to revert the changes made by the handlers when the triggers are activated. I am mostly using the library to send the database changes to a realtime subset database, and this library would be a nice way to keep this second db in sync.

Reading other issues (specifically #20 (comment)_) and questions on SO and googling, I found some references that Triggers.Updated or Triggers.Created should be used only when you know that the data has been persisted.

Is there a trigger that gets activated on rollback (Triggers.UpdateFailed and such don't appear to behave this way) or any other suggestion on how to handle this situation? One of our options includes rewriting the whole transactions logic to make just one context.SaveChanges() call per transaction, accumulating all EF changes in one batch, but that would have other implications to our architecture and frankly would mean a lot of rewriting, so if possible I would like to avoid that as much as possible.

BeforeChangeEntry(u).Original throwing error System.TypeInitializationException

I have implement the triggers:
Triggers<EntityBase>.Inserting += u =>
{
u.Entity.CreationDate = DateTime.Now;
u.Entity.LastModifiedDate = DateTime.Now;
};
Triggers<EntityBase>.Updating += u =>
{
u.Entity.CreationDate = u.Original.CreationDate;
u.Entity.CreatedBy = u.Original.CreatedBy;
u.Entity.LastModifiedDate = DateTime.Now;
};
Triggers<EntityBase>.Deleting += u =>
{
u.Entity.CreationDate = u.Original.CreationDate;
u.Entity.CreatedBy = u.Original.CreatedBy;
u.Entity.LastModifiedDate = DateTime.Now;
};
Since I cannot inherit from DBContext with Triggers I override Savechanges:
public override int SaveChanges()
{
return this.SaveChangesWithTriggers(base.SaveChanges, acceptAllChangesOnSuccess: true);
}

This was all working but now I am getting the below error:
For some reason I cannot get access to the Original data.
'((EntityFrameworkCore.Triggers.TriggerEntityInvoker<Microsoft.EntityFrameworkCore.DbContext, AMSRR.Main.Interface.ReportType>.BeforeChangeEntry)u).Original' threw an exception of type 'System.TypeInitializationException'

Full Exception:
$exception {System.TypeInitializationException: The type initializer for 'EntityFrameworkCore.TypedOriginalValues.OriginalValuesWrapper1' threw an exception. ---> System.TypeLoadException: Declaration referenced in a method implementation cannot be a final method. Type: 'ReportType__OriginalValuesWrapper'. Assembly: 'ReportType__OriginalValuesWrapperAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type) at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() at System.Reflection.Emit.TypeBuilder.CreateTypeInfo() at EntityFrameworkCore.TypedOriginalValues.OriginalValuesWrapper1.GetFactory()
at EntityFrameworkCore.TypedOriginalValues.OriginalValuesWrapper1..cctor() --- End of inner exception stack trace --- at EntityFrameworkCore.TypedOriginalValues.OriginalValuesWrapper1.Create(EntityEntry originalValues)
at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy1.CreateValue()

Any ideas on what is going wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.