public TagDataRaw InsertOrUpdateDataByName(TagDataName input)
        {
            // Transform the "TagDataName" input into "TagDataRaw" format, then call InsertOrUpdateData() to do the work.
            // The most important point is to get/validate the tag information.
            TagDataRaw data = null;
            Tag tag = null;

            if (input.TagId.HasValue)
                tag = _tagRepository.FirstOrDefault(input.TagId.Value);
            else if( !string.IsNullOrEmpty(input.TagName) )
                tag = _tagRepository.FirstOrDefault(t => t.Name == input.TagName);

            if (tag != null)
            {
                // Insert or update the data
                data = new TagDataRaw
                {
                    TenantId = tag.TenantId,
                    TagId = tag.Id,
                    Timestamp = input.Timestamp.HasValue ? input.Timestamp.Value : DateTime.Now,
                    Value = input.Value,
                    Quality = input.Quality.HasValue ? input.Quality.Value : TagDataQuality.Good
                };
                data = InsertOrUpdateData(tag, data);

                // Now update the working table
                UpdateTagDataWorkingTable(tag, data.Timestamp, data.Timestamp);
            }
            return data;
        }
        private TagDataRaw InsertOrUpdateData(Tag tag, TagDataRaw input)
        {
            // Look to see if there is already a data record matching the tag and time
            TagDataRaw data = _tagDataRawRepository.FirstOrDefault(p => p.TagId == input.TagId && p.Timestamp == input.Timestamp);

            if (data == null)
            {
                data = new TagDataRaw
                {
                    TenantId = tag.TenantId,
                    TagId = tag.Id,
                    Timestamp = input.Timestamp,
                    Value = input.Value,
                    Quality = input.Quality
                };
            }
            else
            {
                data.Value = input.Value;
                data.Quality = input.Quality;
            }

            data = _tagDataRawRepository.InsertOrUpdate(data);

            // Update the latest value in the matching tag record. (Sorry, denormalization.)
            if( !tag.LastTimestamp.HasValue || tag.LastTimestamp.Value < data.Timestamp )
            {
                tag.LastTimestamp = data.Timestamp;
                tag.LastValue = data.Value;
                tag.LastQuality = data.Quality;
                _tagRepository.Update(tag);
            }

            return data;
        }
        public long InsertOrUpdateAllDataByName(List<TagDataName> input)
        {
            // Transform the "TagDataName" input array into "TagDataRaw" format, then call InsertOrUpdateData() to do the work.
            // The most important point is to get/validate the tag information.
            long successes = 0;
            Tag tag = null;
            DateTime startTimestamp = DateTime.Now;
            DateTime endTimestamp = DateTime.Now;

            List<TagDataName> sorted = input.OrderBy(p => p.TagId).ThenBy(p => p.TagName).ThenBy(p => p.Timestamp).ToList();

            // The dirty flag will keep track of whether we need to update the working table for the current tag
            // True=update needed; false=no update needed
            bool dirty = false;

            // Loop through all the input records
            foreach (TagDataName one in sorted)
            {
                DateTime timestamp = one.Timestamp.HasValue ? one.Timestamp.Value : DateTime.Now;

                // If we don't have a tag OR the one we have doesn't match the data record, get a new tag
                if ( tag == null ||
                    (one.TagId.HasValue && one.TagId.Value != tag.Id) ||
                    (!string.IsNullOrEmpty(one.TagName) && one.TagName != tag.Name))
                {
                    // New tag. Do we need to write information to the working table?
                    if( dirty )
                    {
                        UpdateTagDataWorkingTable(tag, startTimestamp, endTimestamp);
                        dirty = false;
                    }

                    if (one.TagId.HasValue)
                        tag = _tagRepository.FirstOrDefault(one.TagId.Value);
                    else
                        tag = _tagRepository.FirstOrDefault(p => p.Name == one.TagName);

                    // New tag, so initialize the start/end timestamp range for this tag
                    startTimestamp = timestamp;
                    endTimestamp = timestamp;
                    dirty = true;
                }

                if (tag != null)
                {
                    TagDataRaw data = new TagDataRaw
                    {
                        TenantId = tag.TenantId,
                        TagId = tag.Id,
                        Timestamp = timestamp,
                        Value = one.Value,
                        Quality = one.Quality.HasValue ? one.Quality.Value : TagDataQuality.Good
                    };
                    endTimestamp = timestamp;
                    dirty = true;

                    if (InsertOrUpdateData(tag, data) != null)
                        successes++;
                }
            }

            // Update the working table with anything left over
            if (dirty)
                UpdateTagDataWorkingTable(tag, startTimestamp, endTimestamp);

            return successes;
        }