From d83b17a0c30075c654423292c3b2325fe3f5aa4e Mon Sep 17 00:00:00 2001 From: Bret Ferrier Date: Wed, 22 Apr 2015 09:51:53 -0600 Subject: [PATCH 1/3] Updating to use MongoDriver 2.0 non legacy Updated to use the new driver and tried to make the documents in mongodb easier to query and look for patterns --- src/Elmah.MongoDB/Elmah.MongoDB.csproj | 18 +- src/Elmah.MongoDB/ErrorModel.cs | 16 ++ src/Elmah.MongoDB/MongoErrorLog.cs | 76 +++++---- .../NameValueCollectionSerializer.cs | 158 +++++++++++------- src/Elmah.MongoDB/packages.config | 4 +- 5 files changed, 165 insertions(+), 107 deletions(-) create mode 100644 src/Elmah.MongoDB/ErrorModel.cs diff --git a/src/Elmah.MongoDB/Elmah.MongoDB.csproj b/src/Elmah.MongoDB/Elmah.MongoDB.csproj index 48b8cbe..fd46612 100644 --- a/src/Elmah.MongoDB/Elmah.MongoDB.csproj +++ b/src/Elmah.MongoDB/Elmah.MongoDB.csproj @@ -10,7 +10,7 @@ Properties Elmah Elmah.MongoDB - v3.5 + v4.5 512 ..\..\src\ @@ -24,6 +24,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -32,18 +33,20 @@ TRACE prompt 4 + false ..\packages\elmah.corelibrary.1.2.2\lib\Elmah.dll - - False - ..\packages\mongocsharpdriver.1.7\lib\net35\MongoDB.Bson.dll + + ..\packages\MongoDB.Bson.2.0.0\lib\net45\MongoDB.Bson.dll - - False - ..\packages\mongocsharpdriver.1.7\lib\net35\MongoDB.Driver.dll + + ..\packages\MongoDB.Driver.2.0.0\lib\net45\MongoDB.Driver.dll + + + ..\packages\MongoDB.Driver.Core.2.0.0\lib\net45\MongoDB.Driver.Core.dll @@ -51,6 +54,7 @@ + diff --git a/src/Elmah.MongoDB/ErrorModel.cs b/src/Elmah.MongoDB/ErrorModel.cs new file mode 100644 index 0000000..456f77e --- /dev/null +++ b/src/Elmah.MongoDB/ErrorModel.cs @@ -0,0 +1,16 @@ +using MongoDB.Bson; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Elmah +{ + public class ErrorModel + { + public ObjectId _id { get; set; } + + public Error Error { get; set; } + } +} diff --git a/src/Elmah.MongoDB/MongoErrorLog.cs b/src/Elmah.MongoDB/MongoErrorLog.cs index da704d4..e76bd82 100644 --- a/src/Elmah.MongoDB/MongoErrorLog.cs +++ b/src/Elmah.MongoDB/MongoErrorLog.cs @@ -5,19 +5,20 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using MongoDB.Driver.Builders; - +using System.Threading.Tasks; +using System.Linq; +using MongoDB.Bson.Serialization.Serializers; namespace Elmah { public class MongoErrorLog : ErrorLog { private readonly string _connectionString; private readonly string _collectionName; - private readonly int _maxDocuments; - private readonly int _maxSize; - private MongoInsertOptions _mongoInsertOptions; + private readonly long _maxDocuments; + private readonly long _maxSize; + //private MongoInsertOptions _mongoInsertOptions; - private MongoCollection _collection; + private IMongoCollection _collection; private const int MaxAppNameLength = 60; private const int DefaultMaxDocuments = int.MaxValue; @@ -89,7 +90,7 @@ public MongoErrorLog(string connectionString) static MongoErrorLog() { - BsonSerializer.RegisterSerializer(typeof(NameValueCollection), NameValueCollectionSerializer.Instance); + BsonSerializer.RegisterSerializer(typeof(NameValueCollection), new NameValueCollectionSerializer()); BsonClassMap.RegisterClassMap(cm => { cm.MapProperty(c => c.ApplicationName); @@ -116,23 +117,27 @@ private void Initialize() { var mongoUrl = MongoUrl.Create(_connectionString); var mongoClient = new MongoClient(mongoUrl); - var server = mongoClient.GetServer(); - var database = server.GetDatabase(mongoUrl.DatabaseName); - if (!database.CollectionExists(_collectionName)) + //var server = mongoClient.GetServer(); + + var database = mongoClient.GetDatabase(mongoUrl.DatabaseName); + //var filter = new Filter(new BsonDocument("name", _collectionName)); + var findThisOne = new ListCollectionsOptions(); + findThisOne.Filter = Builders.Filter.Eq("name", _collectionName); + var cursor = database.ListCollectionsAsync(findThisOne).Result; + var list = cursor.ToListAsync().GetAwaiter().GetResult(); + var allCollections = list.Select(c => c["name"].AsString).OrderBy(n => n).ToList(); + if (!allCollections.Contains(_collectionName)) { - var options = CollectionOptions - .SetCapped(true) - .SetAutoIndexId(true) - .SetMaxSize(_maxSize); - - if (_maxDocuments != int.MaxValue) - options.SetMaxDocuments(_maxDocuments); + var options = new CreateCollectionOptions(); + options.Capped = true; + options.AutoIndexId = true; + options.MaxSize = _maxSize; - database.CreateCollection(_collectionName, options); + database.CreateCollectionAsync(_collectionName, options).GetAwaiter().GetResult(); } - _collection = database.GetCollection(_collectionName); - _mongoInsertOptions = new MongoInsertOptions { CheckElementNames = false }; + _collection = database.GetCollection(_collectionName); + //_mongoInsertOptions = new MongoInsertOptions { CheckElementNames = false }; } } @@ -163,12 +168,13 @@ public override string Log(Error error) throw new ArgumentNullException("error"); error.ApplicationName = ApplicationName; - var document = error.ToBsonDocument(); + var toStore = new ErrorModel(); + var id = ObjectId.GenerateNewId(); - document.Add("_id", id); - - _collection.Insert(document, _mongoInsertOptions); + toStore._id = id; + toStore.Error = error; + _collection.InsertOneAsync(toStore).GetAwaiter().GetResult(); return id.ToString(); } @@ -184,14 +190,12 @@ public override ErrorLogEntry GetError(string id) if (id == null) throw new ArgumentNullException("id"); if (id.Length == 0) throw new ArgumentException(null, "id"); - var document = _collection.FindOneById(new ObjectId(id)); + var document = _collection.Find(x=> x._id == ObjectId.Parse(id)).SingleOrDefaultAsync().Result; if (document == null) return null; - var error = BsonSerializer.Deserialize(document); - - return new ErrorLogEntry(this, id, error); + return new ErrorLogEntry(this, id, document.Error); } /// @@ -207,17 +211,18 @@ public override int GetErrors(int pageIndex, int pageSize, IList errorEntryList) if (pageIndex < 0) throw new ArgumentOutOfRangeException("pageIndex", pageIndex, null); if (pageSize < 0) throw new ArgumentOutOfRangeException("pageSize", pageSize, null); - var documents = _collection.FindAll().SetSortOrder(SortBy.Descending("$natural")).SetSkip(pageIndex * pageSize).SetLimit(pageSize); + var documents = _collection.Find(new BsonDocument()).Sort("{$natural: -1}").Skip(pageIndex * pageSize) + .Limit(pageSize).ToListAsync() + .GetAwaiter().GetResult();//.SetSortOrder(SortBy.Descending("$natural")).SetSkip(pageIndex * pageSize).SetLimit(pageSize); foreach (var document in documents) { - var id = document["_id"].AsObjectId.ToString(); - var error = BsonSerializer.Deserialize(document); - error.Time = error.Time.ToLocalTime(); - errorEntryList.Add(new ErrorLogEntry(this, id, error)); + var error = document.Error; + error.Time = error.Time.ToLocalTime(); + errorEntryList.Add(new ErrorLogEntry(this, document._id.ToString(), error)); } - return (int) _collection.Count(); + return Convert.ToInt32(_collection.CountAsync(new BsonDocument()).Result); } public static int GetCollectionLimit(IDictionary config) @@ -234,7 +239,6 @@ public static int GetCollectionSize(IDictionary config) public virtual string GetConnectionString(IDictionary config) { -#if !NET_1_1 && !NET_1_0 // // First look for a connection string name that can be // subsequently indexed into the section of @@ -252,7 +256,7 @@ public virtual string GetConnectionString(IDictionary config) return settings.ConnectionString ?? string.Empty; } -#endif + // // Connection string name not found so see if a connection diff --git a/src/Elmah.MongoDB/NameValueCollectionSerializer.cs b/src/Elmah.MongoDB/NameValueCollectionSerializer.cs index b19b31b..725a806 100644 --- a/src/Elmah.MongoDB/NameValueCollectionSerializer.cs +++ b/src/Elmah.MongoDB/NameValueCollectionSerializer.cs @@ -7,76 +7,108 @@ namespace Elmah { - public class NameValueCollectionSerializer : BsonBaseSerializer - { - private static readonly NameValueCollectionSerializer instance = new NameValueCollectionSerializer(); + public class NameValueCollectionSerializer : SerializerBase + { + BsonDocumentSerializer documentserializer = new BsonDocumentSerializer(); + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, NameValueCollection value) + { + BsonDocument doc = new BsonDocument(); + if (value != null) + { + foreach (var key in value.AllKeys) + { + foreach (var val in value.GetValues(key)) + { + doc.Add(key.Replace('.','|'), val); + } + } + } - public static NameValueCollectionSerializer Instance - { - get { return instance; } - } + documentserializer.Serialize(context, doc); + } - public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) - { - return Deserialize(bsonReader, nominalType, options); - } + public override NameValueCollection Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + BsonDocument doc = documentserializer.Deserialize(context); + var nvc = new NameValueCollection(); + foreach (var prop in doc) + { + nvc.Add(prop.Name.Replace('|','.'), prop.Value.ToString()); + } - public override object Deserialize( - BsonReader bsonReader, - Type nominalType, - IBsonSerializationOptions options - ) - { - var bsonType = bsonReader.GetCurrentBsonType(); - if (bsonType == BsonType.Null) - { - bsonReader.ReadNull(); - return null; - } + return nvc; + } + } + //public class NameValueCollectionSerializer : BsonBaseSerializer + //{ + // private static readonly NameValueCollectionSerializer instance = new NameValueCollectionSerializer(); - var nvc = new NameValueCollection(); + // public static NameValueCollectionSerializer Instance + // { + // get { return instance; } + // } - bsonReader.ReadStartArray(); - while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) - { - bsonReader.ReadStartArray(); - var key = (string)StringSerializer.Instance.Deserialize(bsonReader, typeof(string), options); - var val = (string)StringSerializer.Instance.Deserialize(bsonReader, typeof(string), options); - bsonReader.ReadEndArray(); - nvc.Add(key, val); - } - bsonReader.ReadEndArray(); + // public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options) + // { + // return Deserialize(bsonReader, nominalType, options); + // } - return nvc; - } + // public override object Deserialize( + // BsonReader bsonReader, + // Type nominalType, + // IBsonSerializationOptions options + // ) + // { + // var bsonType = bsonReader.GetCurrentBsonType(); + // if (bsonType == BsonType.Null) + // { + // bsonReader.ReadNull(); + // return null; + // } - public override void Serialize( - BsonWriter bsonWriter, - Type nominalType, - object value, - IBsonSerializationOptions options - ) - { - if (value == null) - { - bsonWriter.WriteNull(); - return; - } + // var nvc = new NameValueCollection(); - var nvc = (NameValueCollection)value; + // bsonReader.ReadStartArray(); + // while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) + // { + // bsonReader.ReadStartArray(); + // var key = (string)StringSerializer.Instance.Deserialize(bsonReader, typeof(string), options); + // var val = (string)StringSerializer.Instance.Deserialize(bsonReader, typeof(string), options); + // bsonReader.ReadEndArray(); + // nvc.Add(key, val); + // } + // bsonReader.ReadEndArray(); - bsonWriter.WriteStartArray(); - foreach (var key in nvc.AllKeys) - { - foreach (var val in nvc.GetValues(key)) - { - bsonWriter.WriteStartArray(); - StringSerializer.Instance.Serialize(bsonWriter, typeof(string), key, options); - StringSerializer.Instance.Serialize(bsonWriter, typeof(string), val, options); - bsonWriter.WriteEndArray(); - } - } - bsonWriter.WriteEndArray(); - } - } + // return nvc; + // } + + // public override void Serialize( + // BsonWriter bsonWriter, + // Type nominalType, + // object value, + // IBsonSerializationOptions options + // ) + // { + // if (value == null) + // { + // bsonWriter.WriteNull(); + // return; + // } + + // var nvc = (NameValueCollection)value; + + // bsonWriter.WriteStartArray(); + // foreach (var key in nvc.AllKeys) + // { + // foreach (var val in nvc.GetValues(key)) + // { + // bsonWriter.WriteStartArray(); + // StringSerializer.Instance.Serialize(bsonWriter, typeof(string), key, options); + // StringSerializer.Instance.Serialize(bsonWriter, typeof(string), val, options); + // bsonWriter.WriteEndArray(); + // } + // } + // bsonWriter.WriteEndArray(); + // } + //} } \ No newline at end of file diff --git a/src/Elmah.MongoDB/packages.config b/src/Elmah.MongoDB/packages.config index 6e8192a..24059ad 100644 --- a/src/Elmah.MongoDB/packages.config +++ b/src/Elmah.MongoDB/packages.config @@ -2,5 +2,7 @@ - + + + \ No newline at end of file From e290263a66b382d46308b6a60e3163c85b6cf6a6 Mon Sep 17 00:00:00 2001 From: runxc1 Date: Fri, 6 Nov 2015 17:04:03 -0700 Subject: [PATCH 2/3] Updating how it is serialized, doesn't keep duplicate keys or values --- src/Elmah-MongoDB.sln | 8 +++++--- src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj | 6 ++++++ src/Elmah.MongoDB.Sample/Web.config | 2 +- src/Elmah.MongoDB/MongoErrorLog.cs | 2 +- src/Elmah.MongoDB/NameValueCollectionSerializer.cs | 9 +++++---- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Elmah-MongoDB.sln b/src/Elmah-MongoDB.sln index c2b8a92..f95449c 100644 --- a/src/Elmah-MongoDB.sln +++ b/src/Elmah-MongoDB.sln @@ -1,8 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elmah.MongoDB", "Elmah.MongoDB\Elmah.MongoDB.csproj", "{41AFEE84-1EE9-4457-917A-52632B55A1BA}" -EndProject +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "package", "package", "{8341DC55-5656-478A-B15F-CD3CC0C087C0}" ProjectSection(SolutionItems) = preProject ..\pkg\nuget\nuspec\Elmah.MongoDB.nuspec = ..\pkg\nuget\nuspec\Elmah.MongoDB.nuspec @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F970C7 .nuget\NuGet.targets = .nuget\NuGet.targets EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elmah.MongoDB", "Elmah.MongoDB\Elmah.MongoDB.csproj", "{41AFEE84-1EE9-4457-917A-52632B55A1BA}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elmah.MongoDB.Sample", "Elmah.MongoDB.Sample\Elmah.MongoDB.Sample.csproj", "{B0812E20-6414-4648-A4F6-F7376FA7939C}" EndProject Global diff --git a/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj b/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj index 4c73038..996c0f4 100644 --- a/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj +++ b/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj @@ -22,6 +22,12 @@ ..\ true + true + + + + + 4.0 true diff --git a/src/Elmah.MongoDB.Sample/Web.config b/src/Elmah.MongoDB.Sample/Web.config index b7b0745..4903b11 100644 --- a/src/Elmah.MongoDB.Sample/Web.config +++ b/src/Elmah.MongoDB.Sample/Web.config @@ -15,7 +15,7 @@ - + diff --git a/src/Elmah.MongoDB/MongoErrorLog.cs b/src/Elmah.MongoDB/MongoErrorLog.cs index e76bd82..6b69687 100644 --- a/src/Elmah.MongoDB/MongoErrorLog.cs +++ b/src/Elmah.MongoDB/MongoErrorLog.cs @@ -63,7 +63,7 @@ public MongoErrorLog(IDictionary config) ApplicationName = appName; - _collectionName = appName.Length > 0 ? "Elmah-" + appName : "Elmah"; + _collectionName = appName.Length > 0 ? "Elmah2-" + appName : "Elmah"; _maxDocuments = GetCollectionLimit(config); _maxSize = GetCollectionSize(config); diff --git a/src/Elmah.MongoDB/NameValueCollectionSerializer.cs b/src/Elmah.MongoDB/NameValueCollectionSerializer.cs index 725a806..9071662 100644 --- a/src/Elmah.MongoDB/NameValueCollectionSerializer.cs +++ b/src/Elmah.MongoDB/NameValueCollectionSerializer.cs @@ -3,6 +3,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using System; +using System.Linq; using System.Collections.Specialized; namespace Elmah @@ -15,12 +16,12 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati BsonDocument doc = new BsonDocument(); if (value != null) { - foreach (var key in value.AllKeys) + foreach (var key in value.AllKeys.Distinct()) { - foreach (var val in value.GetValues(key)) - { + var val = value.GetValues(key).FirstOrDefault(); + if(val != null) doc.Add(key.Replace('.','|'), val); - } + } } From 6fafc80602eef4084325fe4d646e8dd0cbc849f1 Mon Sep 17 00:00:00 2001 From: Chris Reynoso Date: Sun, 14 Jun 2020 17:25:29 -0700 Subject: [PATCH 3/3] Update MongoDB driver to 2.10.4. Update target framework to 4.6 for dependencies of MongoDB driver. Revert collection name prefect to standard convention for original elmah-mongodb library. Add configuration attribute support for custom sort key used for listing error messages. Add configuration attribute support for creating collection as regular, uncapped. Update readme to reflect changes. --- readme.markdown | 13 +++++ .../Elmah.MongoDB.Sample.csproj | 14 ++--- src/Elmah.MongoDB.Sample/Web.config | 26 ++++++++- src/Elmah.MongoDB/Elmah.MongoDB.csproj | 53 ++++++++++++++++--- src/Elmah.MongoDB/MongoErrorLog.cs | 51 +++++++++--------- src/Elmah.MongoDB/packages.config | 15 ++++-- 6 files changed, 127 insertions(+), 45 deletions(-) diff --git a/readme.markdown b/readme.markdown index c881f01..17dcc10 100644 --- a/readme.markdown +++ b/readme.markdown @@ -24,3 +24,16 @@ Here is an example configuration: + +## Azure Cosmos Support +Azure Cosmos is not 100% API compatible with MongoDB and as such requires some modifications to the behavior of this library in order to be compatible. + +These issues are known as of 6/14/2020: +- Capped collections are not supported +- $natural is not supported for sorting + +In order to work around these issues, use the following additional attributes to disable capped collection on collection creation and use the primary key for sorting: + + + + diff --git a/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj b/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj index 996c0f4..2af8ff3 100644 --- a/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj +++ b/src/Elmah.MongoDB.Sample/Elmah.MongoDB.Sample.csproj @@ -1,5 +1,5 @@  - + Debug @@ -13,7 +13,7 @@ Properties Elmah.MongoDB.Sample Elmah.MongoDB.Sample - v4.5 + v4.6 false true @@ -28,6 +28,9 @@ 4.0 + + + true @@ -64,21 +67,19 @@ + - - + True ..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll - - @@ -121,6 +122,7 @@ True ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + False ..\packages\WebGrease.1.3.0\lib\WebGrease.dll diff --git a/src/Elmah.MongoDB.Sample/Web.config b/src/Elmah.MongoDB.Sample/Web.config index 4903b11..6d414a4 100644 --- a/src/Elmah.MongoDB.Sample/Web.config +++ b/src/Elmah.MongoDB.Sample/Web.config @@ -29,9 +29,17 @@ + - + @@ -86,6 +94,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/Elmah.MongoDB/Elmah.MongoDB.csproj b/src/Elmah.MongoDB/Elmah.MongoDB.csproj index fd46612..aafe1aa 100644 --- a/src/Elmah.MongoDB/Elmah.MongoDB.csproj +++ b/src/Elmah.MongoDB/Elmah.MongoDB.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -10,11 +10,13 @@ Properties Elmah Elmah.MongoDB - v4.5 + v4.6 512 ..\..\src\ true + + true @@ -36,22 +38,49 @@ false + + ..\packages\Crc32C.NET.1.0.5.0\lib\net20\Crc32C.NET.dll + + + ..\packages\DnsClient.1.3.2\lib\net45\DnsClient.dll + ..\packages\elmah.corelibrary.1.2.2\lib\Elmah.dll - - ..\packages\MongoDB.Bson.2.0.0\lib\net45\MongoDB.Bson.dll + + ..\packages\MongoDB.Bson.2.10.4\lib\net452\MongoDB.Bson.dll + + + ..\packages\MongoDB.Driver.2.10.4\lib\net452\MongoDB.Driver.dll + + + ..\packages\MongoDB.Driver.Core.2.10.4\lib\net452\MongoDB.Driver.Core.dll + + + ..\packages\MongoDB.Libmongocrypt.1.0.0\lib\net452\MongoDB.Libmongocrypt.dll - - ..\packages\MongoDB.Driver.2.0.0\lib\net45\MongoDB.Driver.dll + + ..\packages\SharpCompress.0.25.1\lib\net46\SharpCompress.dll - - ..\packages\MongoDB.Driver.Core.2.0.0\lib\net45\MongoDB.Driver.Core.dll + + ..\packages\Snappy.NET.1.1.1.8\lib\net45\Snappy.NET.dll + + ..\packages\System.Buffers.4.5.1\lib\netstandard1.1\System.Buffers.dll + + + ..\packages\System.Memory.4.5.4\lib\netstandard1.1\System.Memory.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + @@ -60,6 +89,7 @@ + @@ -67,6 +97,13 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + +